mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-10-18 21:47:17 +02:00
fix: place accessible password visibility toggle inside login input
This commit is contained in:
1 parent
4d397a3741
commit
d590cdd714
5 files changed
+75
-12
No files matched your search
@@ -22,3 +22,7 @@ REQ-003 / AC-003: 图标随状态切换,显示密码/隐藏密码标签和悬
|
||||
|
||||
修改 index.html 的按钮内部结构,style.css 的局部布局和交互样式,auth.js 的标签同步,扩充已有 password-feedback.test.cjs 的状态/标签断言。
|
||||
验证:已有密码反馈测试、JS syntax、diff check,实际浏览器桌面/窄屏与语言、键盘切换检查。回退可 revert 本功能代码提交。发布记录在 CHANGELOG.md Unreleased;不合并或发布。
|
||||
|
||||
## F4 Implementation
|
||||
|
||||
已替换独立文字按钮为输入框内 20px SVG 眼睛,44px 原生按钮点击区域,登录两输入框最小高度 48px;支持主题色及 focus-visible。JS 不再替换按钮 textContent,使用 aria-label/title 同步现有翻译键;模板标记支持语言重绘。更新 CSS/auth.js 缓存版本。已有密码反馈测试 4/4、JS syntax 和 diff check 通过。
|
||||
@@ -6551,19 +6551,59 @@ html[data-theme="dark"] .chat-input-container .send-btn:disabled::before {
|
||||
}
|
||||
|
||||
.login-password-field {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-password-field input {
|
||||
.login-form .login-password-field input[type="password"],
|
||||
.login-form .login-password-field input[type="text"] {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding-inline-end: 52px;
|
||||
}
|
||||
|
||||
.login-password-field button {
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
.login-password-toggle {
|
||||
position: absolute;
|
||||
inset-inline-end: 2px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
border-radius: 8px;
|
||||
background: transparent;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.login-password-toggle:hover {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.login-password-toggle:focus-visible {
|
||||
outline: 2px solid var(--accent-color);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.login-password-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
flex: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-password-icon--hide,
|
||||
.login-password-toggle[aria-pressed="true"] .login-password-icon--show {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-password-toggle[aria-pressed="true"] .login-password-icon--hide {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.password-field-error {
|
||||
@@ -6587,6 +6627,7 @@ html[data-theme="dark"] .chat-input-container .send-btn:disabled::before {
|
||||
|
||||
.login-form input[type="text"],
|
||||
.login-form input[type="password"] {
|
||||
min-height: 48px;
|
||||
width: 100%;
|
||||
padding: 12px 14px;
|
||||
font-size: 0.9375rem;
|
||||
|
||||
@@ -100,7 +100,9 @@ function setLoginPasswordVisible(visible) {
|
||||
button.setAttribute('aria-pressed', String(visible));
|
||||
const key = visible ? 'login.hidePassword' : 'login.showPassword';
|
||||
button.setAttribute('data-i18n', key);
|
||||
button.textContent = typeof window.t === 'function' ? window.t(key) : (visible ? '隐藏密码' : '显示密码');
|
||||
const label = typeof window.t === 'function' ? window.t(key) : (visible ? '隐藏密码' : '显示密码');
|
||||
button.setAttribute('aria-label', label);
|
||||
button.setAttribute('title', label);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -42,10 +42,18 @@ test('password preview toggles accessibly and can be reset without changing the
|
||||
h.ctx.toggleLoginPasswordVisibility();
|
||||
assert.equal(h.fields['login-password'].type,'text');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('aria-pressed'),'true');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('aria-label'),'login.hidePassword');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('title'),'login.hidePassword');
|
||||
h.ctx.setLoginPasswordVisible(false);
|
||||
assert.equal(h.fields['login-password'].type,'password');
|
||||
assert.equal(h.fields['login-password'].value,'test-only-password');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('data-i18n'),'login.showPassword');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('aria-label'),'login.showPassword');
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('title'),'login.showPassword');
|
||||
assert.equal(h.fields['login-password-toggle'].textContent,undefined);
|
||||
h.ctx.window.t=undefined;
|
||||
h.ctx.toggleLoginPasswordVisibility();
|
||||
assert.equal(h.fields['login-password-toggle'].getAttribute('aria-label'),'隐藏密码');
|
||||
});
|
||||
test('password form has field-specific errors without requesting a save',async () => {
|
||||
const h=harness({'auth-new-password':'short','auth-confirm-password':'different'});
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260907-blocked-1">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20261008-password-eye-1">
|
||||
<link rel="stylesheet" href="/static/css/tool-guard.css?v=20260907-7">
|
||||
<link rel="stylesheet" href="/static/css/chat-plan-progress.css?v=20260813-4">
|
||||
<link rel="stylesheet" href="/static/css/c2.css">
|
||||
@@ -69,7 +69,15 @@
|
||||
<label for="login-password" data-i18n="login.passwordLabel">密码</label>
|
||||
<div class="login-password-field">
|
||||
<input type="password" id="login-password" placeholder="输入登录密码" required autocomplete="current-password" aria-describedby="login-error" data-i18n="login.passwordPlaceholder" data-i18n-attr="placeholder" />
|
||||
<button type="button" id="login-password-toggle" class="btn-secondary" aria-controls="login-password" aria-pressed="false" onclick="toggleLoginPasswordVisibility()" data-i18n="login.showPassword">显示密码</button>
|
||||
<button type="button" id="login-password-toggle" class="login-password-toggle" aria-label="显示密码" title="显示密码" aria-controls="login-password" aria-pressed="false" onclick="toggleLoginPasswordVisibility()" data-i18n="login.showPassword" data-i18n-attr="aria-label,title" data-i18n-skip-text="true">
|
||||
<svg class="login-password-icon login-password-icon--show" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
||||
<path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z" />
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
</svg>
|
||||
<svg class="login-password-icon login-password-icon--hide" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">
|
||||
<path d="m3 3 18 18M10.6 5.1A12 12 0 0 1 12 5c6.5 0 10 7 10 7a18 18 0 0 1-3 4M6.2 6.2A18 18 0 0 0 2 12s3.5 7 10 7a12 12 0 0 0 5.8-1.8M9.9 9.9a3 3 0 0 0 4.2 4.2" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="login-error" class="login-error" role="alert" style="display: none;"></div>
|
||||
@@ -6946,7 +6954,7 @@
|
||||
<script src="/static/js/i18n.js"></script>
|
||||
<script src="/static/js/theme.js"></script>
|
||||
<script src="/static/js/builtin-tools.js"></script>
|
||||
<script src="/static/js/auth.js?v=20260907-blocked-1"></script>
|
||||
<script src="/static/js/auth.js?v=20261008-password-eye-1"></script>
|
||||
<script src="/static/js/modal.js"></script>
|
||||
<script src="/static/js/notifications.js"></script>
|
||||
<script src="/static/js/info-collect.js?v=20260717-1"></script>
|
||||
|
||||
Reference in new issue
Block a user