mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-10-03 08:02:02 +00:00
6f29cac8bf7ea7e790b50415e2d38aa37cd2e57e
24
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b063ba97a0 |
refactor(settings): fold Window & Tabs into General, drop two settings (#982)
The Window & Tabs page is gone. Its three remaining tab settings (new tab position, tab bar position, auto grouping) are a Tabs group on General, below Startup & restore, so the nav has seven sections. Removed outright: - SSH tab title (`ssh_tab_title`, #726, unreleased). The sidebar already groups SSH tabs under their host, and renaming a tab pins its name. - Open diff preview from sidebar counts (`sidebar_diff_preview`, #247). The sidebar counts and the Info panel's changes row always open the diff overlay; the large-tree stall it worked around is bounded. An old config.json that still carries either key loads as before. The now-unused window settings icon goes with the page. |
||
|
|
84935813d5 |
feat(terminal): the mouse wheel no longer zooms the font by default
mouse_zoom_modifier now defaults to none. The platform modifier is cmd on macOS, held for so much else that the font jumped size mid-scroll (#668). Picking a modifier in Settings brings the wheel zoom back; cmd+/cmd- are unchanged. |
||
|
|
6fcf659e04 |
feat(search): tabbed Search Everywhere with a Sessions tab to resume agent sessions (#969)
* feat(search): replace the command palette with tabbed Search Everywhere The palette was one flat list that every new kind of row had to be squeezed into: tabs and SSH hosts rode along as "Switch to Tab: …" and "SSH: …" commands, and the only way to narrow to one kind was a magic seed word. Search Everywhere splits it into sources behind one trait — All, Actions, Terminals, Hosts — each with its own empty-query layout and ranking. The All tab shows each source's top rows, ordered by best match, with a row that opens the full tab. Tab / Shift-Tab walk the tabs and keep the query. - Terminals lists every open tab of every workspace (reusing the switcher's tab rows) and jumps to it wherever it lives, plus the shells and agents. - Hosts replaces the separate "Add Connection" input: a typed address or full `ssh …` line offers to connect. - Fixes Return doing nothing after a search that found nothing, or when the search opens pre-filtered: gpui-component re-picks the row from a stale frame; the delegate now re-arms the first row. - Fixes `ssh -p 2222 me@box` being offered as a quick-connect address with user `ssh -p 2222 me`. The keymap action stays `TogglePalette` so custom bindings keep working. * feat(search): a Sessions tab to resume past agent sessions Search Everywhere gains a Sessions tab listing the Claude Code and Codex sessions on this computer, read from ~/.claude/projects and ~/.codex/sessions ($CODEX_HOME). Sessions that ran in the focused tab's directory lead; the All tab offers the last three of them before anything is typed. Return opens a new tab in the session's directory and runs the agent's resume command with its configured launch flags. - Only each transcript's head and tail are read, off the window thread, and cached by path, size and mtime: ~170ms cold for 50 sessions, under 1ms warm. The search opens on the cached list and fills in. - Titles: /rename name, then the agent's own title (ai-title, Codex's session_index.jsonl), then the first thing typed, skipping harness injections. Codex rollouts it ran for itself (subagent/internal) and sessions never asked anything are left out. - A session whose directory is gone is refused with a notice rather than resumed where the agent cannot find it. |
||
|
|
39ceb35fdd |
feat(cursor): give the shell prompt its own cursor shape (#963)
A new prompt_cursor_style setting (follow, block, bar, underline) shapes the caret while the shell waits at a prompt, whether tty7's inline editor or the shell's own line editor draws it. `follow`, the default, keeps cursor_style everywhere, so nothing changes until it is set. Any other value leaves cursor_style to the programs the shell runs: bar here with cursor_style block gives kitty and ghostty's bar at the prompt and a block inside a TUI that never sets a shape, such as Claude Code. A vi-mode prompt keeps the shell's own insert/normal shapes. Settings shows both cursor shapes as dropdowns, kept in step with resets, language switches and config edits made outside the window. Closes #958 |
||
|
|
cf0e8f02e6 |
feat(sidebar): pinned groups above auto groups (#962)
* feat(sidebar): keep pinned groups on the workspace and derive the rest Replace the sidebar's hand-made groups with the model from #955: the sidebar groups tabs by repo automatically, and you pin what you want to keep. - Pinned groups (`PinnedGroup`: id, optional name, optional folder, fold) are stored on the workspace in the machine tree, in display order, and a tab points at one by `GroupId`. Everything else is an auto group worked out every frame and never stored: by repo home, or by `user@host` for an SSH pane — native or a shell that ssh'd onward — so `/home/ubuntu` on two machines no longer lands under one header. - A tab whose cwd *enters* a pinned folder joins it (deepest folder wins; a repo home equal to the folder counts, which keeps worktrees with their repo). It is edge-triggered through `EntryWatch`, so a tab dragged out while still inside the folder stays out until it leaves and comes back, and a tab restored at launch is not pulled in by where it already sits. - Groups sync as one `WorkspaceSetGroups` / `GroupsChanged`, pushed only from an edit and adopted from every pull, so a fresh window can never push an empty set over the workspace's. The machine hands tabs naming a dropped group back to auto grouping in the same mutation. Control dialect → v11. - Config: `sidebar_grouping` (three modes) and `sidebar_collapsed_groups` give way to one `sidebar_auto_grouping` toggle; folds live with the workspace. - `tty7 tab ls` reports the pinned group a tab is in (name or folder leaf; JSON carries id, name and folder). * feat(sidebar): draw pinned groups above a divider, with their own gestures The sidebar now reads as two halves: the groups you keep, in the order you put them, then a divider, then the groups it works out (Arc-style). - Pinned headers drag-reorder among themselves (their own reorder surface, so a pinned header cannot be dropped among the derived ones); the order lands on the workspace's group list, not on the tabs. - An auto header carried above the divider is pinned when let go. With nothing pinned yet the divider appears during that drag as a "Drop here to pin" zone, since a hairline at the top of the list is nothing to aim at. - A tab kept in a pinned group and dropped anywhere below the divider goes back to auto grouping; the divider lights to say so. - An empty pinned group stays, with a "+ New Tab" row that opens a tab in its folder (or where ⌘T would, for a label group) and files it there. - Folder groups carry a pin mark that unpins on click and a tooltip with the folder; auto headers show pin and "+" on hover. - Header menus: pinned — Rename, Set Folder… (local workspaces), Use Current Tab's Folder, Clear Folder, New Tab, Unpin (folder groups), Delete. Auto — Pin Group, New Tab. Nothing renames an auto group; nothing pins implicitly. * feat(sidebar): open folders as pinned groups from Finder, the file tree and the palette Every way into a pinned group the design calls for: - Drop a folder from Finder or Explorer onto the sidebar to pin it (a local workspace only — a dropped path is this machine's, and a folder group keeps a directory on the workspace's host). Files are let fall. - "Pin as Group" on a folder in the file tree, on local and remote workspaces alike, since the tree and the group are both on the workspace's host. - Palette "New Group" makes an empty label group and opens its name for typing; "Open Folder as Group…" picks a folder with the system picker, pins it and opens a tab in it. The picker browses this computer, so that one is not offered on a remote workspace. - Tab right-click "Move to Group" lists the pinned groups plus "New Group…", which files the tab in a fresh label group with its name open for typing. Pinning a folder already pinned hands back the group that keeps it rather than making a second one to split its tabs with. * fix(sidebar): let groups that arrive from elsewhere pull no tab into a folder A window draws its first frames before its copy of the workspace's groups lands, so every tab's entry watch recorded "in no folder" — and the groups landing then read as each tab walking into its folder. A restored tab, or one dragged out of its folder group, was pulled back in on every launch. Groups adopted from a pull or from another window's `GroupsChanged` now start every tab's watch over from where it is; only this window's own pin gathers the tabs inside the folder, and says so tab by tab. A tab also goes up with the group it names even when the window does not know that group yet, so a sync in that same gap cannot send every kept tab back to auto grouping. * docs(sidebar): describe pinned and auto groups, and log the change Rewrite the sidebar page's grouping section around "grouped by repo automatically; pin what you want to keep": the divider, folder and label groups, the edge-triggered join, every way to pin, and the header menus. The configuration reference swaps `sidebar_grouping` for `sidebar_auto_grouping`, the CLI reference describes the GROUP column as the pinned group, and the changelog gains an Unreleased entry (#955). * fix(sidebar): file a tab opened by the CLI in a pinned folder into it A tab that reaches a window as TabCreated — from `tty7 tab new` or another window — started its entry watch as a restored tab, so opening one inside a pinned folder left it in the auto group below. It is as new as a tab opened here, and now joins the folder like one; every window that hears of it reaches the same answer. * test(machine): build the group sets in their initializers Clippy's field_reassign_with_default on the two WorkspaceGroups the set-groups test assembles. * fix(sidebar): draw restored tabs in their auto group, and title by repo again Auto groups are not stored, so after a restart every tab sat in Ungrouped until its own repo probe came back, then jumped; before pinned groups the stored repo key put it in place on the first frame. Each tab now carries `last_auto`, the auto group it last resolved to, as a hint: stored with the tab, sent up alongside its group in `TabSetGroup` whenever the live answer moves, and used to draw the tab until the probe answers. The probe always wins and rewrites the hint, and the hint never outranks a pinned group or the folder-entry rule. Another window's hint only fills a gap, so two windows can never bounce a disagreement between them. The workspace's fallback title regained the repo majority it lost: the most common pinned folder first, then the repo most unpinned tabs were last filed under (a worktree counting toward its repo home), then a pane's cwd. * refactor: drop what the new sidebar left unused, and two clippy findings - `TerminalView::native_ssh_cwd` and its helper existed for the sidebar's old folder grouping of native SSH panes; an SSH tab now groups by host, and nothing else read it. - The file tree's context menu takes `cx` instead of `danger` and the new groups flag, back to the argument count it had on main. - A title test builds its workspace in the initializer. |
||
|
|
afcb8aa2dd |
feat(agents): quick launch for detected CLI agents (#961)
* feat(agents): quick launch for detected CLI agents (#955) Every agent whose launch program is on PATH becomes a palette command, "Agent: <name>", ordered by frecency and bindable as LaunchAgent:<slug>. "New Agent Tab" (Cmd+Shift+A on macOS; unbound elsewhere, where Ctrl+Shift+A is select-all) launches the most recently used one, and the New Tab menu gains a single "Launch Agent..." row that opens the palette pre-filtered to them. A launch always opens a new pane (a tab in the active tab's cwd, or a split when picked from the palette with Alt held) and types the command into that pane's shell once it exists - immediately for a local pane, on landing for a remote one via PendingSpawn::run_on_land - never into a pane that was already there. Detection, status and resume then work as for a hand-typed agent. The command is the agent's bare binary unless the new `agent_launch` config map overrides it. A wrapper named there is detected as its agent without an `agent_commands` entry: the daemon folds the programs `agent_launch` runs into its alias map (interpreters, shells and real agent names excepted), reloaded when config.json changes instead of once per process, and a mapped script run under its interpreter (`bash ~/bin/cc`, `node cc.js`) is now recognised too. A running agent's pane menu gets "Set Current Launch Args as Default", which writes its launch argv - minus session flags and positional prompts, joined with the settings' quoting - into `agent_launch`. Remote workspaces cannot be asked for their PATH through the Host trait, so they offer the agents previously seen running in that workspace (WindowView::seen_agents). * fix(agents): count only agents that start under a view, not ones reattached to A view rebuilt over a running pane - every agent tab after an app restart, or a workspace switched back to - saw its agent appear from nothing and reported it as detected, bumping that agent's frecency once per launch of the app. The terminal now remembers whether its link was an attach, and such a view only reports agents once its shell has been seen back at the prompt with no agent in front. Also pins down that the agents seen in a remote workspace, its quick launch list, persist in views.json with the rest of the workspace. * fix(daemon): probe the foreground as soon as a new program takes it The foreground probes ran on output only, at most once per 500ms interval. A quick launch types the agent's command the moment the shell is up, so the agent drew its whole first screen inside the interval of the prompt it was typed at and then waited for a key: the pane never learned it was running an agent until something else printed. Seen in a dev instance, where a launched Claude Code stayed undetected at its trust prompt. The reader now asks the pty for its foreground process group on every read (one ioctl) and probes immediately when it changes. |
||
|
|
27483e893d |
feat(ui): collapse the New Tab menu's shell list to three by frecency (#960)
The + menu listed every shell the machine reports (nine on a stock macOS box)
above the SSH hosts. The Local section now names the default shell, always
first, then only shells that have actually been opened, by frecency, three rows
at most, the same way the SSH section caps its hosts.
Shell usage is recorded in a new `shell_frecency` config map, keyed by the
inventory label, bumped from both the menu row and the palette. Every shell is
now a palette command titled "Shell: {label}" (same word in every locale), and
an "Other Shells…" row opens the palette pre-filtered to them. That row is
hidden when the menu already names the whole inventory. Running a shell command
from the palette respects the ⌥/Alt split modifier like the menu row.
|
||
|
|
de15f9b0ab |
feat(ssh): keep saved hosts in servers.json and add an SSH tab title setting (#952)
Saved SSH hosts and their usage counts move out of config.json into servers.json beside it, so config.json can be synced between machines without carrying a server list (#911). An older config.json is split on first load: servers.json is written first (0600), then only the two keys are removed from config.json, leaving every other key as it was. When both files hold hosts, servers.json wins and the stale copy falls out of config.json at its next save. A servers.json that does not parse is kept aside and blocks saves, as config.json does; hand edits hot-reload. Settings -> Window & Tabs -> SSH tab title (`ssh_tab_title`) pins an SSH tab to the profile name (saved host name, ~/.ssh/config alias, or the address typed for a quick connect) or the hostname, on the OSC title's rung of the existing label ladder: a renamed tab still wins, OSC titles are still tracked, local panes are untouched (#726). |
||
|
|
7666e2fd83 |
Merge pull request #941 from l0ng-ai/feat/852-portable-mode
feat(windows): portable mode keeps data beside the executable |
||
|
|
386ccc3d81 |
Merge pull request #939 from l0ng-ai/feat/720-font-smoothing
feat(fonts): make macOS stroke thickening configurable |
||
|
|
f5a97ed68c |
feat(windows): portable mode keeps data beside the executable (#852)
A portable ZIP install whose folder has both the .tty7-portable marker and a data\ folder next to tty7-app.exe now uses <exe dir>\data as its config directory, so settings, sessions, scrollback and the daemon's socket travel with the program. --config-dir and TTY7_CONFIG_DIR still win. The folder is an explicit opt-in because the marker has shipped in every portable ZIP since the in-app updater arrived: keying on it alone would move existing users onto an empty directory at their next update. The portable directory is not the machine's config dir, so a portable copy never adopts the installed tty7's legacy tree. An updater test pins that data\ is not a managed root and survives an in-place update. |
||
|
|
592058acc7 |
feat(fonts): make macOS stroke thickening configurable (#720)
Add a macOS-only `font_thicken` key (default true) and a Settings row under Appearance > Terminal text. When off, AppleFontSmoothing is pinned to 0 in this process's NSArgumentDomain before gpui's text system first reads it, so glyphs render at the face's own weight. The volatile domain is in-memory only: nothing is persisted and no other app is affected. gpui caches the preference in a OnceLock, so a change applies after a restart; no gpui fork change is needed. |
||
|
|
9f842975ce |
feat(editor): add ToggleDocumentPreview / ToggleDocumentWrap actions (#754)
The code panel's Preview/Edit and Wrap buttons only toggled state in their click handlers, so there was no way to reach them from the keyboard. Register both as actions, unbound by default like ToggleDocumentFill and the DocumentWidth* actions, list them in the command palette and the Keybindings page, and route the buttons through the same methods. Instead of config keys for the initial state, the last-used state is remembered (editor_soft_wrap / editor_markdown_preview), the way diff_view and scm_graph_expanded already are. A file opened at a line target always opens as source so the cursor is visible. |
||
|
|
03c3081a6e |
feat(ports): detect and forward what a remote pane is serving
A remote workspace's ports were never listed. The pane lives in the peer's registry and QueryProcs asks this machine's daemon, which has never heard of it, so the answer was an empty list — indistinguishable on screen from a pane serving nothing. Add a control request so the peer answers instead, gated on a feature so an older server says "I cannot tell you" rather than "nothing is listening". With the ports visible, the forward becomes something the user should not have to think about: a port opens on a click, and a new one is forwarded unasked, at the same number where that number is free here. The watch runs with the panel shut, which is when a port appearing is most worth saying something about. Ports and Forwards were two sections that never mentioned each other; a row is now a port, and the forward is where that row says it comes out. Adding one by hand asks for one number instead of five fields, with the rest of the ssh -L grammar one disclosure away. Claude-Session: https://claude.ai/code/session_01TPXrptp2rCGKjXaz4xE3Lq |
||
|
|
e231b16fb3 |
feat(ssh): allow remote image clipboard writes (#766)
* feat(ssh): allow remote image clipboard writes * fix(ssh): keep a profile's clipboard grant across a re-attach A native ssh pane's OSC 5522 permission is decided by the spec that dialled the host, and the daemon is the only side that holds it. A window reopening onto a pane that outlived it attaches by pane id, has no spec to read, and sends `allow_remote_clipboard_write: false` — which the daemon took as the new answer and the pane's own view took as a refusal. Both sides then said no, so the first restart after switching the permission on turned every copy into an `EPERM` with the switch still reading "on". Pin the spec's answer in the pane and route both attach and detach through one decision point, so a pane that carries a spec keeps that spec's answer whatever an attaching client claims, and a pane without one — everything on a remote `tty7-server` — is exactly as permitted as its controller says. On the client side, refuse only what the pane can see is forbidden and leave the verdict to the daemon otherwise. Also: release a failed transfer's buffered bytes instead of parking up to `MAX_CLIPBOARD_BYTES` per pane until the next request, and answer the capability probe with the permission actually in force rather than a constant that always reads as "off". --------- Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> |
||
|
|
0b6c332618 |
feat(settings): add interface font family configuration in appearance typography (#761)
* feat(settings): add interface font family configuration in appearance typography * fix(settings): let the interface font go back to the system face Three things the new **Interface font family** row spelled once and needed twice. `apply_theme` only wrote `Theme.font_family` when the setting was `Some`, and `Theme::change` never puts it back — it rewrites the field only when a theme config names a face, and none of ours does. So picking a font worked, and picking **Default** back saved `None`, redrew every window in the font the user had just cleared, and only came true at the next launch: a setting that looked like it had applied instantly and had not. The face is now assigned in both directions, against the stock value read once before anything overrode it. The dropdown's first row borrowed the bold/italic label, "Default (match primary)" — which promises the *terminal's* primary family. The interface falls back to the system UI font instead, so the row said the chrome would come out in Hack while the description beside it said the opposite. It gets its own label in all three locales. `ui_font_family` was also the one key in `config.json` that disappeared when unset; every other optional key is written as `null`. Dropped the `skip_serializing_if` so the file still lists it, and documented the key in the two tables that enumerate the typography settings. --------- Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> |
||
|
|
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. |
||
|
|
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. |
||
|
|
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 |
||
|
|
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> |