diff options
Diffstat (limited to 'packages')
13 files changed, 90 insertions, 9 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js index 8958d44..f0af187 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/auth-page.js @@ -14,6 +14,35 @@ const PASSWORD_MIN_LEN = 12; // before a passphrase derivation rather than after it. const USERNAME_MIN_LEN = 8; +// ── Passphrase field ───────────────────────────────────────────────────────── +// +// A passphrase this long is mistyped often enough that checking it is worth a +// control, and the alternative people reach for otherwise is typing it into +// the username box to read it back. The button is out of the tab order +// (`tabindex="-1"`): everyone who is not reaching for it would pay a keystroke +// between the passphrase and the submit button, and it does nothing a keyboard +// user cannot do by other means. `type="button"` matters — a bare button in a +// form submits it, which here would try to sign in on the first click. +function PasswordInput({ + value, onInput, placeholder, autocomplete, + minlength = null, autofocus = false, +}) { + const [shown, setShown] = useState(false); + const label = t(shown ? 'login.hide_password' : 'login.show_password'); + return html` + <div class="pw-field"> + <input type=${shown ? 'text' : 'password'} placeholder=${placeholder} + value=${value} onInput=${onInput} autocomplete=${autocomplete} + required minlength=${minlength} autofocus=${autofocus} /> + <button type="button" class="pw-toggle" tabindex="-1" + title=${label} aria-label=${label} aria-pressed=${shown} + onClick=${() => setShown(v => !v)}> + <${Icon} name=${shown ? 'eyeoff' : 'eye'} /> + </button> + </div> + `; +} + // ── reCAPTCHA v2 helper ────────────────────────────────────────────────────── let _captchaSiteKey = null; @@ -183,9 +212,9 @@ export function LoginPage({ onLogin }) { <input type="text" placeholder="${t('login.username')}" value=${username} onInput=${e => setUsername(e.target.value)} autocomplete="username" required autofocus /> - <input type="password" placeholder="${t('login.password')}" value=${password} - onInput=${e => setPassword(e.target.value)} - autocomplete="current-password" required /> + <${PasswordInput} placeholder=${t('login.password')} value=${password} + onInput=${e => setPassword(e.target.value)} + autocomplete="current-password" /> ${error && html`<div class="error-msg">${error}</div>`} ${pendingVerif && html` <div class="error-msg" style="background:var(--bg-secondary);border-left:3px solid var(--yellow, #f59e0b)"> @@ -474,9 +503,9 @@ export function RegisterPage() { <input type="email" placeholder="${t('register.email')}" value=${email} onInput=${e => setEmail(e.target.value)} autocomplete="email" required /> - <input type="password" placeholder="${t('register.password')}" value=${password} - onInput=${e => setPassword(e.target.value)} - autocomplete="new-password" required minlength="8" /> + <${PasswordInput} placeholder=${t('register.password')} value=${password} + onInput=${e => setPassword(e.target.value)} + autocomplete="new-password" minlength="8" /> <div style=${`margin:-4px 0 10px;${password ? '' : 'visibility:hidden;height:0;margin:0;overflow:hidden'}`}> <div style="height:4px;background:var(--border);border-radius:2px;overflow:hidden"> <div style=${`height:100%;width:${Math.min(100, passwordBits(password) / 100 * 100)}%; @@ -487,9 +516,9 @@ export function RegisterPage() { ${t('register.strength', { bits: passwordBits(password) })} </p> </div> - <input type="password" placeholder="${t('register.confirm')}" value=${confirm} - onInput=${e => setConfirm(e.target.value)} - autocomplete="new-password" required /> + <${PasswordInput} placeholder=${t('register.confirm')} value=${confirm} + onInput=${e => setConfirm(e.target.value)} + autocomplete="new-password" /> <label style="display:flex; gap:8px; align-items:flex-start; margin:4px 0 2px; font-size:0.88em; color:var(--text-secondary)"> <input type="checkbox" checked=${emailRecovery} diff --git a/packages/meshbay-hub/src/meshbay_hub/static/icon.js b/packages/meshbay-hub/src/meshbay_hub/static/icon.js index 94a82fb..c80258c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/icon.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/icon.js @@ -29,6 +29,12 @@ const ICON_PATHS = { play: ['M8 5.5v13l11-6.5z'], eye: ['M2 12s3.6-6.5 10-6.5S22 12 22 12s-3.6 6.5-10 6.5S2 12 2 12', 'M12 14.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5'], + // The same eye under a stroke. Drawn as `eye` plus the slash rather than as + // a broken lid, so the two states of a reveal button are the same glyph and + // the toggle reads as one control. + eyeoff: ['M2 12s3.6-6.5 10-6.5S22 12 22 12s-3.6 6.5-10 6.5S2 12 2 12', + 'M12 14.5a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5', + 'M4 4l16 16'], trash: ['M4 7h16', 'M10 11v6M14 11v6', 'M6 7l1 12.5A1.5 1.5 0 0 0 8.5 21h7a1.5 1.5 0 0 0 1.5-1.5L18 7', 'M9.5 7V5a1.5 1.5 0 0 1 1.5-1.5h2A1.5 1.5 0 0 1 14.5 5v2'], diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js index 47f0b31..989b5ca 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js @@ -36,6 +36,8 @@ export default { 'login.no_account': 'Noch kein Konto?', 'login.register_link': 'Konto erstellen', 'login.forgot': 'Passphrase vergessen?', + 'login.show_password': 'Passphrase anzeigen', + 'login.hide_password': 'Passphrase verbergen', 'reset.title': 'Passphrase zurücksetzen', 'reset.request_intro': 'Geben Sie Ihren Benutzernamen und die E-Mail des Kontos ein. Stimmen sie überein, wird ein Zurücksetzungscode an diese Adresse gesendet.', 'reset.send_code': 'Code senden', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js index 92a45dd..5042b31 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js @@ -39,6 +39,8 @@ export default { 'login.no_account': 'No account?', 'login.register_link': 'Register', 'login.forgot': 'Forgot your passphrase?', + 'login.show_password': 'Show passphrase', + 'login.hide_password': 'Hide passphrase', 'reset.title': 'Reset passphrase', 'reset.request_intro': 'Enter your username and the email on the account. If they match, a reset code is sent to it.', 'reset.send_code': 'Send code', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js index ae2a405..80beb15 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js @@ -35,6 +35,8 @@ export default { 'login.no_account': '¿No tiene cuenta?', 'login.register_link': 'Crear una cuenta', 'login.forgot': '¿Olvidó su frase de contraseña?', + 'login.show_password': 'Mostrar la frase de contraseña', + 'login.hide_password': 'Ocultar la frase de contraseña', 'reset.title': 'Restablecer la frase de contraseña', 'reset.request_intro': 'Introduzca su nombre de usuario y el correo de la cuenta. Si coinciden, se envía un código de restablecimiento a esa dirección.', 'reset.send_code': 'Enviar código', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js index 795a06c..bc2e221 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js @@ -35,6 +35,8 @@ export default { 'login.no_account': 'Pas encore de compte ?', 'login.register_link': 'Créer un compte', 'login.forgot': 'Phrase secrète oubliée ?', + 'login.show_password': 'Afficher la phrase secrète', + 'login.hide_password': 'Masquer la phrase secrète', 'reset.title': 'Réinitialiser la phrase secrète', 'reset.request_intro': 'Saisissez votre nom d’utilisateur et l’e-mail du compte. S’ils correspondent, un code de réinitialisation est envoyé à cette adresse.', 'reset.send_code': 'Envoyer le code', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js index 2cb1a31..34296e1 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js @@ -36,6 +36,8 @@ export default { 'login.no_account': 'Non ha ancora un account?', 'login.register_link': 'Crea un account', 'login.forgot': 'Ha dimenticato la passphrase?', + 'login.show_password': 'Mostra la passphrase', + 'login.hide_password': 'Nascondi la passphrase', 'reset.title': 'Reimposta la passphrase', 'reset.request_intro': 'Inserisca il suo nome utente e l’e-mail dell’account. Se corrispondono, un codice di reimpostazione viene inviato a quell’indirizzo.', 'reset.send_code': 'Invia codice', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js index e26110f..d256d1b 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js @@ -36,6 +36,8 @@ export default { 'login.no_account': 'アカウントをお持ちでないですか?', 'login.register_link': 'アカウントを作成', 'login.forgot': 'パスフレーズをお忘れですか?', + 'login.show_password': 'パスフレーズを表示', + 'login.hide_password': 'パスフレーズを非表示', 'reset.title': 'パスフレーズをリセット', 'reset.request_intro': 'ユーザー名とアカウントのメールアドレスを入力してください。両方が一致する場合、そのアドレスにリセットコードを送信します。', 'reset.send_code': 'コードを送信', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js index ce3cdf9..2c5c854 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js @@ -36,6 +36,8 @@ export default { 'login.no_account': 'Nog geen account?', 'login.register_link': 'Account aanmaken', 'login.forgot': 'Wachtwoordzin vergeten?', + 'login.show_password': 'Wachtwoordzin tonen', + 'login.hide_password': 'Wachtwoordzin verbergen', 'reset.title': 'Wachtwoordzin opnieuw instellen', 'reset.request_intro': 'Voer uw gebruikersnaam en het e-mailadres van het account in. Als ze overeenkomen, wordt een herstelcode naar dat adres gestuurd.', 'reset.send_code': 'Code versturen', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js index 7f347da..f2cfb8d 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js @@ -39,6 +39,8 @@ export default { 'login.no_account': 'Nie ma jeszcze konta?', 'login.register_link': 'Załóż konto', 'login.forgot': 'Nie pamiętasz hasła-frazy?', + 'login.show_password': 'Pokaż hasło-frazę', + 'login.hide_password': 'Ukryj hasło-frazę', 'reset.title': 'Zresetuj hasło-frazę', 'reset.request_intro': 'Wpisz swoją nazwę użytkownika i adres e-mail konta. Jeśli się zgadzają, kod resetowania zostanie wysłany na ten adres.', 'reset.send_code': 'Wyślij kod', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js index f0f1077..9d51a9c 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js @@ -37,6 +37,8 @@ export default { 'login.no_account': 'Ainda não tem conta?', 'login.register_link': 'Criar uma conta', 'login.forgot': 'Esqueceu sua frase secreta?', + 'login.show_password': 'Mostrar a frase secreta', + 'login.hide_password': 'Ocultar a frase secreta', 'reset.title': 'Redefinir a frase secreta', 'reset.request_intro': 'Digite seu nome de usuário e o e-mail da conta. Se coincidirem, um código de redefinição é enviado para esse endereço.', 'reset.send_code': 'Enviar código', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js index be337b7..5ca0571 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js @@ -36,6 +36,8 @@ export default { 'login.no_account': '还没有账户?', 'login.register_link': '注册', 'login.forgot': '忘记密码短语?', + 'login.show_password': '显示密码短语', + 'login.hide_password': '隐藏密码短语', 'reset.title': '重置密码短语', 'reset.request_intro': '输入您的用户名和账户的邮箱。如果二者匹配,重置码将发送至该邮箱。', 'reset.send_code': '发送验证码', diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index 7c36ed7..e012a1a 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -830,6 +830,32 @@ input[type="email"] { } input:focus { outline: none; border-color: var(--border-focus); } +/* A reveal button inside the field rather than beside it. The input reserves + the room on its right, so a passphrase long enough to scroll ends under the + button instead of behind it. The `background: none` pair is not redundant: + the bare `button` rules just below paint every button with the accent colour + on rest and on hover, and this one has to stay part of the field. */ +.pw-field { position: relative; display: flex; } +.pw-field input { padding-right: 40px; } +.pw-toggle { + position: absolute; + top: 0; + right: 4px; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + width: 32px; + padding: 0; + background: none; + border: none; + border-radius: 6px; + color: var(--text-dim); + font-size: 1.15em; + cursor: pointer; +} +.pw-toggle:hover { background: none; color: var(--text); } + button { padding: 10px 20px; background: var(--accent); |