Commit Graph
1412 Commits
Author SHA1 Message Date
l0ng-ai 760be844aa feat(links): make Windows file paths clickable
Drive-letter paths with either separator and UNC shares resolve as file
links, including when CJK prose or a Markdown link is glued onto them.
Windows paths are spelled with backslashes and an upper-case drive so
Explorer can open and reveal them, the Windows join is textual so a Mac
asking a Windows host sends one spelling, and a POSIX pane looks a drive
path up under /mnt/<drive> for WSL. Drive-relative tokens (a:b, C:,
C:notes.txt) are never probed.

Closes #965
2026-09-26 14:00:44 +02:00
l0ng-ai 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.
2026-09-25 16:53:26 +08:00
l0ng-ai 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.
2026-09-25 16:47:59 +08:00
l0ng-ai 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.
2026-09-25 16:44:32 +08:00
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