prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit 3527c57c6cc2944fa42c4a67f46552eeb33ccd14
parent 1249b550ed17baa980be3378ca9025e1f5886deb
Author: Damian Myrda <damian@prime8.dev>
Date:   Thu, 22 Oct 2026 00:37:33 +0000

web: keep the prompt focused across tab reveals, window focus and clicks

The line reveal hides lines with visibility:hidden, which blurs the
focused input, so focusing before it ran left a hollow cursor.

Diffstat:
Minternal/web/scripts/modules/reveal.js | 2++
Minternal/web/scripts/modules/shell.js | 40+++++++++++++++++++++++++++++++---------
2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/internal/web/scripts/modules/reveal.js b/internal/web/scripts/modules/reveal.js @@ -37,6 +37,8 @@ function cleanup(root) { el.classList.remove("line-in"); el.style.removeProperty("--line"); } + // hidden lines drop focus, so anything that wants it can take it back now + document.dispatchEvent(new CustomEvent("reveal:done")); } function onScreen(el) { diff --git a/internal/web/scripts/modules/shell.js b/internal/web/scripts/modules/shell.js @@ -18,7 +18,7 @@ const LOGIN_KEY = "prime8.lastlogin.v1"; const reducedMotion = () => window.matchMedia("(prefers-reduced-motion: reduce)").matches; let fsPromise = null; -const shellHandlers = { keydown: null, storage: null }; +const shellHandlers = { keydown: null, storage: null, click: null, winFocus: null, revealed: null }; function loadFS() { if (!fsPromise) { @@ -777,13 +777,35 @@ export function initShell() { if (!load(LOGIN_KEY)) store(LOGIN_KEY, String(Date.now())); render(); - // A terminal opens focused, on load and on every switch back to about. Skip touch screens, where focus pops the - // on-screen keyboard, and never steal focus from something else. - const active = document.activeElement; - // A clicked tab keeps focus after the swap; coming back to about counts as idle. - const idle = !active || active === document.body || active === document.documentElement || - !!active.closest?.("#tabs"); - if (!live.hidden && idle && window.matchMedia("(pointer: fine)").matches) { - input.focus({ preventScroll: true }); + // A terminal is focused whenever nothing else wants the keyboard: on load, + // on every switch back to about, when the window regains focus, and on a + // click anywhere that isn't a link, a control or a text selection. Touch + // screens are skipped, since focus pops the on-screen keyboard. + const finePointer = window.matchMedia("(pointer: fine)").matches; + const focusPrompt = () => { + if (!section.isConnected || live.hidden || !finePointer) return; + const active = document.activeElement; + // a clicked tab keeps focus after the swap; that still counts as idle + const idle = !active || active === document.body || active === document.documentElement || + !!active.closest?.("#tabs"); + if (idle && document.visibilityState === "visible") input.focus({ preventScroll: true }); + }; + + for (const [ev, key, target] of [["click", "click", document], ["focus", "winFocus", window], ["reveal:done", "revealed", document]]) { + if (shellHandlers[key]) target.removeEventListener(ev, shellHandlers[key]); } + shellHandlers.click = (e) => { + if (!section.isConnected || e.button !== 0) return; + if (e.target.closest?.("a, button, input, textarea, select, label, summary, [tabindex], #help-overlay")) return; + if (String(window.getSelection?.() || "")) return; + if (window.isLightboxOpen?.()) return; + focusPrompt(); + }; + shellHandlers.winFocus = () => setTimeout(focusPrompt, 0); + shellHandlers.revealed = focusPrompt; + document.addEventListener("click", shellHandlers.click); + window.addEventListener("focus", shellHandlers.winFocus); + document.addEventListener("reveal:done", shellHandlers.revealed); + + focusPrompt(); }