mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-22 08:02:24 +00:00
2ce2e696363fe131f9045a5dbf853627d02e1fa6
23
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3064ce8a68 |
fix(history): stop per-pane history from silently disabling history search
`<config>/history` was two different things at once. The window keeps the input bar's command store there as a *file* (`terminal::history`), and the daemon put each pane's `HISTFILE` in a *directory* of the same name. A path is one or the other, and the daemon created its directory the moment `per_pane_history` was switched on, so the daemon won. After that the window's `append` opened a directory, got an error, and — the call site being `if let Ok(mut f) = open(..)` — dropped the command line without a word. Up and Ctrl-R went on offering whatever had been recorded before the setting was turned on and never grew again. Nothing in the UI or the log said why, and the two halves are in different crates, so neither side had any reason to notice the other's name. Per-pane files move to `<config>/pane-history/`, which leaves the old name to the file that should have had it. An install that already tripped over this is carried across on the next start: the directory sitting at the old name is exactly what belongs at the new one, so it is renamed rather than abandoned — but only when it *is* a directory, since a plain file there is the window's store and moving that would take the very thing this repairs, and only when the new name is free, so a second call cannot bury a directory in use. Verified end to end against a daemon: with the setting on it now creates `pane-history/` 0700 with the pane files in it, and an append to `<config>/history` succeeds where it previously failed. Seeding an old-layout install with `history/pane-9` and starting the daemon leaves the line intact at the new path. Guards: one reads the window's source for the names it passes to `config_path` and fails if the daemon's directory is among them, so moving either side onto the other trips it; one covers the three ways the move can lose data (directory moves, file stays, occupied target is not overwritten). Both were checked against injected regressions. The privacy page and the integration test that read the path follow it. |
||
|
|
0d82891cd1 |
docs(privacy): say where per-pane history is kept
The page said shell history is "your shell's own file, exactly as before
— unless you turned on per-pane history, which merges back into it". The
merge is real, but it is not the whole account: with per-pane history on,
tty7 points each pane's `HISTFILE` at `<config>/history/pane-<n>`, and
what accumulates there is the command lines someone typed.
That is the most sensitive thing tty7 causes to be written anywhere, and
the page named neither the location nor the mode. It enumerates
`<config>/scrollback/*.bin` down to its 0600 and its retention rules;
this belongs on the same footing.
Verified rather than read off the source: with the setting on and the
server restarted to pick it up, a fresh pane reports
HISTFILE=<config>/history/pane-1
and the directory is created `0700`. The files inside are the shell's own
writing, under the user's umask, which the page now says.
The guard asserts the path appears, not the prose around it — the path is
the part a reader needs in order to go and look.
|
||
|
|
56b4769d9c |
docs(privacy): update.log is a file tty7 writes, so list it
The privacy page accounts for every file tty7 leaves in the config directory, and `crash.rs` has a test holding it to that. The test named two files. There are three: the updater appends `<config>/update.log` while it installs a release — what it verified, what it replaced, and why it stopped if it did. It is written whenever an update runs, not only under `TTY7_LOG`. That makes it exactly what the test's own comment describes as belonging on the page: "a file written without being asked for". It is also the only account of a swap that happens after the window is gone, which is why it exists. The guard now names all three, and was checked against the page as it was. Also corrects a filename I got wrong in the previous commit: the doc comment on `install_crash_log` called this file `tty7-updater.log`. There is no such file — I invented the name while describing what a silent panic costs, and it went in unchecked. It is `update.log`, and finding that is what turned up the missing page entry. |
||
|
|
36404a6cbb |
fix(keymap): Restart Server is bindable, like every other menu item
Forty-five items in the app menu dispatch an action. Forty-four of them can be given a key; `RestartDaemon` could not, because it was missing from `default_bindings` and `make_binding` — so config.json dropped the name silently and the Keybindings page never listed it. Nothing else was missing. The gpui action is declared, the handler is wired, the app menu dispatches it and the palette runs it. Only the two table entries that make a name bindable were absent. This corrects a claim I made when I moved it out of the palette's chord lookup: I said then that making it bindable "means a gpui action and a handler, which is a feature rather than a fix". That was wrong — both already existed, and the app menu had been dispatching the action the whole time. Finding it took comparing the menu against the keymap rather than reading either alone. No default chord, like the sixty-odd others that ship unbound. The palette gets its chord lookup back, so once a key is on it the row shows it — which is the thing that lookup was doing wrong before and is now simply right. The shortcuts-page guard added earlier this session caught the last step without being asked: it failed on `RestartDaemon` the moment the action became bindable, which is what it is for. |
||
|
|
eb211d7fc1 |
docs(shortcuts): the document dock's actions are bindable, so name them
The shortcuts page has two halves: a table of default chords, and a list of the rest — "more actions you can bind". An action with no chord of its own appears only in that second list, so leaving it out makes the action unfindable: no key to be discovered by, and no row to be read on. The document dock (#625) shipped with four such actions — `ToggleDocumentFill`, `DocumentWidthThird`, `DocumentWidthHalf`, `DocumentWidthTwoThirds` — all bindable, all chordless, and none of them on the page. The CHANGELOG names them as palette commands; the page a reader goes to for "what can I bind" did not. The neighbouring guard already holds the other direction: the page names no action that has been renamed away. This one asks whether the page is *complete*, which is the half a rename cannot break but a new feature can. Two spellings count as named, because the page uses both. The rebinding lists write identifiers, and compactly — `ResizePaneLeft/Right/Up/Down`, `SelectWorkspace1`…`SelectWorkspace9` — so a trailing direction or digit is part of a family rather than an entry of its own, the same convention the neighbouring test already keeps. The chord table writes labels, which is how `CopyText` is covered: `per_platform("", "ctrl-shift-c")` leaves it chordless on macOS while it is Ctrl+Shift+C elsewhere, so it earns a table row and is written "Copy" there. The first draft of the guard missed that and called it undocumented. Checked against the page as it was: the guard names all four. |
||
|
|
791d0d0cfa |
Merge remote-tracking branch 'origin/main' into polish/ralph-wc
# Conflicts: # README.md # README.zh-CN.md # crates/tty7-cli/src/cli.rs # crates/tty7-cli/src/server.rs # crates/tty7-core/src/core/config.rs # crates/tty7-core/src/core/git/status.rs # crates/tty7-core/src/daemon/install/wsl.rs # crates/tty7-core/src/daemon/protocol.rs # crates/tty7-core/src/daemon/spawn.rs # crates/tty7-core/src/daemon/ssh/mod.rs # src/terminal/completion.rs # src/terminal/remote.rs # src/ui/app.rs # src/ui/i18n/en.rs # src/ui/i18n/ja.rs # src/ui/i18n/zh.rs # src/ui/tree_sync.rs |
||
|
|
958d8b7442 |
feat(window): dock the code panel and the diff overlay beside the terminal (#625) (#685)
* feat(window): dock the code panel and the diff overlay beside the terminal (#625) Opening a file covered the workspace. The terminal underneath kept running and was neither visible nor typeable, so reading a file while an agent talked was a toggle loop: open it, close it to read the reply, open it again. The Files tree already docks; the two surfaces you go to *from* it did not. They dock now, as a flex sibling of the terminal column rather than a narrower overlay — that distinction is the feature. `set_grid_size` is driven by the terminal element's laid-out bounds, so a column takes width away from the grid and the PTY reflows into what is left; a card painted over half the workspace would have left the grid full width with half of it hidden. `overlay_top` stops ordering a pair and starts choosing between them: a column has one child, and two `flex_1` siblings would split it and fight. Fill mode keeps the old vector, the old opaque paint and the old platform hoist untouched, so nothing about today's overlay changes for anyone who picks it. - Half the terminal column by default; drag the divider, double-click it to cycle a third / half / two thirds, or use the palette commands. Two thirds deliberately runs past the half-window cap the side panels obey — only the terminal's floor binds it. - `DOCUMENT_MIN_W` joins the width budget: both side panels reserve it the way they already reserve each other, and the column is derived from the *live* sidebar and panel widths rather than their floors, so a panel someone dragged wider is width the terminal keeps. - A window too narrow to seat both fills for that frame. The fallback is derived at render time and never stored, so widening re-docks on the next frame with nothing to undo. - Fill or dock is per tab, on the header's context menu. Reading a long file over the whole window in one tab while an agent keeps half of another is the normal case, and one global switch made each of those flip the other. A tab that has not been told reads `document_layout` from the config, which is what a fresh tab starts as — and which the menu therefore does not write, since every untold tab is reading it. - Everywhere but macOS the title bar spans the workspace, which left a bar's height of nothing above the column. The header is drawn into it, and behaves like the title bar it now sits in. With the detail panel closed the column reaches the window's right edge, so the header stops short of the trailing chrome through a width the tab strip's own reservation shares. - The docked headers drop the traffic-light inset they never had to clear, and the diff header's branch name becomes the thing that yields so the view toggle and the close tile survive a column's width. New in `config.json`: `document_ratio`, and `document_layout` for what a fresh tab starts as. Four new actions, bindable and unbound by default. * fix(window): hold the docked column to widths the strip and the file agree on Three defects in the document column, each with a guard test that fails without its fix. The tab strip did not know a column had taken width off it. On macOS the strip lives inside the terminal column and sizes itself to the window less the detail panel, so a docked document left it 340 points wider than the column it sits in and the chips ran on under the column — the same overrun the panel's own reservation was added for. Everywhere else the strip spans the workspace and the column's hoisted header is drawn over its trailing end with no fill of its own, so a chip left under it showed through the file name and stayed clickable through it. The column's width now comes off `strip_w` on macOS and off `corner_w` elsewhere, which is where the panel's already goes. The divider wrote widths the file would not keep. `Config::sanitize` holds `document_ratio` to 0.2..=0.8; the drag clamped in pixels only, so a column pushed against either edge of a wide window was saved outside that band and reopened somewhere else — on a 2560-point body, 232 points from where it was dropped. The band is a pair of shared constants now and the drag clamps to it, the way the font size and its stepper were made to agree in #550. The palette named the config's layout rather than the tab's. Fill is per tab, so a tab told to fill was still offered "Document: Fill Window" — a row that named the state it was already in and did the opposite. It reads the active tab through `ChromeState` now. Also: `document_layout`'s doc comment still described the global switch an earlier draft had, three lines after the field became a per-tab default. |
||
|
|
7bcb91d8af |
fix(input): give the PTY back the Ctrl chords tty7 was eating (#684)
* fix(input): give the PTY back the Ctrl chords tty7 was eating Follow-up to #682, which handed Ctrl+V to a full-screen program but left three neighbouring holes of the same shape: a key the terminal answers without the keymap ever seeing it. The C0 table was half a table. `input.rs` mapped the alphabet, `[ \ ]` and Ctrl+2, and nothing else — so `Ctrl-^` (Ctrl+6, vim's alternate file), `Ctrl-_` (readline's undo, typed as Ctrl+/ or Ctrl+Shift+-) and Ctrl+3..8 produced no bytes at all. They were not mis-encoded, they were silent: gpui filters control characters out of `key_char` on all three backends, so the text fallback had nothing to offer either. The table is now the VT-220 one, each digit beside the punctuation that shares its key, because every platform hands Ctrl+Shift+6 over as `^` with the Shift already spent. Ctrl+/ is xterm's addition rather than VT-220's and is spelled out with the reason. The twenty-six letters fold to `& 0x1f`. `on_key_down` swallowed plain Ctrl+1..9 off macOS with a bare `return`, left over from when tabs lived on ctrl-digits — they have been on Alt+1..9 for a long time, so nothing claimed those chords and the block only deleted keys. It also sat before `keystroke_to_bytes`, so not even the kitty protocol got through it. Gone. Ctrl+V is now a binding. `AlternatePaste` carries `ctrl-v` off macOS in a `Terminal && !alt_screen` context, and the pane declares `alt_screen` whenever a full-screen program owns the grid, so the behaviour #682 settled on is unchanged — paste at a prompt, SYN inside vim — while the keymap can finally express it, the Keybindings page lists it, and the user gets a say: `"AlternatePaste": ""` hands Ctrl+V to the shell everywhere, including readline's `quoted-insert`, and `"PasteText": "ctrl-v"` pastes on every screen the way Windows Terminal does. That cohort is real — Warp keeps Ctrl+V pasting on Windows on purpose, as a removable binding, for exactly this reason. The hardcoded arm in `handle_cmd_shortcut` now answers Cmd+V alone, which is macOS's only paste chord and carries no control code to lose. Last, the rule about control codes is one function instead of an assertion buried in a test. `steals_a_control_code` plus a commented `control_code_binding_allowed` back both the defaults test and a new runtime warning, so a hand-edited config.json that takes EOF away from every shell says so in the log. It warns rather than refuses: a chord the user asked for by name is theirs to spend, the way the tmux preset spends Ctrl+B. The invariant that still fails a build is that no *default* spends one silently. Tests: `cargo test --bin tty7-app` 1360 passed, 1 known flake (`a_routed_auth_prompt_carries_the_machine_that_raised_it`, green on a rerun and on a clean tree). New: the whole VT-220 table asserted byte by byte, with Ctrl+- held out; `ctrl_6_reaches_the_pty_as_rs`, `ctrl_v_pastes_at_a_prompt` and `ctrl_v_reaches_a_full_screen_program_as_syn` drive the real keymap through `simulate_keystrokes` rather than calling into the view; the keymap tests cover both escape hatches and the context that withholds the binding. #682's two `handle_cmd_shortcut` tests are replaced by those three, which assert the same behaviour at the layer that now decides it; its end-to-end SYN test stands unchanged. The gpui tests are unix-only, so CI is what runs them. * fix(input): ask the grid, not the last frame, before Ctrl+V pastes `AlternatePaste` carries `Terminal && !alt_screen`, but gpui matches a keystroke against the frame it last painted, so the context outlives the switch: a full-screen program that took the screen after that paint is still "at a prompt" as far as the keymap is concerned, and the clipboard lands in it. In vim's normal mode that runs as commands. The action now re-reads the terminal mode and propagates instead, which hands the chord to `on_key_down` and encodes it as the SYN the program is waiting for. Also: - the two escape-hatch assertions in `paste_ships_both_terminal_chords_off_macos_and_retires_together` built a one-entry binding table instead of the default one, so both passed without the hatch working — an emptied `AlternatePaste` cannot dispatch anything when it is the only entry in the table. They now apply the config line on top of the whole default table, and the `PasteText: ctrl-v` case checks both screens; - the keyboard-shortcuts page claimed every other Ctrl chord reaches the program, which Ctrl+Tab and the Windows/Linux font-size chords do not; - `steals_a_control_code` documents `@` and the backtick, which are in the set it walks but were not in the list beside it. |
||
|
|
3c95995e82 |
fix(input): hand Ctrl+V to a full-screen program on the alternate screen (#677) (#682)
In vim or neovim on Windows and Linux, Ctrl+V pasted the clipboard where the editor expected blockwise Visual mode. Windows Terminal (with its ctrl+v binding removed), WezTerm and Alacritty all send the key; macOS was never affected, since Cmd+V is the paste chord there. Ctrl+V was not a keybinding at all. `on_key_down` hands plain Ctrl+C, V and X to `handle_cmd_shortcut` off macOS, and of the three the "v" arm was the only unconditional one: Ctrl+C copies with a selection and otherwise falls through to SIGINT, Ctrl+X falls through outside the editor, but Ctrl+V always consumed, so SYN never reached the PTY -- `input.rs` had the byte, unreachably -- and an empty clipboard turned the key into nothing at all. #270 set the rule that off macOS ctrl-<letter> belongs to the terminal and anything sitting on one must fall through; Ctrl+V was the exception that had escaped it. The arm is now contextual like its neighbours. On the alternate screen it falls through, and `keystroke_to_bytes` sends 0x16, or the CSI u form when the program has the kitty protocol on; off it Ctrl+V pastes exactly as before, and Cmd+V on macOS is untouched. The alternate screen is the gate rather than `input_active` because the editor is inactive whenever shell integration is missing or the prompt editor is off, and gating on that would take paste away from every such user; a program that has switched screens is precisely the case reported. Inside such a program paste is Ctrl+Shift+V, Shift+Insert or the right-click menu, all of which still stage a clipboard image for an agent. The same block did not exclude Shift, so Ctrl+Shift+C/V/X reached the hardcoded path whenever the keymap had nothing on them -- exactly the state rebinding Paste leaves behind, which #271 promised would retire Ctrl+Shift+V, but it went on pasting behind the user's back. Only unshifted chords enter the block now; the shifted ones are the keymap's alone. The right-click menu advertised Ctrl+C, Ctrl+X and Ctrl+V off macOS as though they were the bindings, next to a Select All row that already showed its hint on macOS only. The three rows take the same treatment, which is also what the command palette does. Three view tests pin the split -- Ctrl+V falls through on the alternate screen while Cmd+V still pastes there, Ctrl+V pastes off it, and a key down on the alternate screen arrives at the PTY as SYN and nothing else -- and the keymap's paste test now asserts that no default claims ctrl-v in the Terminal context. The shortcuts reference notes where plain Ctrl+V pastes and where it is the program's. Fixes #677. |
||
|
|
ef333bf055 |
feat(terminal): make the wheel-zoom modifier configurable (#676)
Cmd-scroll zoomed the font with no way to move it or switch it off, so a thumb left on Cmd resized the terminal mid-scroll (#668). The modifier is now a setting: the platform modifier by default, or Ctrl, Alt, or none. Stored as the choice rather than the resolved key, so one config file still means the same thing on a Mac and on a Linux box. Settings -> Terminal -> Mouse carries the picker; off macOS Ctrl and the platform modifier are the same key, so it shows one cell for them. |
||
|
|
89426fb5c4 |
fix(links): keep the path when a template token holds an absent value
`link_file_command` dropped a whole token whose value was missing. That is
right for `--line={line}`, where the flag means nothing without it, but the
same rule threw the file away in `code --goto {path}:{line}:{column}` —
which is VS Code's own spelling, and one of the three examples the docs
offer, `zed {path}:{line}` being another. Clicking a link that carried no
line number ran `code --goto` with no file and opened nothing, silently.
A token that has already produced the path now keeps it and stops there,
taking the separator that introduced the absent value with it so the
argument ends at the path rather than at a bare `:`. Tokens with no path in
them still go entirely, so the documented flag behaviour is unchanged.
This edge was noted in a test that asserted it as a "sharp edge" rather
than fixing it; that test now asserts the file opens.
|
||
|
|
eaca1e50d8 |
fix(keymap): refuse a tmux prefix that cannot carry a sequence
Every preset binding is built as `<prefix> <key>`, and nothing checked the prefix. `"prefix": ""` in a hand-edited config therefore produced no sequence at all — it bound bare `c`, `x`, `z`, `n`, `o` and the digits directly onto NewTab, CloseActiveTab, ToggleMaximizePane and the rest, so typing an `x` in the terminal closed the tab. `"c"` and `"shift-c"` are the same trap one step removed: the letter starts a sequence and swallows the next keystroke, and shift-c is simply how a capital C is typed. `preset_prefix` now requires one chord carrying a non-shift modifier and falls back to the default otherwise, naming the refused prefix in the log. That also catches `"C-a"` — tmux's own spelling, which gpui does not parse — which previously slipped through to `action_bindings` and dismantled the preset one binding at a time, warning about each key rather than about the prefix that caused it. The GUI only ever offered Ctrl-B and Ctrl-A, so this is reachable through config.json, which the docs describe. Those docs also promised a **Prefix** field to type into; it is a two-option row, and now says so. |
||
|
|
5fc97d5d06 |
docs(shell): put nushell on the page that lists integrated shells
The "Which shells" table named zsh, bash, fish and PowerShell. Nushell has had the same treatment as the rest for as long as they have -- a throwaway `config.nu` passed with `--config`, sourcing the user's own back in -- and a nushell user reading that page concluded they got nothing. The row says how, including the part that makes it unlike the others: `source` is parse-time in Nushell, so the path to your config is resolved as the wrapper is written rather than checked when it runs. The test reads the shells out of the injection dispatch, so a sixth has to be written down before it passes; removing the new row fails it, naming Nushell. Found by checking a table against the thing it claims to describe, which also cleared two nearby ones: `PATH_PROBED_SHELLS` holds twelve shells against these five, and that difference is right -- probing PATH so a shell can be chosen is not the same as having hooks for it, and ksh or tcsh work fine without them. |
||
|
|
9006e3ea17 |
docs: put the two files nobody opted into on the privacy page
"What is stored, and where" listed settings, keychain entries, scrollback
and shell history, and omitted both files that carry incidental personal
data:
crash.log written whenever tty7 panics — the hook is installed
unconditionally, so nobody opts in. Time, version, panic
message, backtrace. Capped at 256 KiB, never uploaded.
tty7.log only while TTY7_LOG or RUST_LOG is set, and genuinely absent
otherwise. At debug it carries the directories and workspace
names in each request.
A page that enumerates storage and leaves out the two files most likely
to end up attached to a bug report is answering the wrong question, so
both rows say what is in them and what to check before sending them on.
Noticed while reading a debug log for something else: a dependency had
written this machine's hostname and working directory into it.
"What leaves your machine" needed no change — nothing sends either file
anywhere, which is why each row says so.
The test lives next to the panic hook, where a change to what gets
written is a change someone is already making.
|
||
|
|
07790f589d |
docs(config): say that agent_commands needs the server restarted
Checking whether any setting is written but never read turned up one
field named nowhere outside `config.rs` — `agent_commands`. It is read,
through `agent_commands_cached`, which is the point: that is the only
setting behind a `OnceLock`, so a running server keeps the map it built
at startup.
Every other key here is picked up by a reload. This one is not, and
nothing about editing the file says so: a user adding `{"cc": "claude"}`
sees the wrapper go on being unrecognised and has no reason to suspect
the server rather than the spelling.
The cache is right — the map is read on every pane spawn, and the
alternative is `Config::load()` off disk each time a pane starts. So the
row says `tty7 server restart` and the function says why it costs that,
each pointing at the other.
All 77 fields are read; this was the only one worth a word.
|
||
|
|
99994553ef |
fix(config): hold side-panel widths to the widths they can be drawn at
#550 settled the rule: one range, defined where the value is validated, so a config-legal number cannot be turned around by a widget's narrower clamp. `sanitize_clamps_to_the_same_bounds_the_gui_steps_within` states it and pins the font pair. The two side panels broke it. `sanitize` clamped both widths to 100–2000, and `docs/reference/configuration.mdx` published that range, while the sidebar floored itself at 180 and the right panel at 216. So a documented `sidebar_width: 120` was accepted by sanitize, kept in the file, and drawn at 180 — the file said one thing and the window showed another, with nothing to explain the difference. The floors move to `core::config` beside the font bounds, and the two widget constants are defined from them, which is the direction that cannot drift: the widget cannot be narrowed without moving the floor the file is validated against. Docs updated to the real numbers. The *ceiling* is deliberately not shared. Both panels also cap against the viewport, but a panel wider than its window is a different question from a panel wider than the setting allows, and only the second belongs in `sanitize`. |
||
|
|
422808191d |
feat(sidebar): group a tab by its folder when its cwd is not a repo (#631)
`sidebar_grouping` gains a third, opt-in mode, `repo-or-directory`: group by repository home as before, and when the repo probe has landed and answered "not a repo", group under the cwd itself instead of filing every such tab under Scratch. A probe that has not run yet resolves to no decision, so a tab keeps the group it already has rather than bouncing through Scratch mid-probe. The decision lives in one `resolved_group` free function shared by the per-frame key derivation and spawn-time seeding. The default (`repo`) and flat modes behave exactly as before, and an unknown value in an existing config still degrades to `repo`. Knock-on: `machine_mirror::subject_path_of` names a window after its most common group, so in the new mode a window of plain shells takes its name from the most common directory rather than from the first pane's cwd. Closes #620. |
||
|
|
0346e35b40 |
fix(shell): stop injecting into a zsh or fish the user gave arguments to (#629)
The zsh and fish arms of `shell_integration::setup` never checked `has_custom_args`, so a shell the user launched with their own arguments was injected anyway — fish had `-C <script>` appended to its argv, zsh had its ZDOTDIR swapped. Both arms now sit behind the same gate bash, PowerShell and WSL already used, hoisted to a single early return ahead of the dispatch so a new ShellKind cannot silently reintroduce the bug.
Docs now describe what the code does: the `shell` row's own `{"program": "fish", "args": ["-l"]}` example loses integration under this rule, and the shell-integration note distinguishes user-written arguments from the ones detection supplies (Git Bash, WSL).
Part of #624; the native-input-mode half is separate.
|
||
|
|
72db26d15a |
feat(prompt): let the shell's own line editor own the prompt (#633)
Closes #624 tty7's inline editor takes the prompt the moment OSC 133 reports one, and until now the only way to keep it off was to hide the shell's own name from tty7 so integration never armed — which costs the prompt boundaries, cwd and exit codes as well. Someone who binds `history-beginning-search- backward-end` to Up in their zshrc had no way to reach it, and the local history the editor walks instead is per-view: a command run in one pane is not in another's list, so the shell's shared history looked broken too. The new `prompt_editor` switch (Settings -> Input -> Prompt, on by default) hands the line back. Off, every key at the prompt goes to the PTY, so ZLE / readline / fish do the editing and what the user bound behaves as written. Shell integration is untouched by it. The gate is one line in `input_inactive_reason`, which every path that could take the prompt from the shell already asks: keys, IME commits, paste, Tab, the completion and reverse-search menus, the input bar. That is what makes this a mode rather than a special case per key. `shell_owns_prompt` learns the flag too, and that half matters more than it looks: the gap hold and the typeahead record both exist to feed the local editor, and `flush_typeahead` sends ^U to erase the line before moving it there — on a line only ZLE is editing, that erases the user's work. Ctrl-R landing on the PTY also stops raising the missing-integration notice: the shell owning it is what was asked for. Turning it off mid-line hands what is typed to the shell the way an unknown chord does, so the text is still on the prompt to finish. Live panes follow the switch, including a hand edit of config.json in another window. Tab completion and history search are menus tty7 opens inside that editor, so the page greys them out and says why while it is off. Only their text dims — a switch already draws its thumb at 35% when disabled, and dimming the row on top of that leaves a pill with nothing visible in it. Their stored values are left alone and come back with the editor. Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> |
||
|
|
d343fd8a13 |
feat(links): open file links in tty7, resolved on the pane's own host (#568)
* feat(links): open file links in tty7, resolved on the pane's own host A clicked file path now opens in the built-in editor at the line and column the link named, and the Files panel reveals it; a directory link opens the panel on that directory. Settings -> Terminal -> Links -> Open files with picks between the built-in editor, the OS file association and a command, migrating anyone who had already set link_file_command. Detection is split into a filesystem-free candidate parser and a probe callback, so a pane whose paths live on another machine resolves them there instead of against the local filesystem -- an absolute path used to open this machine's copy silently. A pane running ssh typed into a local shell can answer for neither side and no longer offers file links at all. Relative paths are measured from the directory the work is happening in (the agent's, not the shell's kernel cwd) and then from the repository around it, and a path that matches nothing under either now says so instead of the click doing nothing. * fix(links): keep a remote path off the local openers, and off a dead end Review follow-ups on the file-link work. - A file resolved on another machine now opens in the built-in editor whatever `link_file_open` says. Under `system` or `command` the path was handed to a local `open` / `code --goto`, which threw away the resolution just done on the pane's host and silently showed this machine's copy — the same bug this branch set out to fix, left live for two of the three modes. A directory outside every tree root says so instead of opening a local file manager on a path that belongs to the far side. - `flush_link_probes` takes the host before it takes the wanted paths. `take_wanted` moves them into the in-flight set on the promise that a call is carrying them; a host that had gone away broke that promise for good and left those paths permanently unanswered — no underline, and a click that says nothing. - `~` no longer borrows this machine's `$HOME` for a pane whose paths are elsewhere. A cwd outside `/home` and `/Users` used to fall back to it, so `~/.zshrc` on a Linux box became `/Users/me/.zshrc` and was asked about — and possibly answered — over there. - An unresolved absolute or `~`-rooted path no longer claims it was looked for under the pane's directory. It never was: roots are only for relative paths. - A pending tree reveal counts down whether or not its row was found. A row that never reported bounds kept the request alive for good, re-issuing a scroll on every render and holding the column against a hand scroll. - The repo root comes from `GitStatusCache` when the git-status probe has already asked about that directory, rather than a second round trip. Tests: the migration `link_file_open` exists for (an old config with a command lands on Command, one without on the editor), a probe with no host staying wanted, and `~` refusing this machine's home for another one. * test(links): only claim a leading slash is absolute where it is `is_rooted` asks `Path::is_absolute`, the same question `FileCandidate::paths` asks before it decides the roots do not apply — and on Windows `/etc/hosts` answers no to both. The predicate is consistent; the assertion was not, so it now lives in a unix-gated test of its own next to the untouched one. --------- Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> |
||
|
|
4da3868797 |
feat(shells): let the new-tab menu carry entries the user wrote (#534)
Closes #443 |
||
|
|
00e1aa8218 |
docs: correct claims that no longer match the code
Audited every page under docs/ against the source. Fixes for what the code actually does: - agents: the status vocabulary is idle/working/waiting/done, not running/waiting/idle; hook rows grow a separate Uninstall button; the Settings table labels read "Copilot CLI" and "Grok Build"; Copy Session ID lives in the tab's context menu, not the pane's - cli: `pane ls --all` reports the owning workspace id, not "tty7-cli"; document bare `tty7 [PATH]` as the GUI launcher it is instead of listing it as unimplemented; note `active_tab` and the `diagnostics` array; wait also defaults to $TTY7_PANE - git: the branch dropdown is a plain list with no search box and no stash-and-switch, and checkout is not a palette command; quote the diff overlay's own overflow notice rather than the sidebar's - window: the unread marker tracks a finished agent turn, not any output; rows cannot be dragged across groups; the sidebar and `tty7 tab ls` resolve labels differently; drop Toggle Commit History and Checkout to from the palette's Git group; ~/.ssh/config aliases are not palette entries - terminal: Ctrl+R dedups by command text and shows no directory; Esc does not dismiss a ghost suggestion; document Cmd+Enter - remote: GSSAPI is an ordinary Auth choice, not a managed-connection-only mechanism - fonts: Maple Mono NF CN leads the chain on Windows and Linux only; list the real per-platform defaults - settings paths: the three Links settings and per-pane history were filed under the wrong sections |
||
|
|
707fd1867b |
docs: add a Mintlify documentation site (#478)
38 pages under docs/, written against the source rather than the README: config keys and their clamps from core::config, default keybindings from ui::keymap, every CLI verb and flag from tty7-cli, agent aliases and hook/fork/resume support from core::cli_agent, and Settings paths taken from the actual en-US strings. docs/features.md and its zh-CN translation are retired — everything in them now lives in a page of its own, plus the two things they carried that nothing else did (IME input, the performance notes). README and README.zh-CN point at docs/ instead. Screenshots and videos are placeholders for now: docs/images/placeholder.svg with a caption naming what each shot should be. Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> |