Commit Graph
590 Commits
Author SHA1 Message Date
l0ng-ai 709243f960 fix(switcher): stop opening context menus with every row greyed out
Two menus in the switcher could open with nothing to press and no word
about why, which reads as broken rather than as not-yet.

A remote workspace this client has never adopted has no local id, and
all four row verbs address a workspace by id — so right-clicking one
greyed the whole menu. A machine reached over WSL or stdio that has
never connected has no home directory for New Workspace, no live link
for Disconnect, and no Restart Server, which is offered to SSH alone;
its menu greyed out the same way.

Both now say what would make the verbs work, in the register the tab
pane already uses for adopted rows ("Open this workspace to see its
tabs"). The machine case gets a named predicate so the one state that
reaches it stays pinned while the verbs move around.
2026-08-09 02:37:08 +07:00
l0ng-ai a74bb674b3 refactor(sidebar): read the tab badge off the order the chord uses
⌘N runs ActivateTabN, which goes through `activate_visual` and lands on
the Nth row as the sidebar lays it out — grouping gathers each
workspace's tabs together, so that is not the Nth entry of `self.tabs`.
The badge agreed, but only because `tab_sidebar` flattened `sections`
into the same order by hand; the badge and the chord were two readings
of one order taken in two places, free to drift apart with nothing to
catch it.

Take the badge from `visual_tab_order`, the function the chord already
calls, and pin the pair with a test over a grouping that reorders.
No change in what any badge says today.
2026-08-09 02:29:51 +07:00
l0ng-ai 92827665b0 fix(switcher): stop eliding a path into half a directory name
The picker cuts a long path to a character count and prefixes "…", which
lands mid-name as often as on a separator. What is left reads as a
directory that exists — "…eeply/nested/projects" offers "eeply" with the
same weight as "nested", and there is no way to tell from the row which
of the two is a real name.

Snap the cut forward to the next separator. A partial name carries
nothing the rest of the path does not, so it goes — unless it is the
longer half, which is a single component overrunning the whole budget
with nothing else left to show; there the fragment is all there is.
2026-08-09 02:15:55 +07:00
l0ng-ai 5195f49b5d fix(home): tell a month-old workspace from a week-old one
The relative-time ladder stopped at "over a week ago", so in the
switcher — where the timestamp is the whole reason the line is there —
a workspace last opened yesterday-week and one opened a year ago read
identically. The interesting half of the range was one label.

Carry it on through weeks, months and "over a year ago". Weeks run to a
twelfth of a year rather than to 30 days: a 30-day month leaves the five
days between the last whole week and the first whole month rounding down
to "0 months ago", and a twelfth tiles the year with no such seam. The
new test walks every day from 7 to 400 to hold that.
2026-08-09 02:14:03 +07:00
l0ng-ai e478c5b9fa fix(close): name the command the confirmation is about to end
Closing a busy tab asks "{what} is still running. Closing ends it." —
and {what} was the window title as it stood when the command began. A
prompt that titles by directory, which is most of them, made tty7 ask
"tty7 is still running. Closing ends it." about a folder. The comment
over CloseReason already says this dialog exists because ending a shell
is permanent and has to name what it ends; it was naming the wrong
thing.

The shell integration already sends the submitted line as the OSC 133;C
mark, and the client records it — only the reader was #[cfg(test)],
because nothing had needed it since the panel that used to read marks
was removed. The wire frame carries whether a command runs, not which,
so the mark is the only place the text exists on this side.

So: prefer the running mark, fall back to the old title when there is no
mark (no shell integration). The text is unescaped — the integration
escapes % and the four bytes that would break OSC framing — and clamped
to 60 characters so the dialog still reads as a sentence.

Verified: `sleep 300` then ⌘W now asks about "sleep 300".
2026-08-09 01:51:49 +07:00
l0ng-ai 94fd075080 fix(keybindings): stop the menu ellipsis trailing off inside a sentence
Rebinding a chord that another action holds prints "{action} took the
shortcut from {previous}, which is now unset." Both names come off the
keybindings page, where a command that opens something carries a "…" —
so the note read "Rename Tab… took the shortcut from Close Pane / Tab",
and the marker that means "this opens a dialog" turned into a sentence
losing its nerve.

The ellipsis is a menu convention, not punctuation, so it comes off when
the name is interpolated into prose. The rows themselves keep it.
2026-08-09 00:17:17 +07:00
l0ng-ai 9c26b2c253 fix(worktree): stop the prompt offering a Create that cannot work
Submitting falls back between the two name fields — either alone is
enough, and only both blank has nothing to name a worktree after. The
form did not say so: Create stayed primary and enabled with both empty,
and clicking it could only raise "The worktree needs a name".

Create is now disabled in exactly that state, and the path preview
follows the same fallback it always should have: with only a branch
typed it read ".tty7/worktrees/…", describing a path that would never be
created, where the worktree actually takes the branch's name.

Checked all three states on screen: both blank disables Create, a branch
alone re-enables it and fills the preview in, and a name alone is
unchanged.
2026-08-08 23:42:14 +07:00
l0ng-ai 6b8194bd67 fix(editor): show the file tree the empty editor tells you to use
Opening the code panel with no file draws "Open a file from the file
tree" and hands the tree keyboard focus — but nothing put the tree on
screen. On a fresh tab ⌘⇧E gave you an empty editor across the whole
content area, naming a panel you could neither see nor reach from there;
⌘J did not help, because the code panel had the space.

The branch that reaches for the tree now reveals it first, using the
file_tree_on_screen check that already existed. Verified end to end:
⌘⇧E opens the editor with the tree beside it, and clicking a file in it
loads the file.
2026-08-08 23:22:34 +07:00
l0ng-ai 52219a1a04 fix(panel): keep the end of the path the Info panel is naming
The cwd row truncated like every other value — from the tail — so a deep
checkout read "/private/tmp/claude-501…" and named nothing. The last
segment is the part that says which directory the pane is in.

The row now lets the head absorb the shrinking and keeps the leaf, the
way a file manager draws a path: at a narrow panel it reads
"…and-another-nested-leve…", and widening it fills in
"/private/tmp/claude-501/-Users-thoma…and-another-nested-level-here".
No character cap, so it uses whatever width the panel has.

Only the path row: a branch name reads head-first, so branch still
truncates from the tail.
2026-08-08 23:14:47 +07:00
l0ng-ai e46a2fbbb6 fix(sftp): let double-click open a file, not just a folder
The gesture ran sftp_enter_dir, which returns early for anything that is
not a directory. Double-clicking a file — what every file browser, and
every SFTP client, answers by opening it — did nothing at all: no
transfer, no cursor change, no message. The row menu right beside it
offers Download, and sftp_open_entry already handles both kinds.

Point the gesture at that instead and drop the directory-only handler,
which had no other caller. A download shows up in the transfers tray and
can be cancelled from it, so the worst case is a click you can take back.

Verified against a real SSH connection to localhost: double-clicking a
file now lands it in ~/Downloads at the right size, and double-clicking
a folder still navigates into it.
2026-08-08 23:04:21 +07:00
l0ng-ai fc73559e2b fix(panes): give the split divider the 8px grab the panel edges have
The sidebar and right-panel edges hand you an 8px target; a split
divider was 5px, so the one drag you do most often was the fussiest to
start. I had left this alone thinking a wider band would eat clicks at
the edge of the terminal text — it does not: the grid is inset by its
own GRID_PAD_X of 8px, so the extra 1.5px a side lands in padding and
stops 4px short of the nearest cell.

The gutter still measures 5px, so the split looks exactly as it did; the
target is an overlay centred on it, the same shape the panel edges use.

Measured rather than eyeballed: the divider line sits on one pixel
column, and dragging from 1.5px outside the old band now moves it.
2026-08-08 22:45:04 +07:00
l0ng-ai 151ed9dcd6 fix(sftp): give the transfers list its scrollbar too
The last scroll area in the app without one. It caps at 200px and
scrolls past it, so eight queued downloads run off the bottom edge with
nothing to say so.

The shared helper grows into a flex parent, which is wrong for a box
that already carries its own height — taking flex_1 there would stretch
it past its cap or collapse it, which is why this one had been left
alone. `over_vertical_scroll` lays the same bar over an area that
measured itself, so the layout is exactly what it was without it.

Verified over a real SSH connection to localhost: queued eight
downloads, confirmed the list still scrolls through them and the bar
renders.
2026-08-08 22:38:02 +07:00
l0ng-ai cab1260faa refactor(ui): define the panel resize-handle width once
The tab sidebar and the right panel each declared their own
RESIZE_HANDLE_WIDTH = 8. Two edges the same window drags, two numbers
that only happened to agree — the same shape as the target fade that had
already drifted apart. One constant now, named where the panel lives.
2026-08-08 22:05:09 +07:00
l0ng-ai 555b21cf9d fix(switcher): give both columns the scrollbar the card already implies
The workspace list and the tab list both scroll once they outrun the
card, and neither showed a bar — with eight tabs the ninth is cut off
mid-row with nothing to say more is below. Both wrap in the shared
helper now, and the column border moves out of the scroll area so it
stays put while the rows move under it.

Checked that arrow-key navigation and the scroll anchors still carry the
cursor through the list with the wrapper in place.
2026-08-08 21:58:55 +07:00
l0ng-ai 544badfa33 fix(diff): give the diff overlay the scrollbar every other scroll area has
A working tree can run to thousands of lines here, and the overlay was
the one long document in the app with nothing to say how far it went or
where in it you were. Nine scroll areas already wrap in the shared
helper; this one had not even been given a ScrollHandle.
2026-08-08 21:54:01 +07:00
l0ng-ai abed0177ae fix(sidebar): show that the diff counts are their own click target
A row's +N −N opens the working-tree diff, but the only sign of it was
the pointer shape: the row highlights as a whole, which says nothing
about the counts being a button inside it, and with the setting off they
look exactly the same. They now underline under the pointer, the idiom
the SFTP breadcrumb already uses for clickable text.
2026-08-08 21:47:30 +07:00
l0ng-ai a53d9ad3e4 fix(settings): let the Advanced disclosures light up under the pointer
Jump host, Port forwarding and Advanced fold and unfold on click, but
only changed the cursor. Every other clickable row on the page answers
the pointer with a band — the settings rows, and the SSH group header,
whose comment already explains it wants the same 8px inset the rows
hover with. These three now take the same band and radius.
2026-08-08 21:44:18 +07:00
l0ng-ai 147953d3a3 fix(ssh): say when the keychain will not forget a password
Three of the four keychain writes log the error the store hands back;
DeletePassword dropped it. That is the arm behind "The stored password
was rejected. Enter a new one." — if the delete keeps failing, the
rejected secret stays in the keychain and gets offered again on the next
connect, with nothing in the log to say why.
2026-08-08 21:40:01 +07:00
l0ng-ai 138455e33b fix(copy): put every popup menu in the Title Case the rest of them use
Thirty-four of the app's thirty-eight menu items are Title Case — the
menu bar, the command palette, the tab menu, the local file tree. Nine
were not, and the split ran along a seam a user actually crosses: the
local Files panel offers "New File" and "New Folder" while the remote
Files panel in the same slot offered "New file" and "New folder".

SFTP: New Folder, New File, Go to Shell Directory, Transfer History,
Hide Transfer History, Follow Symlink. Settings → SSH: Copy Address,
Expand All Groups, Forget Password.

Left alone: "cd Here" and "chmod…" start lower case because the command
is spelled that way, "Import from ~/.ssh/config" already was Title Case
(prepositions stay down, as in "Close Tabs to the Right"), and the SFTP
edit form keeps sentence case because it is a form title, not a menu.
2026-08-08 21:37:49 +07:00
l0ng-ai 36c82f0a63 style(ui): run both of the app's transitions on one duration and curve
The tab reorder slide runs 140ms on ease_out_quint from a named
constant; the home page fade-in ran 150ms linear from a literal. Two
transitions, two answers to the same question. TRANSITION_MS now names
the duration both use, with REORDER_SLIDE_MS defined as it, and the fade
takes the same curve.
2026-08-08 21:29:33 +07:00
l0ng-ai 98be4e5ece fix(ui): give three faded states one value each instead of two
A sweep of every .opacity() call turned up three places where one
meaning had two strengths:

- A Dynamic forward has no target, so the target endpoint fades. The
  rules editor in Settings faded it to 0.35 and the live Forwards panel
  to 0.4 — the same row, drawn twice. Now a named constant both use.
- switcher.rs destructures (fg, muted, dim) in five render functions;
  four dimmed to 0.7 and render_group_header to 0.75, so a header sat a
  step brighter than the rows under it for no reason.
- The tinted-border notice box is 0.4 in the window, the Forwards panel
  and the themes-rejected note, and was 0.35 in the switcher.
2026-08-08 21:23:53 +07:00
l0ng-ai 0eeea07571 fix(settings): round the theme card the way the theme panel rounds it
The Appearance page and the theme panel draw the same object — a theme
preview, its name, a border — and drew it differently. The page card was
rounded_xl, the only 12px corner in the app, while the panel card that
opens from it uses TRACK_RADIUS. Worse, the preview inside the page card
kept its own 8px corners inside a 12px border, the concentric mismatch
that rounding::inner_radius exists to prevent; the panel already insets
its copy of that same preview.

Both now use TRACK_RADIUS with the preview inset by the hairline, and
theme_preview names the token instead of repeating its value.
2026-08-08 21:08:59 +07:00
l0ng-ai 3e7ff034e1 fix(sftp): stop calling a filtered-out directory empty
Typing a filter that matched no entry left the remote Files panel saying
"Empty directory." — not silence, a false statement, and about a machine
the reader cannot see for themselves. It now says which query matched
nothing, the way the local file tree already does for the same case.

Not verified on screen: reaching this panel needs a live SSH host. The
branch is the one the local tree takes, and the full suite passes.
2026-08-08 20:59:43 +07:00
l0ng-ai ca8af9d169 fix(settings): tell the theme picker when its filter matched nothing
Type a word no theme is named after and the panel emptied out under its
own search box with no explanation. Every other filter in the app
answers: the settings search, the tab sidebar, the SSH host list, the
switcher and the palette all say so, and four of them already say it
with this exact string.
2026-08-08 20:56:39 +07:00
l0ng-ai bfc0937bf6 fix(settings): show the reader the note that says nothing matched
"Nothing matches X." is drawn at the top of the content pane, but the
reveal that carries the page to a search result deliberately skipped the
case where the count was zero — the one case where the thing worth
seeing is pinned to the top. Search from halfway down a page and you got
the untouched page the note exists to explain, with the note itself off
screen above you.

The note now claims the same scroll anchor the rows use, and the reveal
fires when the query matched nowhere as well as when it matched here.

Checked on screen: scrolled to the bottom of Terminal, typed a word that
matches nothing, and the page came back up to the note.
2026-08-08 20:50:03 +07:00
l0ng-ai 02f556d597 fix(settings): make background images findable from the settings search
"background image" is a headline feature — the README sells solid,
gradient and image backgrounds — and searching for it found nothing.
Worse than nothing: typing it walked the page to About, because
"background" on its own matches Download updates in the background, and
the last two words then dropped the count to zero and left the reader
stranded there with no badge and nothing dimmed.

Background image and Image opacity now carry index entries, and the
Custom themes intro gains the same keywords so the query has something
to light in the state where those two rows do not exist yet — which is
the state you are in before you duplicate a theme, next to the button
that gets you there.

The badge counts index entries, not rendered rows, so it can read (3)
while one is on screen. That is how the already-indexed ANSI colors
entry behaves too; not changed here.
2026-08-08 20:44:43 +07:00
l0ng-ai 80a61aa494 fix(settings): carry the page to a search hit that is a section header
Only settings_row could claim the scroll anchor, so a query whose only
match on the page is a section heading moved nothing. Search "ansi" from
the bottom of Appearance and every row greys out while the one thing
still lit — ANSI colors — stays above the fold; "how shells work" and
"keybindings" are in the same position.

Section headers and intros now claim the anchor the same way, through a
first_hit_anchor helper the rows share, so the first match on the page
is the first match whatever kind of element it is.

Checked both paths on screen: "ansi" from the page bottom now lands on
the ANSI colors heading, and "smooth" still lands on its row.
2026-08-08 20:27:30 +07:00
l0ng-ai 0967015ff0 fix(settings): give the add-host button the tooltip its neighbour has
The + and the ⋯ sit side by side above the Hosts list at the same size
and weight; only the ⋯ named itself on hover. It now says "New host",
the same words the form it opens puts in its title.
2026-08-08 20:16:43 +07:00
l0ng-ai 2a276d1041 fix(sftp): make the cancel button match the one beside it
The transfer row draws its reveal button as an IconName::Close-family
icon inside a 24px hit target with a tooltip; the cancel button next to
it was a bare "✕" text label with neither. It is the only glyph-as-label
button in the app — every other close in this file and the eight others
across the UI use IconName::Close.

Verified by matching the sibling's construction, not on screen: showing
it needs a transfer in flight against a live SSH host.
2026-08-08 20:13:02 +07:00
l0ng-ai 5050a0eea6 fix(copy): stop two shell settings starting a sentence with "e.g."
"Executable name on PATH or an absolute path. e.g. zsh, fish, pwsh."
opens a sentence in lower case. The app's other two examples are
parenthetical — "(e.g. Ctrl-B then C)" — so put these two in the same
form. Chinese and Japanese already fold the example into the sentence.
2026-08-08 20:02:56 +07:00
l0ng-ai dd5744bbf7 fix(keybindings): name the switcher after the command, not the home tile
The keybindings page read ToggleSwitcher's name off the home screen's
string while TogglePalette read its own off the menu bar, so the one
action that appears in both places was the one whose name could drift.
Point it at CmdSwitchWorkspace, the name the palette shows, and pin it
with the assertion the page's other names already carry.
2026-08-08 19:56:42 +07:00
l0ng-ai fc09bb0a92 fix(settings): keep the theme editor's color labels in the current language
The editor stored each row's label as a String when it was built, so
Background, Foreground, Accent, Cursor, Selection and the sixteen ANSI
rows kept whatever language was set the moment you clicked "Duplicate
to edit" — switching language a few rows further down the same page
left them all behind.

Look the label up from the ThemeEdit at render time instead of caching
it. Verified by switching language with the editor open: the rows now
follow.
2026-08-08 19:54:21 +07:00
l0ng-ai beb0b76535 fix(settings): name the 16 ANSI colors instead of numbering them
The theme editor's palette rows read "Color 0" through "Color 15" —
the only labels on the settings page that never went through the
translation table, sitting directly under Background, Foreground,
Accent, Cursor and Selection, which all do.

They now read Black, Red, … Bright white, in the order a terminal
numbers the slots, so a theme author can see which entry `\e[31m` paints
without counting rows. Chinese and Japanese included.
2026-08-08 19:47:29 +07:00
l0ng-ai d72433a64f test(i18n): let the untranslated-string guard actually guard
KEPT_IN_ENGLISH is meant to list the handful of keys that read as
English in every locale on purpose. It had grown to 834 of the 989
keys — 84% of the table, fifteen of them listed twice — so the
"still reads the English" assertion was switched off for almost
everything it was written to protect.

Only 27 keys are genuinely English everywhere: protocol and command
names, product names, the three pure templates, each language on the
language list, and the shell / agent / ssh set. Trimmed to those, with
the reason recorded next to each group. A key added without a Chinese
or Japanese translation now fails the test — verified by regressing
one on purpose.
2026-08-08 19:46:05 +07:00
l0ng-ai c3037da55b fix(copy): stop the shells explainer running three "and"s together
"Closing a tab ends its shell, and Restart server and Quit and Stop
Server end all of them" hides two UI names inside a chain of "and"s.
Quote them and split the clause, which is what the Chinese and Japanese
versions of this paragraph already do.

Also: the serial comma in the two lists that were missing it, and name
the directory shell integration reports — "prompts, exit codes and
directory" never said which one.
2026-08-08 19:41:14 +07:00
l0ng-ai 933a34297e fix(copy): let every failure open with "Could not", as most already did
Thirteen failure lines start "Could not"; five started "Couldn't" or
"Can't" instead, so the editor, the diff panel and the settings page
each had their own voice for the same kind of bad news. Chinese and
Japanese already say one thing throughout. The SSH field's
"Can't parse host" now reads "Invalid host", matching the
"Invalid port" it sits beside.

Explanatory prose keeps its contractions; this is only the short
one-line failures.
2026-08-08 19:41:14 +07:00
l0ng-ai ba5f3820b5 fix(copy): punctuate the switcher's tab column the same way throughout
The five notes that can appear in the tab column all render through one
helper, at one size and one colour — but "No tab matches." ended in a
period and "No tabs in this workspace" did not. English and Chinese
close the sentence; Japanese, which leaves these short notes open
everywhere else in the app, now does so here too.
2026-08-08 19:41:05 +07:00
l0ng-ai 81651f1ce2 fix(copy): mark the two actions that open something, not do something
Switch Workspace and Command Palette both open a picker you then type
into, and the menu already calls them "Switch Workspace…" and
"Command Palette…". The home screen, the tab-strip tooltip and the
keybindings list used the bare names, so the same action read two ways
depending on where you found it. "Settings…" next to them on the home
screen already had its ellipsis.
2026-08-08 19:41:05 +07:00
l0ng-ai 11cf0a7f1d fix(i18n): re-translate the remote Files search box when the language changes
Every other input has its placeholder reapplied on a language switch.
The remote Files filter is built once with the app and was never in
that list, so it kept the old language until the next restart.
2026-08-08 19:40:56 +07:00
l0ng-ai 526584bcc7 fix(copy): end the last three search boxes the way the other six do
Six of the nine search and filter placeholders trail off with an
ellipsis; the terminal find bar, the workspace switcher and the remote
Files panel did not. The remote panel also carried its own "Search"
key next to the local file tree's "Search files…", so the two Files
panels disagreed about what their search box is called. Point the
remote one at the same key and drop the near-duplicate.
2026-08-08 19:40:52 +07:00
l0ng-ai ef1061acfb fix(files): say which file would not delete
The file tree's delete failure read "Delete failed" with the OS reason
appended — "Delete failed: Permission denied" — and never the name. In a
tree of files that is the first thing you need, which is exactly the reason
the editor's save failure stopped saying "Save failed" and started naming
its file.

The name is already in hand for the confirmation the delete opens with.
2026-08-08 19:20:25 +07:00
l0ng-ai 37659990ff fix(settings): put the last four settings into the search index
A row the index has no entry for is invisible to search, not just
unkeyworded: `section_match_count` only counts index entries, and
`settings_row` leaves a page alone entirely when that count is zero. So
searching "smooth" or "nightly" produced no badge, no navigation and no lit
row — the setting was simply not there.

Four rows were in that position. The whole Updates group on About — update
channel, check on launch, download in the background — which is the part of
that page people go to the search box for, and Smooth scrolling on
Terminal, sitting between Scroll speed and Focus follows mouse, both of
which were findable.

`previously_unsearchable_settings_are_findable` grows five queries that
each landed nowhere before.
2026-08-08 19:05:07 +07:00
l0ng-ai 8458cf24ab fix(chrome): print the chord on the panel toggles too
Two tiles in the title bar row name their shortcut on hover — Switch
Workspace ⇧⌘O, New Tab ⌘T — and the two beside them did not, though both
have one: the sidebar toggle is ⌘B and the detail panel is ⌘J. Four tiles
in a row, two teaching their key and two not.

All four go through `chord_hint` now, which prints nothing extra when an
action has no binding, so a rebind or an unbound action still reads right.
2026-08-08 18:51:14 +07:00
l0ng-ai bb4cc7793f fix(chrome): give the last three icon-only buttons their tooltips
Every other bare glyph in the app names itself on hover — the code panel's
close says "Back to Terminal (Esc)", the diff overlay's says "Close Diff
(Esc)", the SFTP tray's says "Dismiss", a workspace row's ellipsis says
"More". Three were still silent, and each one has a twin that is not:

- the themes panel's close, beside two closes that both name their key;
- the ellipsis on a machine group in the switcher, beside the identical
  ellipsis on a workspace row one line below it;
- the × that removes a port-forwarding rule, beside a + that says "Add
  rule".

The switcher's takes the string its twin already uses. The other two get
one each.
2026-08-08 18:48:17 +07:00
l0ng-ai 04c607bd55 fix(rename): stop a rename box turning typing into prepending
Rename Tab, Rename Workspace and the switcher's rename all opened with the
current name in the box and the caret at offset 0, so the first thing you
typed landed in front of it: renaming "tty7" to "build" got you "buildtty7"
until you noticed and went back. Only the file-tree rename had it right,
because it happens to fill the box with `set_value` rather than
`default_value` — and `set_value` puts the caret at the end of a
single-line input.

All three now go through one helper that does the same, with a test holding
the caret where it belongs. Selecting the name outright is what the platform
does and would be better still, but `InputState::select_all` is
`pub(super)` in the UI crate; that one wants a change over there.
2026-08-08 18:41:14 +07:00
l0ng-ai bce4a384cb fix(settings): file Compression with the algorithm lists it belongs to
`Algorithms` holds five lists — kex, cipher, mac, hostkey, compression —
and the SSH form showed four of them under Algorithms and the fifth under
Connection, next to the keepalives. There it was labelled just
"Compression", which in an ssh_config is a yes/no, over a description
reading "Comma-separated (blank = default)." — an instruction that made no
sense for the switch the label implied.

It moves up to its siblings and takes their naming: "Compression
algorithms", beside "Host-key algorithms". Connection is left with the two
keepalives and the connect timeout, which is what that word means here.
2026-08-08 17:57:09 +07:00
l0ng-ai bf7b35bdf2 fix(settings): give the SSH form one word for "nothing set"
The disclosure headers on a host print what is inside them — a jump host's
name, "2 rules opened with the connection", "algorithms / keepalive /
proxies / X11 / login scripts". Two of them print emptiness, and they did
not agree: Jump host said "(none)" and Port forwarding said "none", one
above the other.

They share the parenthesised one now, which is the one that cannot be
mistaken for a hostname someone typed. `SettingsNoneLower` had no other
caller and goes with it.
2026-08-08 17:49:32 +07:00
l0ng-ai 342b65ae5e fix(git): stop the sidebar count disagreeing with the diff it opens
The +N −N on a tab row is refreshed on an edge — the directory changing, a
command finishing, an agent turn ending, the window coming back — and never
polled. That is the right shape for it, but it means a working tree edited
from anywhere else keeps the old number until one of those happens. Click
the number and the overlay reads the tree there and then: the row said
+27 −8 while the overlay it had just opened said +3 −1.

A diff read is a fresher answer to the same question, so it now hands its
totals back to the status cache. The Changes panel feeds the same path, so
watching a diff shrink there keeps the row honest too. Only the numbers
move — the branch, the root and the probe's own schedule are untouched, and
a diff for a repo nothing has probed is dropped rather than inventing a row
with no branch on it.

The totals are exact even when the diff bodies were truncated by the
repo-wide budget, which `repo_wide_budget_keeps_totals_exact` holds.
2026-08-08 17:26:57 +07:00
l0ng-ai d1f61e109f fix(editor): give the Markdown preview a scrollbar
The editor itself gets one from `Input`; the rendered-Markdown pane beside
it scrolled a whole README with nothing to say how far down it was. It now
carries the same shared bar as the sidebar, the file tree, the right panel
and the settings pages, on a handle kept per open file — so switching away
and back lands where you were reading.

`with_vertical_scrollbar` grows by `flex_1`, which needs a column with a
height of its own around it; dropped straight into the overlay it sizes to
its content and the pane stops scrolling altogether. That requirement is
now written on the helper, where the next caller will read it.
2026-08-08 17:03:59 +07:00
l0ng-ai 90e5069466 fix(menus): stop the View menu teaching Zoom Pane the wrong key
Zoom Pane is ⇧⌘↵. The View menu drew it as ⇧⌘E, and ⇧⌘E is Code Panel —
press what the menu says and the code panel opens instead.

gpui's macOS menu builder maps a keystroke to an AppKit key equivalent
through a table that has no entry for `enter`; the fallback hands AppKit
the literal string "enter", and AppKit takes the "e". Every other chord in
these menus is a single character or a key that table knows, so Zoom Pane
was the only item it could happen to.

The item leaves the View menu, the way Enter Full Screen did above it for
its own reason. Zoom Pane is still in ⌘P, in a pane's own context menu —
which we draw ourselves and which prints ⇧⌘↵ correctly — and on the
Keybindings page.
2026-08-08 16:38:53 +07:00