Skip to content

Adding quite a few features to match with existing terminal emulator - #6

Merged
rigidlab merged 8 commits into
mainfrom
feat/terminal-parity
Aug 25, 2026
Merged

rigidlab merged 8 commits into
mainfrom
feat/terminal-parity

Conversation

@rigidlab

Copy link
Copy Markdown
Owner

No description provided.

Both pinned and vendored locally, like xterm.js itself, so the app stays offline-capable and the proposed APIs it relies on cannot change underneath it. Added to the installed-wheel smoke test as well: an asset excluded by the build backend only surfaces once someone installs it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
Qt maps Ctrl to Command on macOS, Meta to Control and Alt to Option. That does half the job and ruins the other half, so the table spells out both sides rather than leaning on it.

On Windows and Linux the shell owns Ctrl+letter, so the app takes Ctrl+Shift. On macOS the shell uses Control and Command is free, so the binding is a plain Cmd+letter - translating Ctrl+Shift+T would give Cmd+Shift+T, a different gesture entirely. Ctrl+Tab on macOS becomes Cmd+Tab, the OS application switcher, so next-tab is spelled Meta+Tab there. Ctrl+C on macOS is copy, while the interrupt is Meta+C and is deliberately left unbound.

conflicts() exists because a collision is invisible: two QShortcuts sharing a sequence makes Qt fire neither. A test calls it on both platforms rather than trusting the table to have been read carefully.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
… background

Everything a mainstream terminal has that qtxterm did not. Kept as one commit because the pieces share terminal.js, terminal_tabs.py and terminal_widget.py; splitting them further would mean hand-built intermediate states that no test run ever covered.

Find in the scrollback (Ctrl+Shift+F), in the page rather than a Qt widget above the view - a Qt bar would take rows off the grid and reflow the shell every time you searched. Match highlights are translucent because xterm draws decorations over the glyphs: reusing selectionBackground turned every match into a solid white block on VS Code Dark High Contrast.

Clickable URLs on Ctrl+click, opened in the system browser rather than a qtxterm browser tab - that is where the user's extensions and sessions live, and the URL is untrusted output. The scheme is re-checked in Python because the addon regex is not a security boundary.

Fixes the reported bug where arrow keys switched tabs instead of moving between panes: after a split, Qt left focus on the QTabBar, which handles Left/Right by switching tabs. Panes now take the keyboard on split, on new tab and on close. Alt+Arrow navigation ranks candidates by edge overlap, not centre distance - measured, the centres sat one pixel apart and the choice was a coin flip.

Also: copy/paste and font zoom shortcuts, tab-by-number, close-on-exit as three settings rather than a checkbox (a shell that died has usually printed why), and a background image that spans the tab so a split shows one continuous picture instead of one copy per pane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
USAGE.md gains a cross-platform keyboard section, the macOS config paths it was missing, and sections for find, links, close-on-exit and the background image. All 41 em dashes are gone, matching SPEC.md and README.md, which had already been converted.

test_usage_docs.py asserts that every shortcut the app binds appears in the guide, and that the guide survives QTextBrowser.setMarkdown - whose escaping rules are not GitHub's, so the file being correct is no evidence the dialog is. Written after the guide was found advertising chords that could not fire, and after a pipe character silently ate a table cell.

SPEC.md records Phase 5a, including the two failures that unit tests passed straight through and the Qt key-matching traps behind the split shortcuts.

It also corrects the long-standing Ctrl-C entry. That bug does not exist: Windows makes the ignore-Ctrl+C flag inheritable, so an app launched from an automation shell passes it down to every terminal child. Measured with the flag as the only variable - clearing it stops the child immediately. The Git Bash result previously cited as evidence was MSYS2 raising SIGINT itself and never consulting the console path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
Six failures on macOS, all of them bugs in the tests rather than the app. Two distinct causes.

Four docs failures: the guide is written in the names people see, and on macOS those are not Qt's. Qt calls the binding Ctrl+T where the keycap, the menus and the guide all say Cmd+T, so matching Qt's spelling against user-facing text failed while both the code and the guide were correct. Adds display_sequence(), which maps Ctrl to Cmd, Meta to Control and Alt to Option - in that order, so the Meta rule cannot rewrite a Ctrl it just produced. It substitutes the Mod+ form rather than the bare word, which keeps Ctrl++ (zoom in, Ctrl and the plus key) intact.

Two split failures: both tests wrote out the Windows chords by hand. Those punctuation spellings only exist on Windows and Linux - macOS splits on Cmd+D, where the ambiguity they guard against cannot arise. One test is now pinned to the non-mac table because that is what it is about; the other builds its cases from the table, so it covers whichever platform it runs on.

Reproduced locally first by overriding shortcuts.IS_MAC, and the whole suite now passes under both that simulation and real Windows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
No default table can be right everywhere, and the reason is not taste. A tiling window manager that owns Alt+Arrow, a desktop that has claimed a chord, a shell binding somebody depends on - none of these are visible from inside qtxterm, and all of them take the key before the app sees it. Rebinding makes the defaults a starting point rather than a verdict.

Chosen over the alternative of shipping a second default chord as a fallback, which would have been a speculative alias. A hedge - two spellings of one physical keypress, where only one can ever fire - is free. A true alias is two different gestures for one action, and each one permanently consumes a chord in a keyspace the shell already owns most of. Hedge freely, alias against evidence.

Only overrides are written to keybindings.json. Saving the resolved table would freeze today's defaults into every config file, so a later version that improved a binding would never reach anyone who had opened the editor once.

A conflict is refused rather than accepted last-wins, because two QShortcuts sharing a sequence makes Qt fire neither - so a silent overwrite would disable both actions. The editor names the action already holding the chord. Shortcuts are rebuilt rather than patched on change, disposing the previous set first: a left-behind QShortcut keeps firing and becomes ambiguous with its own replacement.

shortcuts.py stays a pure table; keybindings.py layers overrides on top; terminal_tabs resolves through the store when it has one and straight from the table when it does not, which is what tests and embedders get.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
USAGE.md gains a Changing a shortcut section: the editor, that an action can hold several chords or none, why a duplicate is refused, and the config path on all three platforms. It also names rebinding as the answer to a chord a window manager has swallowed.

SPEC.md records the hedge-versus-alias distinction that the question came from, the measurement behind it (26 actions resolving to 44 sequences on Windows/Linux against 29 on macOS, with the tab-number slots alone accounting for about half the aliasing), and why the speculative Ctrl+Shift+Arrow fallback was rejected in favour of this.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01M8UgLA68nRCYg3qFTyW8ms
@rigidlab
rigidlab merged commit bbaa3c4 into main Aug 25, 2026
12 checks passed
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