Commit Graph
4 Commits
Author SHA1 Message Date
jaap-jan 5b32c76c95 Android: the redesign — a Vaults tab with hosts, keychain and snippets, card lists, a keychain that can add, and files that act on what is chosen
ci / build and test (push) Successful in 3m49s
ci / android head (push) Successful in 3m55s
ci / api image (push) Successful in 6s
ci / desktop nightly (push) Successful in 1m22s
The bottom bar is Vaults · Connections · Settings, drawn as Material 3 pills with icons from the embedded
Material font and a count of open sessions on Connections, which replaces the strip of shells above the
bar. Vaults is the open vault: its header carries the name as a switch to the vaults screen, the sync
light and a lock icon, and a Hosts · Keychain · Snippets segmented control sits under it. The keychain
and snippets move there from Settings (IsVaultSurface lights the tab; IsMoreSurface no longer lists
them), and back from either goes to the hosts. Settings becomes an account card and grouped sections:
security, vaults and history, files, this app.

Host rows are cards with a terminal tile and the live dot on its corner. The connect sheet names the
host and its address, labels its box, focuses it as it opens, reveals the password on request, and puts
CANCEL left and CONNECT right; every two-button row on the head now confirms on the right. While a
hosts-screen sheet is up the header and bar are dimmed too and a tap on them lowers it, and the bar
stands down while the software keyboard is up.

The keychain gains a floating + and a sheet of the desktop's five adds (generate a key, paste a key, a
password, a bucket, a tag) over the editors VaultViewModel already had, drawn as a full-screen page with
labels above the boxes; a chosen item gets EDIT and COPY PUBLIC KEY beside MOVE and DELETE. Snippets get
a floating + and cards that say RUNS or INSERTS. Files trade the seven-button block for a selection bar
(save, and a ⋮ sheet with rename, move, permissions and a red delete), folder and file tiles, an upload
button, and header actions for a terminal on the same host, refresh and close; a terminal's + sheet
opens SFTP on that terminal's own host.

The theme takes the audit's visual findings: Fluent's ClipToBounds is cleared on glowing buttons so the
glow is no longer cut to a square (the desktop's Button.accent too), prose is Montserrat with mono kept
for addresses and code, disabled secondary buttons are quieter than enabled ones, Fluent's dropdown,
checkbox, switch, focus and selection brushes come from the palette, touch no longer leaves hover fills
behind, and lists, nav entries and icon buttons carry names for TalkBack.

Builds clean; checked on the emulator end to end: unlock, the three vault views, adding and deleting a
password, connecting through the new sheet, files on the terminal's host with the selection bar and
its sheet. App tests 727 of 733 pass with 5 skipped; the one failure, TheCountsAPhoneShows_
FollowEveryForwardsState, passes three runs of three alone and is untouched here. Layout tests pass.
2026-10-04 15:50:09 +02:00
jaap-jan b931a06998 Repaint the phone's chrome, radii and accent to the v5 vocabulary 2026-08-08 15:22:04 +02:00
jaap-jan cddfeb1f55 Keep the phone's nav under Connections when nothing is running
The chrome stands down for a shell, and it was standing down for the whole
terminal surface. Those parted company when that surface gained a connect page:
with no tabs open it draws a box, a CONNECT button and the machines connected to
before, which is a page in everything but which enum it is in. A third of the
display is worth giving to a shell and is not worth giving to that. Worse, it is
the one screen somebody arrives at by closing their last tab — so the state the
collapsed bar was most likely to be seen in was the state where it left the
system back gesture as the only route to Hosts or Settings.

So RefreshChrome reads one more question. IsTerminalSurface with no tabs joins
the pages in both flags, which keeps the rail and the bar in step: above 600dp
the rail is the bar, and fixing only the narrow layout would leave an unfolded
device on the same screen with the same nothing. The vault header is deliberately
not part of it. The surface draws its own bar with back and the +, and a header
above that is the second row of chrome this head exists to avoid.

The Connections entry lights for the first time, on IsTerminalSurface. It was
left unbound on the argument that the bar was never drawn while that surface was
up, so a lit state was unreachable — that argument is now false, and the flag is
unambiguous on a control that is only drawn in two situations: false on every
page, true on the connect page, and never read while a shell is showing. A bar
sitting under a screen it does not point at is the entry looking broken instead.

Nothing here is testable on this head — the phone's rectangles have no coverage,
for the reasons Phase 8 of manual-checks records — so 11.7 gains the check that
the bar is there with Connections lit, and 11.1 keeps the one that it is gone
with a shell up, which is the half that pays for the arrangement.
2026-08-06 07:38:31 +02:00
jaap-jan 5cb361ea13 Lay the phone out like the desktop when the surface is not a phone
ci / build and test (push) Successful in 2m54s
ci / android head (push) Failing after 3m15s
ci / api image (push) Successful in 46s
Three destinations in a bar and everything else behind SETTINGS is the right
shape at 360dp, where a fourth entry costs the width of the three that are there.
On a tablet, an unfolded foldable or a landscape phone it is the wrong one: there
is room for every destination at once, and the hub becomes an extra tap between
somebody and a screen they can already see space for.

So at 600dp — Android's own boundary between a compact window and a medium one,
in the density-independent units Avalonia lays out in — the bar stands down and
PhoneRail takes the left edge with all nine on it. It is the desktop's NavRail
arrangement rather than its file: the two heads cannot share a view, and this one
draws the phone's destination set with the phone's palette and touch targets.

The flags are computed in code rather than assembled in the markup because none
of them is a single question any more, and Avalonia's bindings have no "and" —
and the header's condition is an "or", which not even a wrapper can express. That
header is the one worth reading twice: narrow it stands down behind SETTINGS, so
the hub's screens can draw their own; wide there is no hub to be behind, so it
stays up everywhere. Losing it on the keychain would be losing the only LOCK
button on the surface.

Removing the hub means removing the routes into it, and there were four kinds.
The rail has no SETTINGS entry, because that screen is a menu of the rail. The
five back arrows in the screens under it are hidden, since an arrow to a screen
the layout removed is the one control on a header that leads nowhere. The system
back gesture goes to Hosts instead. And unfolding while sitting on the hub moves
to Hosts, rather than leaving somebody on a list of things now visible beside it.

One bug fixed on the way: OnBodyResized returned early unless the keyboard was
open, so a foldable would have opened to a phone layout until somebody typed
something. The chrome is refreshed first and unconditionally; the early return
belongs to the older job below it.

What this does not do is use the width *inside* a screen — the host list is one
column at any size. Two columns needs the row model to change, because that list
is headings and hosts in one sequence and a heading has to span, and that model
is shared with the desktop. Check 8.1 walks the rail; nothing here is covered by
a test, for the reason 8.0 exists.
2026-08-05 20:50:18 +02:00