import { html, useState, useEffect, useRef, useCallback, } from './vendor/htm-preact.js'; import { t } from './i18n.js'; import { hubFetch, navigate, session, HUB, loadAuth, _storeRecoveryKey, } from './hub-client.js'; import * as platform from './platform.js'; import { Icon } from './icon.js'; import { loadPending } from './invite-link.js'; const PASSWORD_MIN_BITS = 60; const PASSWORD_MIN_LEN = 12; // The hub's USERNAME_MIN_LEN (api/users.py), checked here so the refusal comes // 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`
${t('firstrun.hint')}
${error && html`${t('firstrun.note')}
${t('welcome.lead')}
${t('welcome.groups')}
<${Icon} name="lock" />${t('welcome.e2e')}
${t('welcome.hub_body')}
${t('welcome.hub_never')}
${t('welcome.hub_free')}
${t('register.verified_msg')}
${loadPending() && html`${t('invite.after_register')}
`} ${t('register.go_login')}${t('register.recovery_intro')}
${recoveryMnemonic}
${emailRecovery ? t('register.recovery_emailed') : t('register.recovery_not_emailed')}
${t('register.recovery_warning')}
${t('register.success_msg')}
${t('reset.working')} ${progress && progress.total ? ` (${progress.done}/${progress.total})` : ''}
${t('reset.signin_restored')}
${error && html`${t('reset.no_recovery')}
`} ${phase === 'done' && !error && stragglers.length === 0 && html`${t('reset.groups_restored')}
`} ${stragglers.length > 0 && html`${t('reset.needs_operator')}
${t('reset.request_intro')}
`} ${phase === 'form' && html`${t('reset.form_intro')}
`}