Skip to content

fix: stop the shell behaving like a browser#99

Merged
omartelo merged 1 commit into
mainfrom
fix/app-window-browser-defaults
Jul 25, 2026
Merged

fix: stop the shell behaving like a browser#99
omartelo merged 1 commit into
mainfrom
fix/app-window-browser-defaults

Conversation

@omartelo

@omartelo omartelo commented Jul 25, 2026

Copy link
Copy Markdown
Owner

The --app window is a browser only by construction, and its reflexes leaked
into every screen that is not a focused terminal — a terminal already swallows
them through xterm's own preventDefault, which is why they only ever misfired
elsewhere.

Two of them were fatal:

  • Ctrl+W closed the window, and a closed window quits lich.
  • Dropping a file on the window navigated to it, replacing the app with the
    file's contents, with no address bar to come back from. Easy to trigger by
    dragging a file at a terminal expecting to paste its path.

The rest were noise with real teeth:

Chord / gesture What leaked
Ctrl+T, Ctrl+N (+Shift) tabs and browser windows
Ctrl+P, Ctrl+S, Ctrl+O print/save/open dialogs; Open navigates away and kills the app
Ctrl+U, Ctrl+H, Ctrl+J view-source, history, downloads
Ctrl+Shift+Delete wipes lich's own lich.* localStorage settings with the browsing data
Ctrl+Shift+M profile switcher
Right-click on the UI Back, Reload, Save as, Print, View source
Middle-click a link browser window (onClick never sees an aux click)
Startup with a mismatched locale the translate bubble, offering to translate lich's own UI

What changed

  • internal/chromium: launch with --disable-features=Translate.
  • frontend/src/lib/browser-defaults.ts (new): the chord table, the context-menu
    rule and the drop guard, installed from main.tsx before React mounts.
  • Markdown.tsx: onAuxClick refuses the middle-click navigation the existing
    onClick handler could not see.

Swallowing is preventDefault and never stopPropagation: a terminal must
still receive every chord as its PTY sequence, since most of these are real
terminal bindings — Ctrl+W kills a word, Ctrl+U a line, Ctrl+D is end-of-file,
Ctrl+P walks shell history.

Kept on purpose

Reload (Ctrl+R / F5) recovers a wedged UI and costs nothing, since the session
lives in the backend; the devtools chords and F11 are worth more than the
purism; and the terminal keeps Chromium's right-click menu, which is where its
Copy and Paste live. Ctrl+F outside a terminal still opens Chromium's find bar —
searching the page is useful, and it is easy to add to the table later.

Test plan

  • gofmt -l ., go vet ./..., go test ./... — 400 pass
  • pnpm test — 416 pass, incl. the new browser-defaults table
  • pnpm build, pnpm check
  • Hand-checked in task dev. Chromium marks some accelerators
    reserved, and a reserved key cannot be cancelled by the page — Ctrl+W was
    the open question. It is not reserved in an --app window: the chord is
    refused and the window stays open.
  • A terminal still gets Ctrl+W / Ctrl+U / Ctrl+D / Ctrl+P as PTY sequences
  • Dragging a file onto the window leaves the app in place

The --app window is a browser only by construction, and its reflexes
leaked into every screen that is not a focused terminal — a terminal
already swallows them through xterm's own preventDefault, which is why
they only ever misfired elsewhere.

Two of them were fatal. Ctrl+W closed the window, and a closed window
quits lich. Dropping a file on the window navigated to it, replacing the
app with the file's contents, with no address bar to come back from.
The rest were noise with real teeth: Ctrl+T and Ctrl+N spawned tabs and
windows, Ctrl+P/S/O opened dialogs with nowhere to land, Ctrl+U, Ctrl+H
and Ctrl+J opened browser surfaces, Ctrl+Shift+Delete would have wiped
lich's own localStorage settings along with the browsing data, the
right-click menu offered Back, Reload, Save as and View source, a
middle-clicked link spawned a browser window, and a browser locale that
did not match the UI raised the translate bubble on startup.

Chromium's translate feature is now switched off at launch, and
lib/browser-defaults.ts refuses the rest before React mounts.
Swallowing is preventDefault and never stopPropagation: a terminal must
still receive every chord as its PTY sequence, since most of these are
real terminal bindings (Ctrl+W kills a word, Ctrl+U a line, Ctrl+D is
end-of-file, Ctrl+P walks shell history).

Kept on purpose: reload (it recovers a wedged UI and costs nothing — the
session lives in the backend), the devtools chords, F11, and the
terminal's own right-click menu, which is where its Copy and Paste live.
@omartelo
omartelo merged commit e625e11 into main Jul 25, 2026
3 checks passed
@omartelo
omartelo deleted the fix/app-window-browser-defaults branch July 25, 2026 23:30
@omartelo omartelo mentioned this pull request Jul 25, 2026
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant