Commit Graph
806 Commits
Author SHA1 Message Date
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 a6beb6d8b2 docs: give the agent count the exact number the code has
CLIAgent::ALL is [CLIAgent; 17], so "~17 CLIs" and "and ~10 more" were
hedging about a constant. Six agents are named in the features list, so
the remainder is exactly 11.
2026-08-08 20:08:37 +07:00
l0ng-ai 4e4057278c docs(features): point the settings paths at sections that exist
Five of the paths named a page and section pair that was never there:
tab completion and history search moved to Input → Prompt, copy on
select and smart selection to Input → Selection & clipboard, and
opacity/blur sit under Appearance → Transparency, not a "Window"
section — that name belongs to a heading on the Window & Tabs page, so
following the old path landed you somewhere real and wrong.

The Chinese page also quoted labels in English that its reader sees in
Chinese — Appearance, Window, Follow theme, Scratch, Copy Working
Directory, Settings → Keybindings — and shortened 窗口与标签页 to
窗口与标签. All checked against the strings the app ships.
2026-08-08 20:07:07 +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 cf61fc900c docs(features): quote the tray item the app actually shows
1108707 settled on one name for the background process — server, in the
app, the menu bar and the README — and the features reference kept the old
one in the two places it appears: a tray menu item quoted as *Quit and Stop
Daemon*, which no build has said for some time, and a performance note about
what "the daemon" buffers.

The menu item is quoted as it reads now, ellipsis and all, and in Chinese
the Chinese label rather than the English one.
2026-08-08 19:27:28 +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
l0ng-ai 6f72388d9a fix(copy): mark New Worktree Tab as the prompt it opens
Everything here that asks before it acts ends in an ellipsis — Rename Tab…,
Delete Workspace…, Add SSH Host…, Save "…" as profile… — in all three
locales. New Worktree Tab never does anything on its own either: it probes
the repo and opens the worktree sheet for a branch and a path. It was the
one such command written as though it just went ahead.
2026-08-08 16:20:13 +07:00
l0ng-ai b8f1b610ec fix(i18n): end Japanese descriptions the way the other eighty do
Japanese UI copy here drops the closing 。 on a setting's description and
keeps the ones between its sentences — 26 multi-sentence descriptions are
written that way, and 80 in total. Three were not: the update channel, the
background download, and the stale-server note each carried a final 。 that
nothing else on the page has.
2026-08-08 16:15:55 +07:00
l0ng-ai 28b5a3c106 fix(i18n): call scrollback one thing in Chinese
The Scrolling settings row is 回滚行数; the pane menu and the command
palette both said "清除 scrollback" — the same word, once translated and
once not, two surfaces apart. Japanese translates both. c32c191 already
drew the line for this locale: zh keeps English for the tokens you type
(shell, PATH, SSH), and a menu item is not one of those.
2026-08-08 16:12:18 +07:00
l0ng-ai 2387388114 fix(completion): say when a candidate's description was cut
The completion menu stops at 480px and clipped whatever ran past it, mid
word and without a mark: git's menu offered "Move or rename a file, a
directory, or a symli" and "Create an empty Git repository or reinitiali".

The menu is monospaced, so what fits is arithmetic: the row spends its
width on the padding, the kind icon, and the name, and the description gets
the cells that are left. Anything longer is elided with the ellipsis inside
that budget, so the cut lands on the menu's edge and says it happened.

`min_w_0` + `truncate` was the obvious reach and the wrong one — it takes
the row's contribution to the container's width down to nothing, and the
whole menu collapses to its 120px floor.
2026-08-08 15:57:21 +07:00
l0ng-ai 4eebd2cd34 fix(palette): keep Tab inside the command palette
The palette is walked with the arrows and run with Enter; Tab has no job
there. Root's focus walker had one for it anyway, and sent it out of the
modal onto whichever chrome tile sat behind the scrim — the New Tab plus,
the panel toggle — which lit up with a focus ring the palette had no way to
take back.

Tab and Shift-Tab bind to `NoAction` in the palette's own context, which is
deeper than Root's and stops the search before the walker is reached.
2026-08-08 15:57:21 +07:00
l0ng-ai 8286317493 fix(switcher): make Tab cross to the tab column instead of chasing a button
The panel's footer says Tab is the way across once a query is in the box,
and it never was. gpui-component's Root binds `tab` to its focus walker,
gpui dispatches bindings *before* key listeners, so `on_switcher_key` never
saw the key: Tab put a blue focus ring on the ellipsis button in the
panel's own header and left the columns where they were.

Tab and Shift-Tab now bind to the panel's own key context, which sits
deeper in the dispatch path than Root's — the same shape the terminal
already uses to keep Tab for the shell.

Crossing over only helps if you can see where the cursor went, and you
could not: both columns keep their rows inside one child element, and
`ScrollHandle::scroll_to_item` indexes a scroll's *direct* children, so it
could only ever find item 0. Walking a long tab list quietly left the
selection below the fold. The selected row now wears a `ScrollAnchor`, and
stepping the cursor scrolls to it — in either column.
2026-08-08 15:57:21 +07:00
l0ng-ai 270598e1f1 fix(settings): give the settings pages the scrollbar the rest of the app has
The tab sidebar, the file tree, the right panel and the SFTP browser all
run their scroll area through `with_vertical_scrollbar`. The settings
pages — the longest scrolling surfaces tty7 has, several viewports of them
on Appearance and Terminal — had no bar at all: no thumb, no sense of how
far down the page you were or how much was left. The SSH host list, the SSH
form and the theme picker's list were the same.

All four now carry the shared bar. It follows the OS "show scroll bars"
preference like every other one, so nothing appears for anyone who asked
for scrollbars to stay hidden.

The scroll areas move inside a wrapper that holds the constraints they used
to hold themselves, and two of those do not survive the move. `min_w_0` on
a column child means the *cross* axis, and carrying it inside let the SSH
form's label column shrink to nothing while the toggles stayed put; it
belongs on the wrapper, which is still the row item it always was. And the
wrapper has to take its width explicitly rather than by stretching, or the
`w_full` inside has no width to be a percentage of — which is how the
settings reading column lost its 640px cap on the Chinese page, exactly the
way 00ad4eb first found it.
2026-08-08 15:57:21 +07:00
l0ng-ai f77ad89b8b fix(chrome): stop hovering a tile into looking selected
`chrome_tile_variant_for` passed `sidebar_accent` to both `hover` and
`active`, and `sidebar_accent` *is* the sidebar surface's selected step. So
a hovered tile wore the exact fill of a selected one: with the right panel
open, pointing at Changes made it and the current Files tab read as two
current tabs, told apart only by a shade of glyph.

The palette already derives the step below it — 1.18 from the surface where
selected is 1.30, with a test holding them apart — and hover now takes it.
A selected button never renders the hover style, so the selected tiles are
untouched; every other tile simply stops overstating a pointer.
2026-08-08 14:12:51 +07:00