diff --git a/web/src/lib/SecretPrompt.svelte b/web/src/lib/SecretPrompt.svelte index 69e8436..3423951 100644 --- a/web/src/lib/SecretPrompt.svelte +++ b/web/src/lib/SecretPrompt.svelte @@ -3,6 +3,12 @@ * The password `workspace.askSecret` asks for, in the same place as the * `[y/N]` prompt. A masked field of its own rather than the command line, so * a password never shows on screen or lands in the command history. + * + * It appears at once rather than rising into place like the `[y/N]` prompt: + * it is where the next keys go, and a field still sliding and fading in + * under them reads as a stutter. Password managers are asked to leave the + * field alone: it is not the login, and their menus redrawn on every key + * are a flicker of their own. */ import { workspace } from './store.svelte' @@ -38,6 +44,10 @@ type="password" autocomplete="off" spellcheck="false" + data-1p-ignore + data-lpignore="true" + data-bwignore + data-form-type="other" bind:this={input} bind:value {onkeydown} @@ -67,7 +77,6 @@ border-bottom: 0; border-radius: var(--radius-l) var(--radius-l) 0 0; font-size: 13px; - animation: rise var(--t-slow) var(--ease-snap) both; } .text { flex: none; color: var(--light); } input { diff --git a/web/src/lib/store.svelte.ts b/web/src/lib/store.svelte.ts index 6c26c9f..84ca5ce 100644 --- a/web/src/lib/store.svelte.ts +++ b/web/src/lib/store.svelte.ts @@ -125,7 +125,7 @@ class Workspace { */ prompt = $state<{ text: string; resolve: (yes: boolean) => void } | null>(null) /** A password asked for in the same place, typed into a masked field. */ - secret = $state<{ text: string; resolve: (value: string | null) => void } | null>(null) + secret = $state.raw<{ text: string; resolve: (value: string | null) => void } | null>(null) /** The vault, as last heard from the server; null until asked. */ vault = $state(null) media = $state([]) @@ -217,13 +217,22 @@ class Workspace { askSecret(text: string): Promise { this.secret?.resolve(null) return new Promise((resolve) => { - this.secret = { + let answered = false + const asked = { text, - resolve: (value) => { - this.secret = null + resolve: (value: string | null) => { + if (answered) return + answered = true + // Put away a turn later, so a question that follows straight on — + // the second of two passwords — takes over the same box instead + // of a new one appearing under the fingers already typing it. + setTimeout(() => { + if (this.secret === asked) this.secret = null + }) resolve(value) }, } + this.secret = asked }) }