Commit Graph
1408 Commits
Author SHA1 Message Date
l0ng-ai bd0dd22bfa feat(tabs): hibernate a tab to free its memory and wake it later (#954)
A tab can be put to sleep from its context menu or the command palette:
its panes are stopped (screens kept on disk), the tab keeps its place in
the sidebar, and selecting it wakes it through the same restore a reboot
runs, resuming a supported agent's session. The sleep mark lives on the
machine tree, so it survives app and daemon restarts.

Closes #762
2026-09-25 16:41:12 +08:00
l0ng-ai 9f034558c0 feat(ssh): switch a port forward off without losing its rule (#953)
A forward could only be removed, so pointing one local port at another
remote target meant deleting the rule and retyping the other one (#439).

Forward rules gain an `enabled` flag (serde default on, so saved profiles
and older peers keep every rule live) and the daemon a SetForwardEnabled
op for panes and workspaces. Off releases the listener and keeps the
entry; on rebinds it from the rule it was made from, and is refused by
name when another switched-on forward of the same owner holds the port.
The Ports panel and the Settings rules editor each get a switch; a
switch flipped in the panel on a rule from a saved host is written back
to that host.

Closes #439
2026-09-25 16:38:00 +08:00
l0ng-ai 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).
2026-09-25 16:34:38 +08:00
l0ng-ai 02d8611e33 feat(cli): add exec, and send --stdin/--from-file/--paste (#951)
`tty7 exec %N -- CMD` types a line at an existing pane's shell prompt,
follows the shell integration's OSC 133 marks to the command's end, prints
what it printed (rendered to text by default, `--raw` for the bytes) and
exits with its exit code. `--timeout` exits 124 and leaves the command
running. A pane with no prompt marks, or one not at a prompt, is refused
before anything is typed.

`tty7 send` can take its text from `--stdin` or `--from-file`, sent byte
for byte and never echoed in --json, so a secret stays out of `ps` and
shell history. `--paste` frames the text the way the GUI's paste does:
bracketed when the pane has mode 2004 on, unframed otherwise, reported in
--json. The framing moves into tty7-core (`core::paste`) so the GUI's
clipboard paste, the agent prompt and the CLI share one construction, and
the daemon now reports each pane's bracketed-paste mode in PaneContext.

Closes #839
Closes #838
2026-09-25 16:32:19 +08:00
l0ng-ai 23948bcdda fix(render): draw fallback Nerd Font icons at the text's size (#949)
* fix(render): scale fallback-font icons up to fill their cell

A lone Private Use Area glyph supplied by a fallback face is drawn at the
primary's font size on the fallback's own metrics, so Nerd Font icons came
out about two thirds of the cell. Grow such a glyph, aspect ratio kept,
until it fills the width of its cells or the height of the row (at most
2x), and centre it there. Overflow is still shrunk as before, text from a
fallback face keeps its metrics, and the primary's own icons are untouched.

Closes #866

* fix(render): fit fallback icons to their cells, borrowing a same-colour blank

Checked in the running app with Symbols Nerd Font Mono behind Menlo: its
icons ink a full em (1.6 cells), so growth alone never fired. What made
them small was the one-cell budget an icon gets when the space after it
paints a background, which is every icon in a coloured prompt segment.

Fit a fallback icon to its cells and one em of height instead, letting it
take the blank after it when that blank paints no background or the
icon's own, and only when that makes it bigger. Leave the Powerline
separators to the existing rule.

* docs(render): name icon_fit in the fit_scale test's comment
2026-09-25 16:28:49 +08:00
l0ng-ai 455e74dc2c feat(scm): filter changed files and show them as a tree (#950)
* feat(scm): filter changed files and show them as a tree

* test(scm): key the tree test's settle on the panel's own root
2026-09-25 13:53:44 +08:00
575d2cd68e fix(agents): drop the previous session id when the foreground agent changes (#957)
同一 pane 里前台从 Claude 换成 omp,或别的 agent 的 hook 写进这个 TTY 时,旧的 session id 会留在新 agent 上,Fork 就会拿错 id。

Co-authored-by: stevelliu <stevelliu@tencent.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-25 13:52:36 +08:00
l0ng-ai 3a52e34b02 fix(linux): wrap the confirmation dialog's text instead of clipping it (#948)
Linux has no native prompt, so every window.prompt fell through to gpui's
fallback renderer, which sets the message and the detail as unbreakable
single lines in a fixed-width box with overflow hidden. The quit-and-stop
warning was cut off mid-sentence. Install a prompt builder on platforms
without a native dialog that lays the same prompt out as a card whose text
wraps, in the app's own surfaces, with Return taking answer 0 and Escape
the cancel answer.

Claude-Session: https://claude.ai/code/session_01JRqYZ9E153WpSHGS2AW3BM
2026-09-25 13:50:39 +08:00
l0ng-ai f852629ba1 fix(procinfo): read proc_listallpids' answer as a pid count, not bytes (#947)
libproc's proc_listallpids takes its buffer size in bytes but answers
with the number of pids, and the macOS process table read that number
as bytes twice: once sizing the buffer (count / 4 + 64 slots) and once
reading back how much was filled (count / 4 again). On a Mac with ~700
processes only the first ~60 pids reached the table.

The kernel lists its newest processes first, so what survived was
whatever started most recently. A `go run` server launched a moment ago
was in; the pane's shell, started long before, usually was not, and a
walk from a root missing from the table returns nothing: no processes,
no ports, and a probe state of Ok. A freshly built parent/child chain,
which is what a quick check of the probe builds, is exactly the case
that happened to work.

The listing now goes through list_all_pids, which takes the call as a
closure so the size arithmetic is tested off a Mac against an emulation
of libproc's convention.

Claude-Session: https://claude.ai/code/session_01JRqYZ9E153WpSHGS2AW3BM
2026-09-25 13:49:28 +08:00
l0ng-ai 458c923aaa docs: lead with the tour video instead of the old hero screenshot
The README now opens on an animated WebP of the one-minute tour (GitHub
won't play an mp4 inline), linking the full-quality mp4. The docs home
page autoplays the mp4 in the hero slot. The old hero screenshot and the
separate tour poster block are gone.
2026-09-24 08:28:23 +08:00
l0ng-ai 33aa52ca90 style: rustfmt the right panel's tab row height 2026-09-24 08:26:31 +08:00
l0ng-ai fed4aec170 docs: replace placeholder screenshots and add a one-minute tour video
Every placeholder frame in docs/ now shows a real capture of the current
build: 20 screenshots plus two short looping clips (prompt editor, pane
drag). The README and the docs home page link a one-minute tour covering
agent status across repos, one agent driving another through the CLI,
the prompt editor, diffs, pane dragging, and sessions surviving a quit.
2026-09-24 08:18:14 +08:00
l0ng-ai 1ca7268919 feat(ui): blink a working agent's status dot
A working agent's dot now alternates every 600ms between its colour and
a paler, still opaque, blend of it over the dot's ring, so a column of
tabs shows at a glance which agents are still going.

It rides the existing half-second home-cursor tick rather than an
animation: two repaints a second while some tab has a working agent,
and none once no agent is working (the dot settles back on its full
colour with one last frame).
2026-09-23 22:33:23 +08:00
l0ng-ai 90327861c1 fix(panel): shorten the right panel's tab row below the title bar
On Windows and Linux the tabs sit under a full-width title bar, and a
second 40px band left them well down from the window's top. The row is
now the tab's hover pill plus 2px either side.
2026-09-23 22:20:53 +08:00
l0ng-ai 58d69e0fad fix(search): keep the find bar's frame neutral while focused
An accent border over the bar's drop shadow read as a glow; the caret
already says where focus is. A regex error still turns it red.
2026-09-23 22:20:53 +08:00
l0ng-ai c2d4e7f2c8 feat(settings): open settings in a window of its own
Settings used to cover the workspace it was opened from, hiding the
terminal a setting was being tried on. It now opens a separate window.

- The state and page stay on the workspace's Tty7App; a thin
  SettingsWindow view borrows it to draw, observes it to repaint, and
  sets its own rem size. The state is built against the new window, so
  its inputs and subscriptions belong to it.
- Opening again raises the window instead of closing it. Esc, Cmd-W and
  the close button go through close_settings_checked, so unsaved forms
  and theme drafts still prompt; closing hands focus back to the
  workspace window. The window goes when its owner is released.
- open_settings_then / close_settings_then run follow-ups in the right
  window: loading an SSH form happens in the settings window, and
  Connect opens its tab in the workspace window.
- Window opacity, blur and backdrop are pushed to every window, since
  they are now changed from a window that is not the workspace.
- Tests keep the in-window overlay so they can drive settings through
  their single test window.
2026-09-23 22:18:54 +08:00
l0ng-ai 826c42f6bd fix(scm): give the folded History bar air above the window edge
Folded, the history section is the last line in the window and sat flush
against the bottom edge, inside the window's rounded corner. Pad it 2px
above and 4px below; the expanded section is unchanged, so its
commits-per-height pin still holds.
2026-09-23 22:06:38 +08:00
l0ng-ai 0988c57d5c fix(ui): restore the sidebar's rounded-rectangle corners
The v5 restyle turned the tab rows, the search field and the workspace
switcher into full pills, which read too round at this density. Put
back CARD_RADIUS on the rows, rounded_lg on the search field and
rounded_md on the switcher (and its rename field), keeping the 28px
row height. PILL_RADIUS has no users left and goes with them.
2026-09-23 22:02:45 +08:00
l0ng-ai a3d64bfcc9 fix(ui): drop the solid accent from the switcher's picked row
The workspace switcher painted its picked row a hard-coded #1768CF with
white text, in the workspace list, the tab list and the New Workspace
host dropdown. It bypassed the theme and was the loudest block on
screen. Use the popover's neutral selected step and keep each row's own
inks, so a taken-over warning still reads as one.
2026-09-23 21:50:04 +08:00
l0ng-ai 859c286c6a fix(panel): lead the right panel's tab row on Windows
The tabs sat after the row's flex spacer, so they hugged the window
controls instead of opening the row the way they do on macOS.
2026-09-23 21:49:27 +08:00
l0ng-ai ae0d129816 Merge pull request #946 from l0ng-ai/feat/v5-ui-restyle
feat(ui): restyle the workspace after the v5 design
2026-09-23 21:38:10 +08:00
l0ng-ai d18f797a6c Merge pull request #945 from l0ng-ai/fix/942-wsl-files-panel
fix(files): keep a WSL tree in step with its distro (#942)
2026-09-23 21:38:06 +08:00
l0ng-ai 4ff8fe4fb2 Merge pull request #944 from l0ng-ai/fix/bounded-grid-lock
Let go of the grid lock every 64 KB while parsing pane output
2026-09-23 21:38:01 +08:00
l0ng-ai 27406e472d Merge pull request #943 from l0ng-ai/fix/watch-close-off-ui-thread
Stop waiting on remote round trips from the UI thread
2026-09-23 21:37:57 +08:00
l0ng-ai f1648117b7 feat(ui): restyle the workspace after the v5 design
- Rails share the window fill and are separated by a hairline only;
  the current tab is a neutral selected step plus a semibold title
  instead of the blue navigation wash (the unused role is removed).
- Default Light theme takes ink #0F1419 and accent #1F6BF0; the caret
  follows the accent.
- Sidebar: 28px pill rows with 18px avatars (two-line rows keep card
  corners), pill search field and workspace switcher, group headings
  in body ink with the chevron shown only when folded.
- Right panel: word tabs (Info / Changes / Files) with an underline
  bar on a closing hairline. The Changes count is dropped before any
  label is cut, and the panel floor grows with the measured labels so
  the chrome tiles are never pushed off the edge.
- Headings are no longer uppercased; popovers use a 10px radius.
- docs/design-system.md updated to match.
2026-09-23 21:35:28 +08:00
l0ng-ai 1215eba16d fix(ui): drop the trailing tiles while a docked document holds the right edge
On macOS the panel toggle and app menu tiles sat at the end of the terminal
column's strip whenever the detail panel was closed, including when a docked
diff or file column stood to its right, leaving them in the middle of the
window beside the document's header. Hide them while a document is docked and
stop reserving their width for the tab chips.
v26.9.3
2026-09-23 19:28:39 +08:00
l0ng-ai 8fa9651814 fix(files): keep a WSL tree in step with its distro (#942)
Three things went wrong with a Files panel rooted on a distro's \\wsl$
share:

- A drop brought a `name:Zone.Identifier` file along with every
  downloaded file. fs::copy is CopyFileEx on Windows, which copies the
  NTFS alternate data streams, and the share has no streams to keep one
  in, so it lands as a file of its own. A copy onto a WSL share now
  moves the contents only; one onto NTFS still goes through fs::copy
  and keeps the mark.
- A file removed from the shell in the distro stayed in the tree. The
  share accepts a ReadDirectoryChangesW and never reports a change, so
  the watch looked healthy and was deaf. Directories on a WSL share are
  now watched by a notify PollWatcher every 2s instead, made the first
  time one is needed.
- Right-clicking a row did not select it, so nothing marked the row the
  menu was about. A right click now selects the row without opening it.
2026-09-23 19:26:26 +08:00
l0ng-ai d843b82286 chore(release): v26.9.3 2026-09-23 19:21:12 +08:00
l0ng-ai 439f19ff85 Stop waiting on remote round trips from the UI thread
Dropping a RemoteWatch sent WatchClose with a blocking call, and the last
handle is usually let go on the UI thread: from Tty7App::render via
scm_sync_watchers, and from scm_watch_opened when an open lands after its
subscription moved on. Every window froze for a round trip each time, up
to WatchClose's 5 s deadline on a quiet link. Sampling a live instance
caught ~1.1 s of such stalls in 10 s.

- ControlClient::post sends a request without registering for its reply;
  the reader already drops replies nobody is waiting for.
- RemoteWatch::drop posts WatchClose instead of calling it.
- pane_workspace_for built the full SSH spec, keychain lookups included,
  only to strip the secrets again. It now builds it from NoCredentials,
  which takes the securityd trips off pane_liveness::sweep.

Claude-Session: https://claude.ai/code/session_01YX786Hyr4ivijWxv66zVkf
2026-09-23 19:00:54 +08:00
l0ng-ai b75b60e6af Let go of the grid lock every 64 KB while parsing pane output
The reader parsed each batch — and a relink's whole replay, up to 8 MB —
in one hold of the grid lock, and every UI-thread handler that touches
the grid queues on it. Feed the emulator in MAX_LOCKED_FEED slices, the
same bound Alacritty's reader uses.

Claude-Session: https://claude.ai/code/session_01YX786Hyr4ivijWxv66zVkf
2026-09-23 18:46:48 +08:00
l0ng-ai 800c6bf724 Merge pull request #933 from l0ng-ai/feat/comfortable-ui-surfaces
feat(ui): refine panel readability and workspace surfaces
2026-09-23 16:59:54 +08:00
l0ng-ai 7666e2fd83 Merge pull request #941 from l0ng-ai/feat/852-portable-mode
feat(windows): portable mode keeps data beside the executable
2026-09-23 16:58:12 +08:00
l0ng-ai 0b53ff16ef Merge pull request #940 from l0ng-ai/feat/767-prompt-input-new-line
feat(prompt-editor): start input on its own row when the prompt leaves too little room
2026-09-23 16:58:08 +08:00
l0ng-ai 386ccc3d81 Merge pull request #939 from l0ng-ai/feat/720-font-smoothing
feat(fonts): make macOS stroke thickening configurable
2026-09-23 16:58:03 +08:00
l0ng-ai 296a8a3c3f Merge pull request #938 from l0ng-ai/feat/760-stable-workspace-numbers
feat(workspaces): number workspaces in a stable order (#760)
2026-09-23 16:57:59 +08:00
l0ng-ai da6c8624de Merge pull request #937 from l0ng-ai/feat/723-remote-tree-download
feat(file-tree): download a remote file from the tree's context menu
2026-09-23 16:57:54 +08:00
l0ng-ai a012321542 Merge pull request #936 from l0ng-ai/feat/892-741-codebuddy-cursor-hooks
feat(agents): CodeBuddy CLI support and Cursor CLI hooks
2026-09-23 16:57:50 +08:00
l0ng-ai 2eb1a79d76 Merge pull request #935 from l0ng-ai/feat/754-dock-preview-wrap-actions
feat(editor): add ToggleDocumentPreview / ToggleDocumentWrap actions
2026-09-23 16:57:45 +08:00
l0ng-ai 2aa28d3982 Merge pull request #934 from l0ng-ai/feat/867-direct-tab-cycling
feat(tabs): step to the next/previous tab without the switcher (#867)
2026-09-23 16:57:41 +08:00
l0ng-ai 5d861f991f Merge pull request #932 from l0ng-ai/feat/826-sftp-open-at-cwd
feat(sftp): open the Files panel at the shell's current directory
2026-09-23 16:57:36 +08:00
l0ng-ai 3d4c158278 fix(agents): open Cursor turns on beforeSubmitPrompt, count tool calls as activity
The interactive cursor-agent TUI fires beforeSubmitPrompt and stop; only
print mode (-p) lacks them. Mapping postToolUse to prompt-submit froze the
activity counter (opportunistic git refresh, agents --changed), shortened
turn timing and hid tool-less turns. Map it to tool-complete instead.
2026-09-23 16:54:06 +08:00
l0ng-ai 75480cac05 fix(ui): improve panel readability at narrow sizes 2026-09-23 16:51:59 +08:00
l0ng-ai 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.
2026-09-23 15:44:27 +08:00
l0ng-ai 9a01a12852 docs(prompt): describe the input moving below a long prompt (#767) 2026-09-23 15:43:05 +08:00
l0ng-ai 57ca21dd68 feat(workspaces): number workspaces in a stable order (#760)
SelectWorkspace1-9 and the Window menu numbered workspaces by most
recent use, so switching to one moved it to slot 1 and reshuffled the
rest. Number them by the order this client first had them instead
(the append-only views list), skipping synced remote references until
they are opened; opening one moves it to the end so it takes the next
free number. The switcher keeps its MRU list and shows each row's
number.
2026-09-23 15:38:28 +08:00
l0ng-ai 5c3d2a9e8a feat(prompt-editor): start input on its own row when the prompt leaves too little room (#767)
With a long prompt the inline editor started at the prompt's end column,
leaving e.g. 24 of 143 columns for the first row of input. When fewer than
max(20, cols/3) columns remain (and the prompt is at least as wide as what
it left, so a new row actually gains room), the input overlay now starts at
column 0 of the row below the prompt.

The start position is decided by one pure fn, input_start(), and fed to
everything that lays out the input: the overlay rows and overflow shift,
click-to-index mapping, vertical caret movement, the rendered bar, the
completion menu anchor and the IME candidate anchor.
2026-09-23 15:32:26 +08:00
l0ng-ai 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.
2026-09-23 15:23:10 +08:00
l0ng-ai 43d2b902bb feat(file-tree): download a remote file from the tree's context menu
A remote workspace's Files tree took uploads by drag-and-drop but had no
way to bring a file back. Right-click a file -> Download now reads it
through Host::read_file and saves it into this machine's Downloads
folder, named and numbered the way the SFTP panel's Download does.

Capped at the same ~63 MB as uploads (one control frame); an oversized
file is refused up front with the limit and nothing is transferred.
Local trees keep Reveal in that slot; folders are not offered.

Closes #723
2026-09-23 15:22:57 +08:00
l0ng-ai fe7b6e0b76 feat(agents): wire Cursor CLI (cursor-agent) into agent hooks (#741)
Cursor's ~/.cursor/hooks.json is a flat hook map — `command` directly on
each entry under `hooks.<event>` — so it reuses the flat writer Crush
introduced. Two things are Cursor-specific:

- The file needs `"version": 1` at its root or Cursor ignores it. tty7
  now writes it when it creates the file or finds it missing, never
  overwrites a version the user set, and reports a versionless file that
  holds our hooks as Outdated so refresh repairs it.
- Payloads name the session `conversation_id` (only sessionStart repeats
  it as `session_id`), and most events carry `workspace_roots` instead of
  `cwd`. Both are now read as aliases, which is what Copy Session ID and
  resume were missing.

Events: sessionStart, beforeSubmitPrompt, postToolUse, stop, sessionEnd.
cursor-agent does not fire beforeSubmitPrompt today (a known gap on
Cursor's side), so postToolUse also reports prompt-submit: the first tool
call opens the turn and Cursor's stop, which the CLI does fire, closes it.
A turn with no tool calls never shows as working. None of Cursor's
permission hooks are installed, since those would block on the empty
stdout tty7's hook prints.
2026-09-23 15:21:34 +08:00
l0ng-ai 962fd87ea5 feat(tabs): step to the next/previous tab without the switcher (#867)
Add SelectNextTab / SelectPrevTab, which move to the neighbouring tab in
the order the strip or sidebar shows them, wrapping, with no popup.
Defaults: Cmd+Shift+] / Cmd+Shift+[ on macOS, Ctrl+PgDn / Ctrl+PgUp
elsewhere. The tmux preset's prefix n / p now bind these (tmux
next-window semantics) instead of the MRU switcher, which never
auto-committed there.

NextTab / PrevTab keep their config names and Ctrl+Tab, but are labelled
Recent Tab Switcher on the Keybindings page; the palette's Next/Previous
Tab entries now show the SelectNextTab chord they actually run.
2026-09-23 15:20:34 +08:00