Commit Graph
769 Commits
Author SHA1 Message Date
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
l0ng-ai d3b7c24529 fix(chrome): let the New Tab button say what it is
Hovering the tiles either side of it names them — Switch Workspace ⇧⌘O,
More, Hide Sidebar — and the plus between them said nothing, in all three
places it appears: the sidebar head, the top strip, and the collapsed rail.
It now names itself and its chord, from the one helper all three share.

`switcher_hint` was that helper for exactly one tooltip; it becomes
`chord_hint(what, action)` so the second caller does not copy it.
2026-08-08 14:03:14 +07:00
l0ng-ai 3631c534ac fix(settings): take the page to what the search found
Typing in the settings search dims the page and lights up the rows that
match, and the nav badges count them per section. But nothing moved the
page. Search "cursor" and Appearance says (2) while the screen shows a
greyed-out Theme section: both matches live under Cursor, most of a page
below the fold, with no sign they are down there. On Terminal the single
match sat right on the bottom edge.

The content pane now carries a ScrollHandle, and the first matching row on
the page claims a ScrollAnchor on it. Whenever the query changes, or a
section is opened with a query already live, the page scrolls that first
match into view — one row per page, so the view never travels past matches
above it. Clearing the query leaves the page where the reader left it.
2026-08-08 13:57:07 +07:00
l0ng-ai 943d69501b fix(settings): start every SSH row's title on one column
The host rows carry a liveness dot in front of the title; Defaults, which
has no session to be live, carried nothing and skipped the space too. Its
title and subtitle therefore began 14px to the left of every host title
under them — the one row at the top of the list, hanging off the column the
rest of the list holds.

The gutter is now unconditional and simply stays empty on the rows that
have no dot to put in it. The group headers were 2px off the same column
for the same kind of reason: 6 + 10 + 4 instead of 8 + 6 + 8. Their inset
becomes the 8px the rows already hover with, which lands them on it.
2026-08-08 13:41:28 +07:00
l0ng-ai 9a07616e82 style: put the branch back under rustfmt
`cargo fmt --check` is the first job CI runs, and main passes it. Three
files on this branch no longer did: a long method chain in the settings
shell picker, the card-width clamp the switcher gained when it learned to
fit its window, and a Chinese plural that shrank below the line limit when
its backticks came off. None of it changes behaviour — it is what rustfmt
would have written, restored so a merge does not fail on formatting.
2026-08-08 13:27:49 +07:00
l0ng-ai a24fd0c5a6 fix(tabs): let the tab menu teach the chords the pane menu already does
Right-clicking a pane offers "Split Right ⌘D", "Close Pane / Tab ⌘W" and
six more with their shortcuts printed, because those items are built with
`menu(label, action)` and PopupMenu looks the binding up from the action.
Right-clicking the tab that owns the pane offered the same commands as bare
labels — every item there is built from a click handler instead, since it
has to act on that tab rather than the active one, and a handler carries no
action to look up.

The items keep their handlers and now carry the matching action purely so
the chord can be found: `confirm` prefers the handler whenever both are
set, so nothing about what a click does changes. Split Right, Split Down
and Close Tab pick up the chords they have always had; the rest are ready
for the day someone binds them.
2026-08-08 13:26:52 +07:00
l0ng-ai b1b12ae831 fix(home): stop offering chords the empty window cannot honour
The home page is what a window shows with no tabs open, and it listed
Split Right and Split Down among the things you can do from there. Both
ask the active tab for a pane to split and return without a word when
there is none, so the two chords the screen taught did nothing on the one
screen that taught them. Reopen Closed Tab had the same shape whenever the
closed stack was empty.

The splits leave the list, and Reopen Closed Tab appears only while there
is something to reopen. A fresh window now offers four rows, and every one
of them works.
2026-08-08 13:26:52 +07:00
l0ng-ai e893ec1dd1 fix(copy): stop printing Markdown backticks at the user
Nine strings wrapped a command in backticks — `tty7`, `git diff`, `git
status`, `{alias}` — and nothing in this app renders Markdown, so the
marks landed on screen as literal characters. The Settings label read
"Install the `tty7` command on PATH", and its description wrapped mid-token
and left a lone backtick opening a line.

Everywhere else the same copy writes known_hosts, ~/.ssh/config and
"git diff →" bare, so the backticks go and the minority follows the
majority. The one that quotes a name — the missing ~/.ssh/config alias —
takes the quotes the rest of the app already uses for a name: "..." in
English, “...” in Chinese.
2026-08-08 13:26:52 +07:00
l0ng-ai 3ae1a931e8 fix(settings): give the shell picker's chevron the 24px target the rest of the chrome has
An icon-only xsmall button is a 20x20 box, under the 24x24 desktop floor
in WCAG 2.5.8 that hit_target exists to hold. The glyph is unchanged; only
the box it sits in grows.
2026-08-08 12:36:56 +07:00
l0ng-ai eefb3ac52b fix(switcher): fit the card to the window it floats over
The card is fixed-size on purpose — one that resized under the pointer
while the tab column changed length would be unusable — but 840x420 was
fixed in the absolute sense too, so on a small window it hung off both
edges with its search box reading "orkspaces, tabs and machines". It now
takes what the window has when that is less than what it wants, and is
untouched above that.
2026-08-08 12:21:09 +07:00
l0ng-ai e26a02213b fix(window): give the window a floor to stand on
Nothing stopped tty7 being dragged down to a couple of hundred pixels,
and below roughly 700 the chrome stops being chrome: the sidebar sits at
its 180px floor with barely forty columns beside it, and the overlays the
app drops on top have nowhere to land. 720x520 is the smallest window it
still holds its shape in.
2026-08-08 12:21:09 +07:00
l0ng-ai 78501b86aa fix(settings): stack a row instead of squeezing its label to a letter per line
The control never shrinks and the label column must keep min_w_0 or long
descriptions stop wrapping, so on a narrow pane the label was squeezed to
nothing: at 600px the Terminal page rendered "Program" as a vertical
column of single letters, and the SSH form did the same below about
1000px, which 4275bb1 left open as the real remaining bug.

flex_wrap was the wrong answer — it let the label column size to its own
description, which then ran out past the row on every wide page. Measure
instead: render_settings knows the viewport and which page it is drawing,
so it can say how much width a row will actually get, and the row lays
itself out side by side or stacked from that. Nothing moves above the
breakpoint.
2026-08-08 12:09:58 +07:00
l0ng-ai e6240e899c fix(settings): let the Keybindings preset row wrap instead of shoving its control off
The Preset and Prefix rows on the Keybindings page are hand-rolled rather
than built from settings_row, and they were missing the one thing that
makes every other row survive a narrow window: a label column allowed to
shrink. At 560px the tmux description ran off the right edge and took the
Default/tmux control with it. Same gap_8 as the shared helper, so nothing
moves on a wide window.
2026-08-08 11:57:11 +07:00
l0ng-ai 5e30d43152 fix(i18n): quote names in Chinese the way Chinese quotes them
Four Chinese descriptions already used “ ”, but the nineteen strings that
quote a name — a file about to be deleted, a machine about to be
restarted — used ASCII " ", which in a run of Chinese text reads as two
stray marks. Japanese quotes with 「」 throughout; Chinese now does the
same with “ ”, and drops the space that a full-width quote does not need.
2026-08-08 11:47:20 +07:00
l0ng-ai c32c191d7b fix(i18n): translate the Scrollback label into Chinese
It was the only English row label in a Chinese Scrolling section, sitting
between 滚动速度 and 平滑滚动 with its own description already in Chinese
— Japanese had translated it, Chinese had not. zh keeps English for the
tokens you type (shell, PATH, SSH); a row label is not one of those.
2026-08-08 11:41:55 +07:00
l0ng-ai 00ad4eb62f fix(settings): keep the theme card inside the reading column
On the Chinese and Japanese pages the theme card ran the full width of
the window while every other row, and the dividers between them, stopped
at 640 — and its "Change theme" chevron, which the card pushes to its
right edge, ended up stranded in the middle. The column carries
`w_full` under `max_w(640)`, and with nothing definite to resolve the
percentage against it falls back to what the content measures, where the
card's own row wins over the cap. Give the padding box a width.
2026-08-08 11:36:02 +07:00
l0ng-ai 1cffdd1199 fix(settings): let Escape close the theme picker before the page
Escape closed the whole Settings page even with the theme picker open, so
a panel opened a moment earlier went with it and you had to walk back to
Appearance to try again. Peel one layer at a time: the picker first, then
the page.
2026-08-08 11:18:25 +07:00
l0ng-ai 6142a00c7b fix(settings): put the caret in the theme picker's search when it opens
The panel leads with a Search themes box and opened with the caret still
back on the page, so the first thing typed at a panel whose whole job is
picking one of nine themes went nowhere. Closing hands the caret back to
the settings search rather than leaving it on a box that is no longer
drawn.
2026-08-08 11:13:17 +07:00
l0ng-ai b02a0a4d27 fix(tabs): let a shortened tab name say what it really is
A tab title that is a path is elided to its last three segments, capped
at forty characters, and then truncated again by the chip — so a tab
could read "…/src/daemon/install" with no way to find out which checkout
that was. Name it on hover, in both the strip and the sidebar, and only
when something was actually dropped so tabs that already show their whole
name stay quiet under the pointer.
2026-08-08 10:56:17 +07:00
l0ng-ai 85bf4d91ab fix(theme): write button labels and panel headings in the preset's ink
button_foreground and secondary_foreground both resolve from
gpui-component's stock foreground, and apply_theme never overrode them —
so a button's label and the detail panel's section headings were the only
text in the window not written in the theme's own colour. On Rose Pine
Dawn that is near-black next to the preset's #575279 everywhere else.
2026-08-08 10:42:25 +07:00
l0ng-ai cbb9dc4016 fix(tabs): keep the active tab and the New Tab button on the strip
A chip cannot shrink below 100px and the row simply clips, so past six or
seven tabs the ones at the end were not drawn — including, right after
⌘T, the tab that had just been opened and made active. The strip now
draws the run of chips that holds the active one, sliding by as little as
it takes.

The New Tab button was a second casualty, and only when the detail panel
was open: on macOS the panel draws its own title-bar chrome, so the strip
stops at the panel's edge, but it was still sized to the whole viewport
and reserved room for chrome it no longer holds. Measured at 1000px with
the panel open: four chips and a + where there were six chips and no way
to open a seventh.
2026-08-08 10:14:55 +07:00
l0ng-ai 63a4f0da6e fix(theme): light the splitters up in the theme's accent
drag_border was never set, so the sidebar and panel splitters, the pane
divider and the drop target for a dragged-in file all lit up in
gpui-component's stock blue — the same blue under all nine presets.
Measured on Rose Pine Dawn: the handle went from #656361 to #907aa9, the
preset's own accent, which is already what the focus ring and an on
switch use.
2026-08-08 09:57:22 +07:00
l0ng-ai afd84a53fd fix(prompts): put the action on the right in the sheets the app draws itself
confirm_answers settled this for every native alert, but the four sheets
tty7 renders in-window kept the mirrored order: the SSH password, key
passphrase and keyboard-interactive sheets, the two host-key sheets and
the worktree sheet all led with the action, left-aligned, while the SFTP
rename and the port-forward form beside them already right-aligned with
Cancel first. On the changed-host-key sheet that also moves Abort — which
was already the emphasized answer — to where the eye lands last.
2026-08-08 09:40:40 +07:00
l0ng-ai f3f0c4710f fix(theme): highlight the command line in the theme's own colours
kind_color() reaches for theme.green, .cyan, .blue, .yellow and .magenta,
and apply_theme never set any of them — so every dark preset shared one
stock ramp and every light preset another, and the line you were typing
kept a palette the output right above it had already left behind. Point
them at the preset's ANSI ramp, cleared for legibility through the same
floor the semantic inks already use.
2026-08-08 09:33:26 +07:00
l0ng-ai 2cc3ddf4b6 fix(theme): hand the preset's ink to every tooltip and menu
apply_theme set tokens.popover_foreground but never the field beside it,
and that field is the one gpui-component reads for tooltips, dropdown
menus and date pickers — they all kept the stock near-white and came out
brighter than the window they float over. It also inverted the terminal's
own menus: the completion and Ctrl+R rows painted resting text with it
and the selected row with the preset's foreground, so the selected row
was the dimmest line in the list. Wire the field up, and let those rows
say muted_foreground, which is what they meant and what their icons
already used.
2026-08-08 09:21:51 +07:00
l0ng-ai c553824444 fix(settings): let Shell → Program pick from the shells tty7 already found
tty7 detects the installed shells and lists them on the new-tab button,
but the Program field asked you to type one from memory: the same choice
was a menu in one place and a blind text field in the other. Put the
inventory behind a chevron inside the field — login shell, then every
detected shell, with a tick on the one in force. The field stays
editable, because a shell tty7 did not find still has to be reachable by
path.
2026-08-08 09:14:19 +07:00
l0ng-ai 3a0ab46218 fix(settings): start the SSH empty-list note on the column the rows use
"No saved hosts yet." and the no-match note sat 8px left of the Defaults
row above them, because they carried no horizontal padding of their own.
2026-08-08 09:02:52 +07:00
l0ng-ai 3f4d378582 fix(settings): give the slider knob the switch's knob
slider_thumb was left at gpui-component's primary_foreground, which on a
dark theme paints a black disc on a dark page while the switch beside it
carries a light one. Both are the same handle; use the same colour.
2026-08-08 09:02:52 +07:00
l0ng-ai aab3946928 fix(settings): sentence-case the labels on the settings pages
Settings speaks in sentence case — every row label, description, section
header and segmented option — but one segmented option said "When
Unfocused", and the Updates section carried "Check Now" and "Cancel
Download" beside "Restore all defaults" and "Restart server…". A single
button even flipped from "Check Now" to "Checking for updates…" while
it worked. The menu bar and the palette keep title case.
2026-08-08 08:53:04 +07:00
l0ng-ai 1108707bc5 fix(copy): call the background server one name, everywhere
Four update strings called it a service — including a "Restart Service"
button that fires the same action, on the same page, as "Restart
server…" two sections below. Every other line in the app, the menu bar
and the README say server. Fold the two buttons into one string and say
server in English, Chinese and Japanese.
2026-08-08 08:52:35 +07:00
l0ng-ai e80b7c5f80 fix(settings): give the About page the section headers the other pages use
How shells work, Updates and Server were hand-rolled headings — text_sm
at medium weight — so the one page a new user is most likely to open
looked like a different app from Terminal or Window & Tabs. Run them
through section_header.
2026-08-08 08:52:06 +07:00
l0ng-ai 7d443b2149 fix(settings): build the command-line setting out of the same pieces as the rest
It was the one hand-rolled row on the page: a quieter heading than every
other section, a full-width description, and the only switch in Settings
standing to the left of its own label. It was also the one row the
settings search could neither highlight nor dim, so searching "cli"
badged Agents with (2) and showed one match. Run it through
section_header and settings_row, and point the index entry at the label
the row actually renders.
2026-08-08 08:42:08 +07:00
l0ng-ai e17bebb7e2 fix(chrome): give the last icon-only buttons the tooltips their twins have
Every icon-only control in the chrome names itself on hover — except the
settings close, the two SSH ellipsis menus, the switcher row menu, and
the tab close in both the strip and the sidebar. Same glyphs, same jobs,
same words as the buttons beside them.
2026-08-08 08:35:50 +07:00
l0ng-ai a4f6fdc655 fix(settings): colour a filled slider like an on switch
slider_bar falls back to tokens.primary, the near-black we give primary
buttons, so on one settings page a set slider and an on switch disagreed
about what a set value looks like. Both are the same statement; give
them the same colour.
2026-08-08 08:35:50 +07:00
l0ng-ai a8577fbe5c fix(palette): line section headers up with the rows they head
A section header carried px(11) while a row is a ListItem inset by mx(5)
whose own padding is px_3, so every label under a header started 6px to
its right. Give the header the sum, from named constants so the two
cannot drift apart again.
2026-08-08 08:35:50 +07:00
l0ng-ai 4275bb1aef Revert the settings row wrap that broke every long description
5423301 gave `settings_row` `flex_wrap()` and swapped the label column's
`min_w_0` for `flex_1` + a 200px floor, so the SSH host form would stack
instead of overflowing in a narrow pane. On every other settings page that
stopped long descriptions wrapping at all: the text ran under its own
control and out past the row it belongs to, which is plainly worse than
the case it was meant to fix.

The row goes back to what it was. `#ssh-detail` keeps its `min_w_0` — that
half of the commit is what actually fixed the 1200px overflow, and it is
independent of the row.

The SSH form still degrades below roughly 1000px, where the label column
squeezes to a word per line. That is the real remaining bug and it needs a
different answer than wrapping this row.
2026-08-08 09:00:47 +08:00
l0ng-ai 56075221b5 fix(panel): size the Session label column to the labels it holds
The key column was a hardcoded 46px, which fits "cwd" and "shell" and
little else. In English "changes" folded mid-word into "change / s". In
Chinese and Japanese almost every label wrapped — ja "作業ディレクトリ" is
eight glyphs in a column sized for three ASCII characters.

It now measures the labels actually on screen through the text system and
takes the widest, clamped to 46–108px so one long Japanese label cannot
eat the panel. The labels are `whitespace_nowrap`, so anything past the
clamp runs into the gap rather than folding.

Checked on screen in English and Chinese: "changes" and "工作目录" each sit
on one line, and the values still line up.
2026-08-08 08:43:30 +08:00
l0ng-ai 8d57e07422 fix(search): make a match visible on a light theme
Search matches were painted as the terminal's selection tint at 0.32
alpha. That tint is itself only `mix(bg, fg, 0.24)`, so the blend landed
about 1.17:1 against the background — a 7% shift, in every theme. On the
built-in Light theme the matches were a barely-there grey smudge on white;
on dark ones only the glyph colour gave them away. "3/3" told you there
were three, and you still had to hunt for them.

The wash is now opaque and solved for a contrast ratio instead of a fixed
alpha, so it keeps the same weight whatever the theme's two colours are:

| | Before | After |
|---|---|---|
| Other matches | fixed 0.32 alpha ≈ 1.17:1 | `MATCH_WASH` = 1.45:1 |
| Current match | fixed 0.85 alpha | `CURRENT_MATCH_WASH` = 2.1:1, plus the caret-coloured border it already had |

`presets::wash(surface, tint, target)` bisects the blend the same way
`bisect_contrast` walks a colour, and falls back to `legible_ink` for a
theme whose selection colour is so close to its background that no opacity
reaches the target.

These are non-text ratios on purpose — enough to spot the block, not so
much that the glyph on top of it stops reading.

Verified on screen in both built-in themes: all three matches are now
findable at a glance, and the current one still stands out from the other
two.
2026-08-08 08:29:59 +08:00