Commit Graph
795 Commits
Author SHA1 Message Date
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
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