ZHIWEI666 commited on
Commit
9791d3d
·
verified ·
1 Parent(s): ac326f9

Upload 104 files

Browse files
前端页面/auth/找回账号表单组件.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createAuthFormScope } from "./认证表单生命周期.js";
2
  // 前端页面/auth/找回账号表单组件.js
3
  // ==========================================
4
  // 🔎 找回账号表单组件
@@ -35,6 +35,8 @@ export function renderRecoverForm(container, switchView) {
35
  </div>
36
  `;
37
  const formScope = createAuthFormScope(container);
 
 
38
 
39
  container.querySelector("#recover-to-login").onclick = (e) => { e.preventDefault(); switchView("login"); };
40
 
@@ -64,10 +66,11 @@ export function renderRecoverForm(container, switchView) {
64
  btnSendCode.onclick = async (e) => {
65
  e.preventDefault();
66
  if (!formScope.current() || btnSendCode.disabled) return;
 
67
 
68
  const emailInput = container.querySelector("#recover-email").value.trim();
69
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput);
70
- if (!isEmail) return showToast(t('auth.invalid_email'), "error");
71
 
72
  btnSendCode.disabled = true;
73
  btnSendCode.innerText = t('auth.sending');
@@ -93,11 +96,12 @@ export function renderRecoverForm(container, switchView) {
93
  const btnSubmit = container.querySelector("#btn-submit-recover");
94
  btnSubmit.onclick = async () => {
95
  if (!formScope.current() || btnSubmit.disabled) return;
 
96
  const email = container.querySelector("#recover-email").value.trim();
97
  const code = container.querySelector("#recover-code").value.trim();
98
 
99
- if (!email) return showToast(t('auth.invalid_email'), "warning");
100
- if (code.length !== 6) return showToast(t('auth.invalid_code'), "warning");
101
 
102
  btnSubmit.disabled = true;
103
  btnSubmit.innerText = t('auth.sending');
 
1
+ import { createAuthFormScope, createAuthFieldFeedback } from "./认证表单生命周期.js";
2
  // 前端页面/auth/找回账号表单组件.js
3
  // ==========================================
4
  // 🔎 找回账号表单组件
 
35
  </div>
36
  `;
37
  const formScope = createAuthFormScope(container);
38
+ const field = id => container.querySelector('#' + id);
39
+ const feedback = createAuthFieldFeedback(container, formScope, { notify: showToast });
40
 
41
  container.querySelector("#recover-to-login").onclick = (e) => { e.preventDefault(); switchView("login"); };
42
 
 
66
  btnSendCode.onclick = async (e) => {
67
  e.preventDefault();
68
  if (!formScope.current() || btnSendCode.disabled) return;
69
+ feedback.clear([field('recover-email')]);
70
 
71
  const emailInput = container.querySelector("#recover-email").value.trim();
72
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput);
73
+ if (!isEmail) return feedback.report([field('recover-email')], t('auth.invalid_email'), "error");
74
 
75
  btnSendCode.disabled = true;
76
  btnSendCode.innerText = t('auth.sending');
 
96
  const btnSubmit = container.querySelector("#btn-submit-recover");
97
  btnSubmit.onclick = async () => {
98
  if (!formScope.current() || btnSubmit.disabled) return;
99
+ feedback.clear();
100
  const email = container.querySelector("#recover-email").value.trim();
101
  const code = container.querySelector("#recover-code").value.trim();
102
 
103
+ if (!email) return feedback.report([field('recover-email')], t('auth.invalid_email'));
104
+ if (code.length !== 6) return feedback.report([field('recover-code')], t('auth.invalid_code'));
105
 
106
  btnSubmit.disabled = true;
107
  btnSubmit.innerText = t('auth.sending');
前端页面/auth/注册表单组件.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createAuthFormScope } from "./认证表单生命周期.js";
2
  import { api } from "../core/网络请求API.js";
3
  import { regionData, getSortedCountries } from "./国家地区数据.js";
4
  import { showToast } from "../components/UI交互提示组件.js";
@@ -69,6 +69,8 @@ export function renderRegisterForm(container, switchView, onSuccessCallback) {
69
  </div>
70
  `;
71
  const formScope = createAuthFormScope(container);
 
 
72
 
73
  const genderSelect = container.querySelector("#reg-gender");
74
  const avatarPreview = container.querySelector("#avatar-preview");
@@ -187,10 +189,11 @@ export function renderRegisterForm(container, switchView, onSuccessCallback) {
187
  btnRegSendCode.onclick = async (e) => {
188
  e.preventDefault();
189
  if (!formScope.current() || btnRegSendCode.disabled) return;
 
190
  const emailInput = container.querySelector("#reg-email").value.trim();
191
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput);
192
 
193
- if (!isEmail) return showToast(t('auth.invalid_email'), "error");
194
 
195
  btnRegSendCode.disabled = true;
196
  btnRegSendCode.innerText = t('auth.sending');
@@ -212,6 +215,8 @@ export function renderRegisterForm(container, switchView, onSuccessCallback) {
212
  };
213
 
214
  container.querySelector("#btn-submit-register").onclick = () => {
 
 
215
  const account = container.querySelector("#reg-account").value.trim();
216
  const name = container.querySelector("#reg-name").value.trim();
217
  const password = container.querySelector("#reg-password").value;
@@ -220,12 +225,12 @@ export function renderRegisterForm(container, switchView, onSuccessCallback) {
220
 
221
  const code = container.querySelector("#reg-code").value.trim();
222
 
223
- if (!account || !name || !password || !email || !code) return showToast(t('auth.required_fields'), "warning");
224
- if (code.length !== 6) return showToast(t('auth.invalid_code'), "warning");
225
 
226
- if (!/^[a-zA-Z0-9_]{6,20}$/.test(account)) return showToast(t('auth.account_format_error'), "error");
227
- if (password !== pwdConfirm) return showToast(t('auth.password_mismatch'), "error");
228
- if (!/^[A-Za-z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]{6,}$/.test(password)) return showToast(t('auth.password_format_error'), "error");
229
 
230
  const formData = {
231
  type: "register", account, password, email, name, gender: genderSelect.value, code,
 
1
+ import { createAuthFormScope, createAuthFieldFeedback } from "./认证表单生命周期.js";
2
  import { api } from "../core/网络请求API.js";
3
  import { regionData, getSortedCountries } from "./国家地区数据.js";
4
  import { showToast } from "../components/UI交互提示组件.js";
 
69
  </div>
70
  `;
71
  const formScope = createAuthFormScope(container);
72
+ const field = id => container.querySelector('#' + id);
73
+ const feedback = createAuthFieldFeedback(container, formScope, { notify: showToast, relatedFields: [[field('reg-password'), field('reg-password-confirm')]] });
74
 
75
  const genderSelect = container.querySelector("#reg-gender");
76
  const avatarPreview = container.querySelector("#avatar-preview");
 
189
  btnRegSendCode.onclick = async (e) => {
190
  e.preventDefault();
191
  if (!formScope.current() || btnRegSendCode.disabled) return;
192
+ feedback.clear([field('reg-email')]);
193
  const emailInput = container.querySelector("#reg-email").value.trim();
194
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailInput);
195
 
196
+ if (!isEmail) return feedback.report([field('reg-email')], t('auth.invalid_email'), "error");
197
 
198
  btnRegSendCode.disabled = true;
199
  btnRegSendCode.innerText = t('auth.sending');
 
215
  };
216
 
217
  container.querySelector("#btn-submit-register").onclick = () => {
218
+ if (!formScope.current()) return;
219
+ feedback.clear();
220
  const account = container.querySelector("#reg-account").value.trim();
221
  const name = container.querySelector("#reg-name").value.trim();
222
  const password = container.querySelector("#reg-password").value;
 
225
 
226
  const code = container.querySelector("#reg-code").value.trim();
227
 
228
+ if (!account || !name || !password || !email || !code) return feedback.report([['reg-account', account], ['reg-name', name], ['reg-password', password], ['reg-email', email], ['reg-code', code]].filter(([, value]) => !value).map(([id]) => field(id)), t('auth.required_fields'));
229
+ if (code.length !== 6) return feedback.report([field('reg-code')], t('auth.invalid_code'));
230
 
231
+ if (!/^[a-zA-Z0-9_]{6,20}$/.test(account)) return feedback.report([field('reg-account')], t('auth.account_format_error'), "error");
232
+ if (password !== pwdConfirm) return feedback.report([field('reg-password-confirm')], t('auth.password_mismatch'), "error");
233
+ if (!/^[A-Za-z0-9!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]{6,}$/.test(password)) return feedback.report([field('reg-password')], t('auth.password_format_error'), "error");
234
 
235
  const formData = {
236
  type: "register", account, password, email, name, gender: genderSelect.value, code,
前端页面/auth/登录表单组件.js CHANGED
@@ -1,3 +1,4 @@
 
1
  import { showToast } from "../components/UI交互提示组件.js";
2
  import { t } from "../components/用户体验增强.js";
3
 
@@ -21,15 +22,20 @@ export function renderLoginForm(container, switchView, onSuccessCallback) {
21
  </div>
22
  `;
23
 
 
 
 
24
  container.querySelector("#toggle-to-register").onclick = (e) => { e.preventDefault(); switchView("register"); };
25
  container.querySelector("#toggle-to-reset").onclick = (e) => { e.preventDefault(); switchView("reset"); };
26
  container.querySelector("#toggle-to-recover").onclick = (e) => { e.preventDefault(); switchView("recover"); };
27
  container.querySelector("#btn-submit-login").onclick = () => {
 
 
28
  const account = container.querySelector("#login-account").value.trim();
29
  const password = container.querySelector("#login-password").value;
30
  const remember = container.querySelector("#login-remember").checked;
31
 
32
- if (!account || !password) return showToast(t('auth.account_password_required'), "warning");
33
 
34
  const formData = { type: "login", account, password, remember };
35
  if (onSuccessCallback) onSuccessCallback(formData);
 
1
+ import { createAuthFormScope, createAuthFieldFeedback } from "./认证表单生命周期.js";
2
  import { showToast } from "../components/UI交互提示组件.js";
3
  import { t } from "../components/用户体验增强.js";
4
 
 
22
  </div>
23
  `;
24
 
25
+ const formScope = createAuthFormScope(container);
26
+ const field = id => container.querySelector('#' + id);
27
+ const feedback = createAuthFieldFeedback(container, formScope, { notify: showToast });
28
  container.querySelector("#toggle-to-register").onclick = (e) => { e.preventDefault(); switchView("register"); };
29
  container.querySelector("#toggle-to-reset").onclick = (e) => { e.preventDefault(); switchView("reset"); };
30
  container.querySelector("#toggle-to-recover").onclick = (e) => { e.preventDefault(); switchView("recover"); };
31
  container.querySelector("#btn-submit-login").onclick = () => {
32
+ if (!formScope.current()) return;
33
+ feedback.clear();
34
  const account = container.querySelector("#login-account").value.trim();
35
  const password = container.querySelector("#login-password").value;
36
  const remember = container.querySelector("#login-remember").checked;
37
 
38
+ if (!account || !password) return feedback.report([['login-account', account], ['login-password', password]].filter(([, value]) => !value).map(([id]) => field(id)), t('auth.account_password_required'));
39
 
40
  const formData = { type: "login", account, password, remember };
41
  if (onSuccessCallback) onSuccessCallback(formData);
前端页面/auth/认证表单生命周期.js CHANGED
@@ -19,3 +19,58 @@ export function createAuthFormScope(container) {
19
  }
20
  };
21
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  }
20
  };
21
  }
22
+
23
+ let nextFieldFeedbackId = 0;
24
+
25
+ export function createAuthFieldFeedback(container, scope, { notify, relatedFields = [] } = {}) {
26
+ const errors = new Map();
27
+ const clearField = input => {
28
+ const record = errors.get(input);
29
+ if (!record) return;
30
+ const descriptions = (input.getAttribute('aria-describedby') || '').split(/\s+/).filter(id => id && id !== record.node.id);
31
+ if (descriptions.length) input.setAttribute('aria-describedby', descriptions.join(' '));
32
+ else input.removeAttribute('aria-describedby');
33
+ if (input.getAttribute('aria-invalid') === 'true') {
34
+ if (record.previousInvalid === null) input.removeAttribute('aria-invalid');
35
+ else input.setAttribute('aria-invalid', record.previousInvalid);
36
+ }
37
+ record.node.remove();
38
+ errors.delete(input);
39
+ };
40
+ const onInput = event => {
41
+ if (!scope.current()) return;
42
+ const fields = relatedFields.find(group => group.includes(event.target)) || [event.target];
43
+ fields.forEach(clearField);
44
+ };
45
+ container.addEventListener('input', onInput);
46
+ scope.addCleanup(() => {
47
+ container.removeEventListener('input', onInput);
48
+ [...errors.keys()].forEach(clearField);
49
+ });
50
+ return {
51
+ clear(fields = [...errors.keys()]) {
52
+ if (scope.current()) fields.forEach(clearField);
53
+ },
54
+ report(fields, message, level = 'warning') {
55
+ if (!scope.current()) return;
56
+ const inputs = [...new Set(fields)].filter(input => input && container.contains(input) && !input.disabled);
57
+ inputs.forEach((input, index) => {
58
+ clearField(input);
59
+ const node = container.ownerDocument.createElement('div');
60
+ node.id = `ranking-auth-error-${++nextFieldFeedbackId}`;
61
+ node.className = 'auth-form__error auth-field-error';
62
+ node.textContent = message;
63
+ if (index === 0) node.setAttribute('role', 'alert');
64
+ const previousInvalid = input.getAttribute('aria-invalid');
65
+ const descriptions = (input.getAttribute('aria-describedby') || '').split(/\s+/).filter(Boolean);
66
+ input.setAttribute('aria-describedby', [...new Set([...descriptions, node.id])].join(' '));
67
+ input.setAttribute('aria-invalid', 'true');
68
+ const row = input.closest('.auth-form__code-row');
69
+ (row && container.contains(row) ? row : input).after(node);
70
+ errors.set(input, { node, previousInvalid });
71
+ });
72
+ inputs[0]?.focus();
73
+ notify?.(message, level);
74
+ }
75
+ };
76
+ }
前端页面/auth/重置密码表单组件.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createAuthFormScope } from "./认证表单生命周期.js";
2
  import { api } from "../core/网络请求API.js";
3
  import { showToast } from "../components/UI交互提示组件.js";
4
  import { t } from "../components/用户体验增强.js";
@@ -25,6 +25,8 @@ export function renderResetForm(container, switchView, onSuccessCallback, prefil
25
  </div>
26
  `;
27
  const formScope = createAuthFormScope(container);
 
 
28
 
29
  container.querySelector("#toggle-to-login").onclick = (e) => { e.preventDefault(); switchView("login"); };
30
 
@@ -60,15 +62,16 @@ export function renderResetForm(container, switchView, onSuccessCallback, prefil
60
  btnSendCode.onclick = async (e) => {
61
  e.preventDefault();
62
  if (!formScope.current() || btnSendCode.disabled) return;
 
63
 
64
  // 先抓取上方输入的账号,没填就不让发验证码
65
  const accountInput = container.querySelector("#reset-account").value.trim();
66
- if (!accountInput) return showToast(t('auth.enter_account_first'), "warning");
67
 
68
  const verifyInput = container.querySelector("#reset-verify").value.trim();
69
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(verifyInput);
70
 
71
- if (!isEmail) return showToast(t('auth.invalid_email'), "error");
72
 
73
  const contactType = "email";
74
 
@@ -93,6 +96,8 @@ export function renderResetForm(container, switchView, onSuccessCallback, prefil
93
  };
94
 
95
  container.querySelector("#btn-submit-reset").onclick = () => {
 
 
96
  const acc = container.querySelector("#reset-account").value.trim();
97
  const verify = container.querySelector("#reset-verify").value.trim();
98
  const code = container.querySelector("#reset-code").value.trim();
@@ -100,12 +105,12 @@ export function renderResetForm(container, switchView, onSuccessCallback, prefil
100
  const np = container.querySelector("#reset-new-password").value;
101
  const cp = container.querySelector("#reset-confirm-password").value;
102
 
103
- if (!acc || !verify || !np || !code) return showToast(t('auth.reset_required_fields'), "warning");
104
- if (code.length !== 6) return showToast(t('auth.invalid_code'), "warning");
105
 
106
 
107
- if (np !== cp) return showToast(t('auth.password_mismatch'), "error");
108
- if (np.length < 6) return showToast(t('auth.password_format_error'), "error");
109
 
110
  const formData = {
111
  type: "reset",
 
1
+ import { createAuthFormScope, createAuthFieldFeedback } from "./认证表单生命周期.js";
2
  import { api } from "../core/网络请求API.js";
3
  import { showToast } from "../components/UI交互提示组件.js";
4
  import { t } from "../components/用户体验增强.js";
 
25
  </div>
26
  `;
27
  const formScope = createAuthFormScope(container);
28
+ const field = id => container.querySelector('#' + id);
29
+ const feedback = createAuthFieldFeedback(container, formScope, { notify: showToast, relatedFields: [[field('reset-new-password'), field('reset-confirm-password')]] });
30
 
31
  container.querySelector("#toggle-to-login").onclick = (e) => { e.preventDefault(); switchView("login"); };
32
 
 
62
  btnSendCode.onclick = async (e) => {
63
  e.preventDefault();
64
  if (!formScope.current() || btnSendCode.disabled) return;
65
+ feedback.clear([field('reset-account'), field('reset-verify')]);
66
 
67
  // 先抓取上方输入的账号,没填就不让发验证码
68
  const accountInput = container.querySelector("#reset-account").value.trim();
69
+ if (!accountInput) return feedback.report([field('reset-account')], t('auth.enter_account_first'));
70
 
71
  const verifyInput = container.querySelector("#reset-verify").value.trim();
72
  const isEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(verifyInput);
73
 
74
+ if (!isEmail) return feedback.report([field('reset-verify')], t('auth.invalid_email'), "error");
75
 
76
  const contactType = "email";
77
 
 
96
  };
97
 
98
  container.querySelector("#btn-submit-reset").onclick = () => {
99
+ if (!formScope.current()) return;
100
+ feedback.clear();
101
  const acc = container.querySelector("#reset-account").value.trim();
102
  const verify = container.querySelector("#reset-verify").value.trim();
103
  const code = container.querySelector("#reset-code").value.trim();
 
105
  const np = container.querySelector("#reset-new-password").value;
106
  const cp = container.querySelector("#reset-confirm-password").value;
107
 
108
+ if (!acc || !verify || !np || !code) return feedback.report([['reset-account', acc], ['reset-verify', verify], ['reset-new-password', np], ['reset-code', code]].filter(([, value]) => !value).map(([id]) => field(id)), t('auth.reset_required_fields'));
109
+ if (code.length !== 6) return feedback.report([field('reset-code')], t('auth.invalid_code'));
110
 
111
 
112
+ if (np !== cp) return feedback.report([field('reset-confirm-password')], t('auth.password_mismatch'), "error");
113
+ if (np.length < 6) return feedback.report([field('reset-new-password')], t('auth.password_format_error'), "error");
114
 
115
  const formData = {
116
  type: "reset",
前端页面/styles/主题基础.css CHANGED
@@ -347,7 +347,7 @@
347
  .comfy-ranking-root .top-nav__icon { position: relative; flex: 0 0 36px; width: 36px; height: 36px; padding: 4px; box-sizing: border-box; border: 1px solid transparent; border-radius: var(--rk-radius-sm); background: transparent; color: inherit; font-size: 18px; line-height: 1; cursor: pointer; }
348
  .comfy-ranking-root .top-nav__identity { flex: 1 1 auto; min-width: 0; min-height: 36px; padding: 6px 12px; box-sizing: border-box; border: 1px solid var(--rk-border); border-radius: var(--rk-radius-sm); background: var(--rk-panel); color: var(--rk-text); font: inherit; font-size: 12px; line-height: 1.4; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
349
  .comfy-ranking-root .top-nav__actions:has(.top-nav__identity[data-authenticated="true"]) { flex: 0 0 auto; margin-inline-start: auto; }
350
- .comfy-ranking-root .top-nav__identity[data-authenticated="true"] { flex: 0 0 50px; width: 50px; height: 50px; padding: 0; border: 2px solid var(--rk-border); border-radius: 50%; }
351
  .comfy-ranking-root .top-nav__avatar { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
352
  .comfy-ranking-root .top-nav__badge { position: absolute; top: -2px; right: -3px; padding: 2px 5px; border-radius: 10px; background: #c62828; color: #fff; font-size: 10px; font-weight: bold; line-height: 1; }
353
  .comfy-ranking-root .top-nav button:focus-visible { outline: 2px solid var(--rk-focus); outline-offset: 2px; }
@@ -1685,7 +1685,8 @@ button.ranking-rating__star { cursor: pointer; min-width: 28px; min-height: 28px
1685
  .auth-form__avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; border: 2px solid var(--rk-border, var(--border-color, #555)); box-sizing: border-box; }
1686
  .auth-form__file { width: 100%; min-width: 0; max-width: 100%; min-height: 36px; color: inherit; font: inherit; font-size: 12px; }
1687
  .auth-form__file::file-selector-button { min-height: 36px; max-width: 100%; padding: 4px; border: 1px solid var(--rk-border, var(--border-color, #555)); border-radius: var(--rk-radius-sm, 6px); background: var(--rk-bg, var(--comfy-menu-bg, #202020)); color: inherit; font: inherit; cursor: pointer; }
1688
- .auth-form__error { color: var(--rk-danger, #ef5350); font-size: 12px; overflow-wrap: anywhere; }
 
1689
  .auth-form__age { color: var(--rk-muted, var(--descrip-text, #a4a4a4)); font-size: 12px; overflow-wrap: anywhere; }
1690
  .auth-form__textarea { resize: vertical; }
1691
 
 
347
  .comfy-ranking-root .top-nav__icon { position: relative; flex: 0 0 36px; width: 36px; height: 36px; padding: 4px; box-sizing: border-box; border: 1px solid transparent; border-radius: var(--rk-radius-sm); background: transparent; color: inherit; font-size: 18px; line-height: 1; cursor: pointer; }
348
  .comfy-ranking-root .top-nav__identity { flex: 1 1 auto; min-width: 0; min-height: 36px; padding: 6px 12px; box-sizing: border-box; border: 1px solid var(--rk-border); border-radius: var(--rk-radius-sm); background: var(--rk-panel); color: var(--rk-text); font: inherit; font-size: 12px; line-height: 1.4; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
349
  .comfy-ranking-root .top-nav__actions:has(.top-nav__identity[data-authenticated="true"]) { flex: 0 0 auto; margin-inline-start: auto; }
350
+ .comfy-ranking-root .top-nav__identity[data-authenticated="true"] { flex: 0 0 36px; width: 36px; height: 36px; padding: 0; border: 2px solid var(--rk-border); border-radius: 50%; }
351
  .comfy-ranking-root .top-nav__avatar { display: block; width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }
352
  .comfy-ranking-root .top-nav__badge { position: absolute; top: -2px; right: -3px; padding: 2px 5px; border-radius: 10px; background: #c62828; color: #fff; font-size: 10px; font-weight: bold; line-height: 1; }
353
  .comfy-ranking-root .top-nav button:focus-visible { outline: 2px solid var(--rk-focus); outline-offset: 2px; }
 
1685
  .auth-form__avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; border: 2px solid var(--rk-border, var(--border-color, #555)); box-sizing: border-box; }
1686
  .auth-form__file { width: 100%; min-width: 0; max-width: 100%; min-height: 36px; color: inherit; font: inherit; font-size: 12px; }
1687
  .auth-form__file::file-selector-button { min-height: 36px; max-width: 100%; padding: 4px; border: 1px solid var(--rk-border, var(--border-color, #555)); border-radius: var(--rk-radius-sm, 6px); background: var(--rk-bg, var(--comfy-menu-bg, #202020)); color: inherit; font: inherit; cursor: pointer; }
1688
+ .auth-form__error { color: var(--rk-danger, #ef5350); font-size: 12px; overflow-wrap: anywhere; }
1689
+ .auth-form__error.auth-field-error { color: var(--rk-text, var(--fg-color, #ddd)); border-inline-start: 2px solid var(--rk-danger, #ef5350); padding-inline-start: 6px; }
1690
  .auth-form__age { color: var(--rk-muted, var(--descrip-text, #a4a4a4)); font-size: 12px; overflow-wrap: anywhere; }
1691
  .auth-form__textarea { resize: vertical; }
1692