commit 04bfb55d386b04013e063f2d9f2e76ef417f8c73
parent 49fc72b24e0abea8f6dfd1c8756b118150562963
Author: Damian Myrda <damian@prime8.dev>
Date: Fri, 23 Oct 2026 07:24:19 +0000
web shell: stop auto-grabbing focus; empty prompt hands digits/tab to tab switching
Diffstat:
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/internal/web/scripts/modules/shell.js b/internal/web/scripts/modules/shell.js
@@ -688,6 +688,18 @@ export function initShell() {
}
}
+ // An empty prompt doesn't own the page shortcuts: digits and tab give
+ // focus back and switch tabs, so the terminal never eats navigation.
+ // Once something is typed they're plain input and completion again.
+ if (input.value === "" && !busy && !e.ctrlKey && !e.metaKey && !e.altKey &&
+ (/^[1-9]$/.test(e.key) || e.key === "Tab")) {
+ e.preventDefault();
+ e.stopPropagation();
+ input.blur();
+ document.dispatchEvent(new KeyboardEvent("keydown", { key: e.key, shiftKey: e.shiftKey, bubbles: true, cancelable: true }));
+ return;
+ }
+
switch (e.key) {
case "Enter": {
e.preventDefault();
@@ -777,10 +789,10 @@ export function initShell() {
if (!load(LOGIN_KEY)) store(LOGIN_KEY, String(Date.now()));
render();
- // 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.
+ // The prompt takes focus only when asked: typing a letter (above) or a
+ // click inside the terminal. It is never grabbed on load, on a tab switch
+ // or on window focus, so the page shortcuts keep the keyboard by default.
+ // 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;
@@ -796,16 +808,13 @@ export function initShell() {
}
shellHandlers.click = (e) => {
if (!section.isConnected || e.button !== 0) return;
+ if (!section.contains(e.target)) 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;
+ shellHandlers.winFocus = null;
+ shellHandlers.revealed = null;
document.addEventListener("click", shellHandlers.click);
- window.addEventListener("focus", shellHandlers.winFocus);
- document.addEventListener("reveal:done", shellHandlers.revealed);
-
- focusPrompt();
}