From 422d5ca10ec02b62eb8a71f3ad548ebf28615f5b Mon Sep 17 00:00:00 2001 From: Jaap-Jan de Wit | DodoTech Date: Sat, 8 Aug 2026 00:49:59 +0200 Subject: [PATCH] Record v5b in the design-import log, and true up the manual checks --- docs/design-import-gaps.md | 68 ++++++++++++++++++++++++++++++++++++-- docs/manual-checks.md | 63 ++++++++++++++++++----------------- 2 files changed, 99 insertions(+), 32 deletions(-) diff --git a/docs/design-import-gaps.md b/docs/design-import-gaps.md index 759925f..d02c27e 100644 --- a/docs/design-import-gaps.md +++ b/docs/design-import-gaps.md @@ -69,7 +69,7 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference > labelled sidebar and the chrome went from 72 tall to 86, so `880x560` became `1016x574` — leaving every > screen the same `826x464` it was designed against. Four of the tables stop fitting at 690 wide, so > widening the sidebar without widening the window would have broken them where the layout suite was not -> looking. +> looking. **v5b grows it again**, to `1081x583` — see that section, below. > > **Buckets became a destination** rather than a toggle inside the files screen, matching the phone: the > `HOST` / `BUCKET` pair is gone and `ShellScreen.Buckets` draws the same `TransfersScreen` with the other @@ -102,6 +102,14 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference > each get the full window width instead of `826`. See `MainWindowViewModel.IsVaultsTab` for why the tab is > a page test rather than a fourth `ShellSurface`. > +> **v5b reverses the paragraph above, and says so rather than leaving it to be found out of date.** The tab +> strip this paragraph describes is retired — see the v5b section, below — and with it the rule that made the +> rail conditional at all: the switcher it drew moved onto the nav rail's own head as three segments, the +> rail stopped collapsing for any of them, and neither SFTP, S3 nor a terminal gets the window's full width +> any longer. `IsVaultsTab` outlived the strip essentially unchanged — it is still a page test rather than a +> read of a `ShellSurface`, now over which of the rail's own destinations is showing rather than which of +> three tabs was. +> > **The hosts screen became a grid of cards** — groups above, hosts below — and the 268-pixel host sidebar > went with it. That column was choosing among forty machines *and* editing one of them at two-thirds > width; the grid took the first job at full width and a 304-pixel right-hand drawer took the second. The @@ -243,6 +251,62 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference > | The QUICK ACCESS hint's claim that pins live in a sidebar | "Pinned folders appear above the terminal for this host." — no sidebar exists on this screen for the sentence to point at, so the shipped hint says where they actually draw. | > | The mock's "Saving to **DodoTech ▾** vault" subtitle, with a picker's chevron inside a sentence | The pre-existing `DrawerSubtitle` wording — the vault's name alone, unchanged by this pass. A chevron inside running text implies the text itself is the control, which it is not: the vault picker is its own element, shown only while creating and only above one writable vault, as it always has been. | > | The design's two deeper text steps, `#6D6F84` and `#5D5F74` | Not added as `Palette.axaml` keys. `TextGhost`, already repicked to `#7C7F98` for v3, is reused everywhere the design reaches for either — a resource nothing yet distinguishes from `TextGhost` is not free to carry, on the same reasoning the border ramp's own remarks give. | +> +> ## The desktop's v5b — chrome, session shell and SFTP restyled +> +> A sixth pass, and the first aimed at the chrome and the two screens somebody spends the most time inside +> rather than at any one screen on its own. **What shipped:** the titlebar and nav rail redrawn against +> `TitleBar.dc.html` and `NavRail.dc.html` — a segmented SSH/SFTP/S3 switcher at the rail's own head, a +> mode-dependent first row beneath it, and Vaults and Preferences moved off the rail's list and into a +> popover under the user chip at its foot. The terminal and the SFTP surface both gained a session shell — an +> in-screen tab row, a host header, a bottom status bar and a 300-pixel QUICK ACCESS/SNIPS sidebar — +> replacing the window-wide tab strip v3 built and the pin-chip strip that used to sit above the terminal +> alone. The SFTP screen's own two panes and its TRANSFERS strip were restyled against `SFTP.dc.html` — row +> grids, quiet Material glyphs, slim progress bars — and three screens that had never had a `.dc.html` of +> their own got one each: Keychain, Snippets (titled Snips on screen now, matching the rail) and Logs. +> +> **The nav rail stopped being conditional furniture, and that is the one structural change underneath the +> paint.** v3 drew it only under the Vaults tab, so SFTP, S3 and an open terminal each got the window's full +> width; v5b's own fidelity pass puts the rail up beside all three, because the design draws it that way and +> because the segmented switcher now living at its head is only worth having if it stays reachable from +> everywhere. `MainWindowViewModel.IsVaultsTab` still exists and still answers the question it always did — +> which of the rail's own six destinations is on screen, as opposed to SFTP or S3 — but nothing about it gates +> the rail's own visibility any more. The window's minimum grew again to hold the difference: `1016x574` +> became `1081x583`, nine pixels for the titlebar's 44-to-53 and sixty-five for the rail's 190-to-255, added +> straight onto the minimum rather than absorbed by shrinking a screen — see `MainWindow.axaml`'s own remark +> and `LayoutHarness.MinimumWidth`/`MinimumHeight`. `ScreenWidth` held at 826 because the rail is the only +> thing beside a page that grew; `ScreenHeight` grew by the 42 pixels the retired tab strip used to cost every +> screen, because nothing replaced that strip as chrome every screen pays for — the terminal and SFTP +> surfaces pay for their own tab row out of their own session-shell budget instead. See +> `LayoutHarness.ScreenHeight` and `SessionScreenHeight`. +> +> | v5b element | What ships instead | +> | --- | --- | +> | The host header's OS label (`Ubuntu 24.04 LTS`) and latency reading (`12 ms`) | Neither. The client does not know the remote's OS and nothing measures round-trip time — the same two absences the terminal pane header already recorded before this pass, carried into the new header rather than reopened. | +> | A **Port forward** button on the host header | Omitted. The feature does not exist; see the FORWARDING rows earlier in this document. | +> | The tab and status dots' third, amber state | ◆ **Still two states**, for the reason the hosts screen's own dot has stayed two states since v3: green means a shell is open (or a session is connected), grey means it is not, and nothing here pings a host to justify a third colour meaning "reachable but not connected". | +> | The status bar's negotiated cipher, host-key algorithm and key/credential name | Omitted, and recorded rather than silently dropped. None of the three is reachable from this shell today: `SshNetConnection` holds the negotiated algorithms but nothing reads them off `ISshConnection`; `HostKeyPresentation` exists only for the trust questions a connection asks on the way in, not for a session already open; and a key's display name is never threaded past the connect call into anything the session keeps. What is real and bound is CONNECTED plus a dot, the session's own elapsed timer, and — terminal surface only — `UTF-8`, worded as a fact about this client's own renderer rather than a claim the remote agreed to. Candidates for future plumbing, not a defect closed here. | +> | S3 dimmed in the design's own switcher | **Enabled.** The mock leaves S3 as future work; this application already has bucket browsing, so SSH, SFTP and S3 are a true three-way segment, wired to `IsSshShowing`, `IsTransfersShowing` and `IsBucketsShowing` exactly alike. | +> | The S3/Buckets screen | **Did not get the session shell this pass.** `TransfersScreen` serves both SFTP and S3 today and only the SFTP usage in `MainWindow.axaml` sits inside the new tab row/header/status bar/sidebar; the S3 usage is unchanged. | +> | No pins destination in the design at all | **Kept anyway.** The rail still carries Pins — `KnownHostsScreen` — because the mock has no screen for approved host keys and this application's has to stay reachable. | +> | The popover's Settings and Preferences rows, and the design's own Settings-* family of screens | Both land on the one screen this application actually has, `Preferences` — the design's own Settings area is future work, so this is two doors to one room rather than a door removed. | +> | `· Org` after the user chip's name, and a `Primary` tag on a vault row in the popover | Neither. There is no organisation concept behind a vault — only the vault itself — and no vault is distinguished as primary; the popover's vault rows are the existing shown-vaults toggles, restyled. | +> | The design's titlebar, which has nowhere for a sync indicator | `SYNCED` stays, on the titlebar's right side, ahead of the window's own minimise/maximise/close buttons — the one thing this titlebar keeps that the design's own does not draw at all. | +> | Per-tab SFTP sessions, implied by a tab row shared between the terminal and the SFTP surface | **Not built, and not what shipped instead.** A click on the SFTP tab row runs `MainWindowViewModel.SelectFilesHostAsync`, which opens (or reuses) a second, SFTP-specific connection through the same "Browse files" plumbing a pin click already used — an honest second login, not a channel multiplexed onto the terminal's. `SelectedTab` moves with the click, which is also what keeps the sidebar's QUICK ACCESS in step — that list is keyed to `SelectedTab` on both surfaces, so selecting a different terminal tab afterwards can leave QUICK ACCESS naming a host that is not the one the remote pane is actually browsing. | +> | A scroll-to-section affordance behind the sidebar's **+ Pin folder** row | Not built — this application has no way to open the host editor already scrolled to one card inside it. `PinFolderFromSidebarCommand` opens the same three-card editor the hosts screen's own EDIT reaches, at the top, which is the closest honest affordance rather than a new one. | +> | The TRANSFERS strip's aggregate throughput readout (`8.4 MB/s` beside an arrow) | Omitted. `TransferRowViewModel.Progress` computes a rate per transfer; nothing sums those into one number for the whole queue, and inventing one would be exactly the fabricated fact this document's honesty rule forbids. | +> | Freshness/"hot row" colouring and a selection ring on a finished transfer | Not drawn — there is no tracked notion of how recently a row finished, so nothing distinguishes a transfer that just completed from one that finished an hour ago. | +> | The design's Material-Symbols-only "draft" glyph for a file row | `insert_drive_file`, the closest classic Material Icons has — a plain document rather than a page with a folded corner. Recorded as a substitution rather than silently swapped; "draft" is simply not a glyph this font contains. | +> | `REMOTE` across the SFTP panes' arrow column | `HOST`/`BUCKET`, kept from before this pass rather than adopted from the design. The pair is the difference between a directory tree and a flat namespace with inferred folders, which is a distinction worth keeping even where the design collapses it. | +> | `THIS MACHINE` on the local pane | `LOCAL`, the design's own word, adopted — nothing in this file's own comments ever defended "THIS MACHINE" the way HOST/BUCKET is defended above, so there was no reason to keep it. | +> | The Keychain screen's `MODIFIED` column and `FINGERPRINT` box | Both dropped. `VaultItem` carries no timestamp of any kind, and `SshKeySecret` has no fingerprint field — computing one would mean parsing armour the type stores verbatim, an already-recorded gap this pass did not revisit. | +> | Five separate add buttons (GENERATE / + SSH KEY / + PASSWORD / + TAG / + BUCKET) | One `+ New key` accent button, opening a menu of the same five actions in the same order — the design draws one button because its own mock has one "add" concept; this application has five, and none of them was dropped to fit. | +> | `NEW ITEMS GO TO` on the vault-filing picker | `NEW ITEMS FILE TO`, the design's own wording, adopted — this screen's own comments already called the act "filing", so the label was this application's vocabulary already. | +> | The Snippets screen's own on-screen name | **Snips**, matching the rail, which already said so. | +> | A modified-date on a snippet card | Omitted — `SnippetSecret` carries no timestamp, the same absence every other item kind has. | +> | The design's shorter captions — "Type into terminal", the rationale-only sentence for "runs on insert" | The screen's own longer, more actionable wording kept instead: the insert button still names the destination tab (`TYPE INTO {tab}` / `NO TERMINAL OPEN`), and the "runs on insert" caption still states the operational consequence rather than only the reason the setting exists. Both of the design's sentences are true; the ones already here say more. | +> | A delete confirmation for a snippet | **Built, matching the mock.** `SnippetsViewModel.RequestDelete`/`ConfirmDelete`/`CancelDelete` say the same vault-wide-reach, tombstone, no-undo sentence `VaultViewModel.HowFarADeletionGoes` already says for every other item kind — additive beside the existing uncounted `DeleteCommand`, which the phone's own DELETE row still calls. | +> | The Logs screen's own footer sentence about encryption | Adopted verbatim — verified against this screen's own header remark and ADR 0001 before shipping it, both true, so it is drawn as literal text rather than reworded. | Most of it landed. This file is the rest: every element of that design with nothing behind it, which project each piece would have to land in, and **what the shipped interface does instead**. That last @@ -404,7 +468,7 @@ caption buttons and window title drawn on top of the application's own — two s | `SNIPPETS` panel, `↵` to run | client-domain | A snippet item type (`Snippet = 8`, reserved). | **Shipped**, as a screen rather than a panel. `↵` is per snippet and off by default: inserting types the command at the prompt and stops, because nothing here can tell whether the terminal is at a prompt at all. | | Broadcast to all panes (`⌥↵`) | client-ssh | Input is routed strictly by session id in `TerminalDataPlane.Dispatch`; there is no fan-out. Needs splits first. | Omitted. | | Pane header `24ms` | client-ssh | Round-trip measurement. SSH.NET offers no RTT API. | Omitted. | -| Pane header `aes256-gcm` | client-ssh | **The closest miss on this list.** `SshNetConnection` holds the `SshClient`, so `ConnectionInfo.CurrentServerEncryption` is right there — it just is not on `ISshConnection` or surfaced by `TerminalWorkspace`. | Omitted; the tab strip shows the account and endpoint actually dialled. | +| Pane header `aes256-gcm` | client-ssh | **The closest miss on this list.** `SshNetConnection` holds the `SshClient`, so `ConnectionInfo.CurrentServerEncryption` is right there — it just is not on `ISshConnection` or surfaced by `TerminalWorkspace`. | Omitted; the session shell's own host header shows the account and endpoint actually dialled (v3–v4: the tab strip did; v5b moved the fact to `SessionHeader`, off `MainWindowViewModel.SessionAddress`, when the strip was retired). | | Pane header showing the running command and `following` | client-ssh | The host moves opaque bytes and never parses terminal output. Would need shell integration (OSC 133) on the remote. | Omitted. | | A `local · zsh` tab | client-ssh | Every session here is an SSH channel. Needs ConPTY and a second session kind. | Omitted. | | Tab strip `+` button | ui | Not missing so much as redundant: the real operation is *select a host, press Connect*, which the hosts grid already is. | **Shipped**, as the palette rather than a menu: it opens what Ctrl+K opens, so the strip and the shortcut are one way of doing one thing. A `MenuFlyout` offering "SSH" and "local shell" is the nicer answer and is not verifiably safe above the terminal's native child window — and there is no local shell to offer. | diff --git a/docs/manual-checks.md b/docs/manual-checks.md index bdd01f2..a686a94 100644 --- a/docs/manual-checks.md +++ b/docs/manual-checks.md @@ -24,15 +24,17 @@ a phase had nothing left for a person to do, which is the good outcome rather th --- -## Phase 1 — the shell and the tab strip +## Phase 1 — the shell and the nav rail ### 1.1 No screen is sliced at the WebView's left edge · **the important one** -Open two terminals, then visit every nav rail entry in turn — Hosts, Keychain, Pins, Snippets, Logs, Vaults, -Preferences — and both of the fixed tabs, SFTP and S3. +Open two terminals, then visit every nav rail entry in turn — Hosts, Keys, Pins, Snips, Logs — and both of +the switcher's other two segments, SFTP and S3, at the rail's own head. -**Pass:** each screen draws whole, its buttons all clickable, and the tab strip stays across the top of all -nine. The nav rail is there for the seven and gone for the two, because it belongs to the Vaults tab. +**Pass:** each screen draws whole, its buttons all clickable, and the nav rail stays up the left edge for +every one of them. Since v5b's chrome pass the rail is permanent furniture — it no longer collapses for +SFTP, S3 or an open terminal the way it did while it belonged to a Vaults tab; see +`MainWindowViewModel.IsVaultsTab`'s own remark for what changed and what stayed the same under that name. **Failure means:** a screen is not collapsing while the terminal shows. The terminal is a native child window and composites above everything Avalonia paints, so the symptom is a screen cut off at the WebView's @@ -41,8 +43,8 @@ bound to `IsShowingPages` in `MainWindow.axaml` is what should make it impossibl ### 1.2 A tab clicked from another screen takes the keyboard -Go to FILES with a terminal open. Click the tab. **Start typing immediately, without clicking anything -else.** +Go to SFTP with a terminal open, then click the SSH segment at the rail's head to return to it. **Start +typing immediately, without clicking anything else.** **Pass:** every character reaches the shell, including the first. @@ -53,7 +55,7 @@ be typed immediately rather than after a pause. ### 1.3 Leaving a terminal gives the keyboard back -With a terminal focused, click FILES. Type into the filter box. +With a terminal focused, click SFTP at the rail's head. Type into the filter box. **Pass:** the characters appear in the box. @@ -64,10 +66,10 @@ strip rework and is now on the hot path. See `docs/platform-flags.md`. ### 1.4 The middle click closes tabs and only tabs -Middle-click a tab (closes it), the strip background to the right of the last tab (closes nothing), and the -`+` button (closes nothing, opens nothing). +On the terminal's own in-screen tab row: middle-click a tab (closes it), the row's background to the right +of the last tab (closes nothing), and the `+` button (closes nothing, opens nothing). -**Pass:** as described. Covered by `TerminalTabsTests` headlessly, so this is a confirmation that headless +**Pass:** as described. Covered by `SessionTabRowTests` headlessly, so this is a confirmation that headless pointer input matches a real mouse rather than a first look. ### 1.5 Connecting from the palette while on another screen @@ -82,28 +84,28 @@ Approving connects; CANCEL leaves you on FILES. question that cannot be reached — or the window has jumped to HOSTS, which is what it used to do and what cost the palette its whole point. -### 1.6 The vault menu draws above the terminal's rectangle · **the one with a precedent** +### 1.6 The user-chip popover draws above the terminal's rectangle · **the one with a precedent** -With a terminal open and showing, press the `⌄` beside the Vaults tab. +With a terminal open and showing, press the user chip at the foot of the nav rail. -**Pass:** the window leaves the terminal for the Vaults tab as the menu opens, and the menu is drawn whole -over the screen underneath it — no part of it clipped along the WebView's edge. +**Pass:** the popover opens without leaving the terminal, and is drawn whole over the screen underneath it — +no part of it clipped along the WebView's edge. -**Failure means:** the popup is being composited under the renderer's native child window, and the guard -this design relies on has stopped working. It is not supposed to be possible: `OnVaultMenuPressed` selects -the Vaults tab *before* opening the flyout, and a page surface is one where the renderer is not drawn — the -same move QuickConnect makes. `OpeningTheVaultMenu_SelectsTheVaultsTabSoTheTerminalIsNotUnderIt` asserts -the ordering headlessly, which is as far as a headless test can go: it has no native window, so it cannot -see what is painted over what. This check is the other half. +**Failure means:** the popup is being composited under the renderer's native child window. Unlike the v3–v5 +tab strip's own vault menu, which sat directly above the terminal's own rectangle and had to select the +Vaults tab before opening for exactly that reason, the rail's popover should not need that guard at all: it +opens inside the rail's own 255-pixel column — see `NavRail.axaml`'s own remark — which the terminal's +native child window never occupies, so there should be no rectangle here for a popup to be composited +under. If this fails, that assumption is the thing to re-examine, not the ordering of two calls. No headless +test can see this either way — it has no native window, so it cannot see what is painted over what. -It is on this list rather than assumed because the note beside the `+` button in `TerminalTabs.axaml` -refuses a flyout on exactly this reasoning, and `docs/platform-flags.md` records what this project has -already paid for treating a rendering claim as settled without looking. +It is on this list because `docs/platform-flags.md` records what this project has already paid for treating +a rendering claim as settled without looking. ### 1.7 Switching a vault off does not switch it out -With a team vault holding at least one host: press `⌄` beside Vaults, switch the team vault off, and check -the hosts screen, the keychain and the pins. +With a team vault holding at least one host: press the user chip at the foot of the nav rail, switch the +team vault off in the popover, and check the hosts screen, the keychain and the pins. **Pass:** that vault's hosts, keys and pins are gone from all three; the vault is still in the "file this into" picker on a host editor; the sync indicator still settles rather than stalling; and a host in another @@ -120,7 +122,8 @@ view models — this check is the version with a real connection on the end of i ### 2.1 The fingerprint column is readable end to end Connect to two or three hosts, approving each fingerprint. Go to Pins and narrow the window to its minimum -(1016px since the v2 sidebar; it was 880 while the rail was 54 wide), then widen it to something ordinary. +(1081px since v5b's titlebar and rail fidelity pass; it was 1016 with the v2 sidebar, and 880 while the rail +was 54 wide), then widen it to something ordinary. **Pass:** the full `SHA256:…` is on screen at both sizes, never cut off and never ellipsised. @@ -1727,9 +1730,9 @@ button that always refuses is the milder failure and is still worth removing. Rename a shared vault from the VAULTS screen. **Pass:** the new name is on the vault list, on the badge of every host card in that vault, in the keychain -screen's "new items go to" picker, in the host editor's vault picker, and in the tab strip's vault menu — -and on the second account after a refresh. Nothing in the vault needs re-encrypting and everybody's key -still opens it. +screen's "new items file to" picker, in the host editor's vault picker, and in the nav rail's user-chip +popover — and on the second account after a refresh. Nothing in the vault needs re-encrypting and +everybody's key still opens it. **Failure means:** a name that moved in one place and not another is the shape this rename is most likely to fail in, because several screens read it separately from a cached vault row. A vault that stops opening