diff --git a/HANDOFF-hosts-v5.md b/HANDOFF-hosts-v5.md deleted file mode 100644 index 7ab4066..0000000 --- a/HANDOFF-hosts-v5.md +++ /dev/null @@ -1,168 +0,0 @@ -# Handoff: Hosts v5 design import — execution in progress - -Written 2026-08-07, mid-execution, because the session hit its usage limit. A future session should -read this file plus `hosts-v5-design-spec.md` (same directory) and resume at "Where things stand". - -## What this is - -Implementing the Claude Design project screen `Hosts.dc.html` -(https://claude.ai/design/p/b20d580c-eae5-4cdd-bfae-88f669b5f8a3?file=Hosts.dc.html) in the desktop -Avalonia client. Worktree: `.claude/worktrees/connection-host-default-text-9e501d`, branch -`claude/hosts-design-implementation-22f161`. Nothing is committed yet — all progress is uncommitted -working-tree changes. - -`hosts-v5-design-spec.md` holds the palette mapping table, the design's measurements, the monogram -hue pairs, and — critically — the **agreed deviations** from the mock (relay copy stays honest, dot -stays two-state, card click selects rather than connects, quick-connect keeps the auth word, find box -stays, "New group…" moves into the Group ▼ flyout, quick-access hint drops the "sidebar" claim). - -## User-approved decisions (asked and answered, do not re-ask) - -1. **Full re-theme** — shared palette swapped to purple/near-black; Montserrat + JetBrains Mono + - Material Icons embedded in the shared Shell project; Android recolors via the shared palette but - keeps its own default sans. -2. **Flat sections** — desktop hosts screen becomes one collapsible section per group (nesting - flattened phone-style, label order), "No group" first, Collapse All; group cards + breadcrumbs go. - Collapse state in-memory only. -3. **Pinned folders: full build** — `HostSecret.PinnedPaths`, drawer QUICK ACCESS editor, pin-count - card badge, chip strip above the terminal; **clicking a pin opens the SFTP tab with the remote pane - at that path**. -4. **Last connected from the connection log** — `max(StartedAt)` per `HostId` on hosts-screen - activation and on `SessionEnded`; relative time, restrung on a 1-minute tick; live sessions show - the green dot instead. - -Execution method the user chose: **Sonnet subagents**, one wave at a time (waves 2+ serialize because -they all touch `src/DodoSSH.Client.Shell/ViewModels/VaultViewModel.cs`, ~11.8k lines, shared with the -Android head). - -## Where things stand - -### Wave 1 — DONE and verified (full `dotnet build DodoSSH.slnx`: 0 errors) - -**Domain (complete, 264/264 tests pass in `tests/DodoSSH.Client.Domain.Tests`):** -- New `src/DodoSSH.Client.Domain/PinnedPathList.cs` — immutable, insertion-ordered, ordinal-deduped - (keeps first occurrence), `Empty`, `Create(...)`, `ToPathMap()` for the merge. -- `HostSecret.PinnedPaths` (default `Empty`), constants `MaxPinnedPathLength = 1024`, - `MaxPinnedPaths = 32`, validation wired into `TryValidate` (blank/control-char/oversize refused; - relative paths allowed). -- `HostSecretMerge.MergePinnedPaths` — per-element three-way merge; result order = base, then local - additions, then remote additions. -- `HostSecretCodec`: `PinnedPathsSchemaVersion = 7` (now current), field written last and null when - empty; old payloads decode as pinning nothing. -- UI usage: add = `host with { PinnedPaths = PinnedPathList.Create([.. host.PinnedPaths, p]) }`; - remove via `Create(host.PinnedPaths.Where(...))`. - -**Theme (complete):** -- `src/DodoSSH.Client.Shell/Theme/Palette.axaml` fully re-colored per the spec table. New resources: - `AccentGradient` (#8D51E7→#5D42DE), `AccentGlow` (BoxShadows `0 0 22 0 #598D51E7`), `IconFont` - (Material Icons, reference glyphs BY CODEPOINT, e.g. push_pin ``). `AccentInk` is now - white. `Live` #34D399. Essay comments updated where the recolor falsified them. -- Fonts embedded at `src/DodoSSH.Client.Shell/Assets/Fonts/` (Montserrat 4 statics, JetBrains Mono 3, - MaterialIcons-Regular; licenses alongside; `` added to - the Shell csproj). `Program.cs` default sans → `avares://DodoSSH.Client.Shell/Assets/Fonts#Montserrat` - with Inter fallback; `MonoFont` → JetBrains Mono first. `tests/DodoSSH.Client.App.Layout.Tests/HeadlessApp.cs` - updated to keep its documented font parity with `Program.BuildAvaloniaApp`. -- `App.axaml`: tile/section radius 12, buttons/inputs radius 10, chips 6; `Button.accent` uses - gradient + glow (glow zeroed on :disabled). Deliberately skipped: raising global control MinHeight - to 40 (too risky unverified — style the hosts screen explicitly instead). -- Known stale doc: `docs/design-import-gaps.md:304` still says Inter/system-mono — wave 5 fixes docs. - -### Wave 2 — WAS RUNNING when the session ended: state UNKNOWN, verify before building on it - -A Sonnet agent was mid-flight restructuring the hosts screen. **Its changes may be complete, partial, -or absent in the working tree.** Before anything else: `git status` + `git diff --stat`, look at -`src/DodoSSH.Client.App/Views/HostsScreen.axaml` and `VaultViewModel.cs`, build, and run -`tests/DodoSSH.Client.App.Layout.Tests` + `tests/DodoSSH.Client.App.Tests`. If its work is partial or -broken, either finish it by hand or reset those files and re-dispatch. - -Wave 2's full brief (re-usable as a subagent prompt): -- Flat sections per approved decision 2. Reuse/generalize the phone's flattening - (`SidebarRows` / `SidebarGroupHeader` / `ToggleGroup` / in-memory `collapsedGroups` ~line 1251). - No-groups keychain renders headerless (documented invariant). Section heading: - `{label} · {count}`, vault name beside it when >1 vault, per-section collapse, "Collapse All" on - the first heading row. Group Edit/Move/Delete on a heading menu (that order; no "Open"). Group - cards, `GroupTrail`, double-click-open, drag-host-onto-group-card all go (desktop only — phone - must not regress; it shares the VM). -- PRESERVE: multi-select (marquee `Band` + chosen-hosts ops + the three action panels), keyboard - nav, context menus, conflict panel, `NoVisibleHostsMessage`, find box (only search that crosses - group boundaries). -- Toolbar: "Hosts" title + count chip; Group ▼ / Tag ▼ checkable filter flyouts ("New group…" at the - Group flyout's foot); + New host accent button. Host passes tag filter if it wears ANY checked tag; - empty sections hide; filters ∩ find ∩ collapse compose. -- New card: 32×32 monogram avatar (two letters, stable-hash into the spec's four hue pairs, JetBrains - Mono bold), name 17 bold + two-state dot, mono `user@hostname` line (full address stays on - tooltip), pin badge (`IconFont`  + `HostRowViewModel.PinCount` reading - `Host.PinnedPaths.Count`) only when >0, tag chips bottom-left, `LastConnectedText` - `[ObservableProperty]` slot bottom-right (EMPTY — wave 4 fills it), keep warn Badge/VaultBadge/✓/ - pencil→`OpenHostPaneCommand`. Click selects, double-click connects. Responsive card widths. -- Update layout + behavioral tests; add coverage for flattening order, no-groups invariant, collapse, - filter composition, monogram stability. - -### Wave 3 — NOT STARTED: drawer + pinned folders UI - -- Restyle `src/DodoSSH.Client.App/Views/HostDrawer.axaml` to the mock: width 304→320, section labels - ADDRESS / GENERAL / CONNECTION / QUICK ACCESS (10px semibold, letterspacing 1.2), "Saving to - {vault} vault" inline picker (same gating: creating only + >1 writable vault), address field first - (mono, accent inset border), Name/group/tags/notes, username + 78px port on one row, auth - ComboBox, relay checkbox card — **KEEP the honest copy including "(not wired up yet)" - (HostDrawer.axaml:516-535); do not adopt the mock's relay text**. Footer: Save host - (accent/gradient, flex) + Cancel (92px ghost). -- QUICK ACCESS editor: rows (folder glyph  via IconFont, mono path, ✕ remove), mono add - field + add button, hint sentence exactly: "Pinned folders appear above the terminal for this - host." Editor state on `VaultViewModel` following the `editorTagIds` pattern; enforce - `HostSecret.MaxPinnedPaths`/`MaxPinnedPathLength` before `TryValidate`. -- Detail pane (non-editing) should also show pinned paths read-only (design only shows the editor; - match the pane's existing fieldrow idiom). -- Terminal pin strip: chip row above the terminal for the connected host's pins, visible only when - the active tab's host has pins. Clicking opens the SFTP tab with the remote pane navigated to that - path for THAT host — needs an "open SFTP at path for host X" entry point on the transfers side - (extend what the context menu's "Browse files" already does; see `TransfersViewModel`). SFTP is a - second authenticated connection (documented, fine). Tab→host matching today is by label - (`MainWindowViewModel.RefreshConnectedHosts` ~3179) — reuse that convention and its documented - tradeoff. -- Android: the field syncs; phone editor UI deferred (document in wave 5). - -### Wave 4 — NOT STARTED: last connected + quick connect - -- Last connected per approved decision 4. Read via `VaultSession.ConnectionLog` - (`ConnectionLogSecret`: `HostId?`, `StartedAt`; entries written once at close). Compute on hosts - screen activation + on `TerminalWorkspace.SessionEnded`; fill `HostRowViewModel.LastConnectedText` - ("just now", "N min ago", "N hr ago", "N days ago"); 1-minute restring tick while the screen is - visible; NO background log re-read (the log is deliberately not on the sync loop — - `LogsViewModel.cs:138-143`). Precedent: `MainWindowViewModel.RecentConnections` ~1113. -- Quick connect restyle (`src/DodoSSH.Client.App/Views/QuickConnect.axaml` + `MainWindowViewModel` - ~1576-1730): card 520→~640 wide, radius 14, mono prompt `>_` styling, row = 8px live dot - (`IsConnected` exists on the row VMs' hosts) + mono name + mono address + auth word right - (credential/key/password — NOT the mock's SSH/SFTP constant). Selected row fills Accent with white - text. Footer hint kept. - -### Wave 5 — NOT STARTED: docs + full verification - -- New **v5 section** in `docs/design-import-gaps.md`, same format as v2/v3/v4: what shipped, then a - table of mock elements refused/deviated (amber dot; relay copy; card-as-link; SSH/SFTP kind column; - collapse persistence — in-memory, settings.json stays two scalars by decision; phone pinned-folder - editor deferred; Collapse All placement; "sidebar" claim dropped from the quick-access hint). Fix - the stale font row at ~line 304. Note the v3→v5 reversal (sections replace breadcrumbs) and the - v4 reversal (chips + address back on the card) explicitly, with the user's approval as the reason. -- Full `dotnet build DodoSSH.slnx` + ALL test projects. LF check over every touched file. - -## Conventions that bit or will bite - -- **LF everywhere** — one CRLF file fails the build via IDE0055; merges/agents reintroduce it. - Verify by raw bytes, not shell grep (two agents got false readings from shell quoting). -- Single test: `dotnet test -- --filter-method ''` — plain `--filter` is silently ignored. -- Essay-style comments are load-bearing; never leave one asserting something a change made false. -- `PublicAPI.Unshipped.txt` analyzer exists only for `DodoSSH.Contracts` (not touched so far). -- Don't run two `dotnet build`s concurrently (shared artifacts dir) — serialize agent builds. -- The repo's honesty rule: nothing in the UI claims what the code doesn't do. This killed the mock's - relay copy, amber dot, and SSH/SFTP kind column — keep applying it. - -## Suggested resume sequence - -1. `git status` / `git diff --stat`; assess wave 2's actual state (complete/partial/absent). -2. Finish or re-dispatch wave 2; build + layout & app tests green before proceeding. -3. Waves 3 → 4 sequentially (both touch `VaultViewModel`), each verified by build + tests. -4. Wave 5 (docs + full sweep). Then review the whole diff, and commit in reviewable slices - (suggested: domain / theme+fonts / hosts screen / drawer+pins / ago+palette / docs). -5. Delete `HANDOFF-hosts-v5.md` and `hosts-v5-design-spec.md` before the branch is merged — they are - working notes, not repo docs. diff --git a/docs/design-import-gaps.md b/docs/design-import-gaps.md index 6ed49e6..759925f 100644 --- a/docs/design-import-gaps.md +++ b/docs/design-import-gaps.md @@ -165,6 +165,84 @@ the chrome, hosts and terminals, file transfer, the vault, teams, and preference > | **Show more ⌄** | Not drawn as a disclosure. What it would hide — notes, the relay switch, forgetting the host key — is in the editor, one press away, and a second fold inside a pane that already scrolls is a second place for a field to be missing from. | > | **Port Forwarding** in the sidebar | Nothing, for the third time in this document. | > | The host grid's toolbar avatar, share and tag-filter controls | Omitted, as in v3 and for the same reasons. | +> +> ## The desktop's v5 — hosts, restyled, and two reversals +> +> A fifth pass, and the first that undoes as much of its own predecessors as it adds: a re-theme both heads +> share, pinned folders as a full feature, a last-connected time read off the connection log, and two pieces +> of earlier passes put back on the user's own instruction rather than kept. +> +> **The palette went purple, and three fonts are embedded that were only ever named before.** The shared +> scheme moves from v3's near-black-and-blue to a purple accent (`#5D42DE`, with a gradient and a glow of its +> own) over the same near-black canvas, and both heads recolour together — `Palette.axaml` is shared, and a +> colour is not allowed to mean something different on the two of them. What changed on the desktop alone is +> the type: Montserrat, JetBrains Mono and Material Icons are embedded as `AvaloniaResource` in the Shell +> project rather than requested by name and left to whatever the machine happens to have, the way `WithInterFont` +> alone used to leave Inter registered but unused. Montserrat is the default sans now, with Inter kept as its +> fallback rather than the whole answer; `MonoFont` gains JetBrains Mono at the front of its own list, ahead of +> the system-mono stack that is still behind it for a glyph JetBrains Mono does not cover. Android recolours +> with the shared palette and keeps its own default sans — fonts are per-head by decision, only colour is +> shared. +> +> **Flat sections replace the grid of group cards and the breadcrumb trail — reversing v3, on the user's own +> approval rather than a defect found in it.** v3's grid held one level of the group tree at a time, opened by +> a double-click, with a trail above it saying where you were; that model is retired outright rather than +> folded into anything; every group is now a heading on one flat board, in label order, exactly the flattening +> the phone's `SidebarRows` has always drawn — a headerless board for a groupless keychain, "No group" first +> where there is one, a chevron per heading and one Collapse All. Nothing here was found broken; the grid did +> what v3 asked for. The user asked for the phone's shape on the desktop too, and decision 2 in +> `hosts-v5-design-spec.md` is that request recorded. +> +> **Tag chips and a mono address line are back on the card — reversing v4, also by approval and not by +> defect.** v4 had replaced both with a two-line `ssh, root, pci, eu-west-1` subtitle and moved the address to +> the tooltip alone, on the reasoning that a card read while scanning forty machines wants the kind of machine +> more than its address. v5's own mock draws chips and an address on every card, and the user chose the mock's +> arrangement over v4's when the two were put side by side — so the card now carries both: the full address is +> still on the tooltip, as v4 left it, and a truncated mono copy sits under the name as well. +> +> **Pinned folders are a full feature, not a field with nowhere to be edited.** `HostSecret.PinnedPaths` is an +> ordered, deduplicated list merged per path the way `TagIds` is merged per tag, so two machines pinning +> different folders on one host both keep theirs. The drawer's QUICK ACCESS section edits it — a row per pin +> with a close box, an add field and button, staged on `VaultViewModel.EditorPinnedPaths` the way the tag +> picker stages `editorTagIds` — the card shows a pin-count badge once there is at least one, and the detail +> pane repeats the list read-only in its own fieldrow idiom. Clicking a pin above a connected terminal opens +> the SFTP tab with the remote pane navigated straight to that path, through the same connect path "Browse +> files" already used — a second authenticated connection, exactly as every SFTP session here is. +> +> **Last connected is read off the synced connection log, and it is deliberately narrow about when.** +> `max(StartedAt)` per `HostId` is computed on the hosts screen's own activation and again when a session ends, +> then restrung into words on a one-minute tick for as long as the screen stays visible — never on the sync +> loop, which is the same rule `LogsViewModel` states for the log itself: nobody is watching their own +> connection from an hour ago update on a screen they are not looking at. A connected host prints nothing here; +> the green dot already answers "right now", and printing an age beside it would be answering a question about +> a session that is not the one still running. **It is scoped to the active vault alone**, the same limitation +> `LogsViewModel` and `MainWindowViewModel.RecentConnections` already carry — a host filed in a second vault +> this session can read gets no ago-text until something on this screen reads more than one vault's connection +> history, which nothing does yet. And a session ending can, briefly, still show the previous entry: the +> recorder's own write is queued onto a background task rather than made inline, so a re-read landing before +> that write drains reads the log as it stood before the session it is reporting on closed. It self-heals on +> the next activation or the next session end, and closing the gap outright was judged not worth blocking a +> screen transition on the recorder's queue. +> +> **Quick connect grew to match, and quietly answers a different question than the mock's own row does.** The +> card widened from 520 to 640, gained the mock's `>_` prompt styling and a live dot per row the way the hosts +> board's own two-state dot works. What it does not draw is the mock's SSH/SFTP kind column — every palette +> connection here is SSH, so a constant printed as though it varied would be exactly the kind of decoration +> this document has refused before (the terminal's session footer, the snippet's kind badge); the auth word +> (credential/key/password) answers a question that is actually different per row instead. +> +> | v5 element | What ships instead | +> | --- | --- | +> | The status dot's third, amber state | ◆ **Still two states, on the same reasoning v3 and v4 both gave and v5 was asked to reconsider.** Green means a terminal is open on that host, grey means one is not; nothing here pings a machine, so the amber "reachable but not connected" state has no fact behind it to draw. | +> | The relay checkbox's mock copy | The pre-existing sentence, unchanged, "(not wired up yet)" included — restyled into the mock's own nested-card shape (radius 12, a title beside the box rather than under it) but not reworded. The mock's own copy implies a relay this client can dial; it cannot, for the reason `HostDrawer.axaml`'s own remarks give. | +> | The host card as a link straight to a terminal | Click still selects, double-click still connects, and the pencil still opens the pane — the mock's card-as-link is not adopted, because multi-select (Ctrl, Shift, the marquee band) depends on a plain click meaning "choose this one" rather than "go". | +> | Quick connect's SSH/SFTP kind column | The auth word — credential, key, or password — see above. | +> | A collapsed section staying collapsed after a restart | In memory only, for the running session. `settings.json` holds two scalars by decision — the terminal's text size and whether this machine checks for updates on its own — and collapse state is not judged worth a third. | +> | QUICK ACCESS's editor, on the phone | **Deferred; the data is not.** `HostSecret.PinnedPaths` is shared, synced and merged on both heads, so a pin made on the desktop reaches the phone and back — Android just has nowhere yet to add or remove one itself. | +> | Collapse All beside every section's own collapse chevron | Bound on every heading's view model and shown on only the first — `SidebarGroupHeader.IsFirstBoardSection` is what a virtualised list of sections uses in place of a control of the board's own that would otherwise have to sit above all of them. | +> | 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. | 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 @@ -301,7 +379,7 @@ field cannot be removed and stays as a permanently refused member; `SyncEndpoint | Status bar port forwards | client-ssh | Port forwarding. See below. | Omitted. | | Status bar `sftp · 2 transfers` | client-app | Nothing now — file transfer is built. What is missing is the count reaching the status bar, which is a screen away from where the queue lives. | Omitted from the status bar. The queue itself is on the FILES screen, with a row per transfer. | | Status bar `locks in 09:41` | client-app | An idle auto-lock. See preferences below. | Omitted. | -| IBM Plex Mono / IBM Plex Sans | ui | Shipping the font files as `AvaloniaResource` and registering them. The design loads them from Google Fonts, which a desktop app cannot. | Inter (already embedded) for prose, and the system monospace stack the terminal already names. Named once in `App.axaml` as `MonoFont`, so the substitution is reversible in one place. | +| IBM Plex Mono / IBM Plex Sans | ui | Shipping the font files as `AvaloniaResource` and registering them. The design loads them from Google Fonts, which a desktop app cannot. | **Shipped, since v5, and not IBM Plex.** The v5 design asked for Montserrat, JetBrains Mono and Material Icons instead, and all three are embedded as `AvaloniaResource` in the Shell project rather than requested by name and left to whatever the machine happens to have. Montserrat is the default sans, with Inter — the earlier substitution — kept only as its fallback; `MonoFont` puts JetBrains Mono first, ahead of the system-monospace stack the terminal still falls back to. Named once each in `Program.cs` and `Palette.axaml`, so a later design's fonts are still a one-line swap. | | `⌘K`, `⌥↵` | ui | Nothing; the design is Mac-flavoured. | `CTRL K`. Development is Windows-only today (`docs/platform-flags.md`). | **Also worth knowing:** custom chrome went in as `SystemDecorations="BorderOnly"`, not by extending the diff --git a/hosts-v5-design-spec.md b/hosts-v5-design-spec.md deleted file mode 100644 index def0a47..0000000 --- a/hosts-v5-design-spec.md +++ /dev/null @@ -1,130 +0,0 @@ -# Hosts v5 design import — spec and decisions - -Source: Claude Design project `DodoSSH`, file `Hosts.dc.html` (1920x1080 desktop mock). -This file holds (a) the agreed decisions, (b) the palette mapping, (c) the raw design HTML at the bottom. - -## Agreed decisions (user-approved) - -1. **Full re-theme**: swap the shared palette (`src/DodoSSH.Client.Shell/Theme/Palette.axaml`) to the new - purple/near-black scheme — both heads recolor together. Embed Montserrat + JetBrains Mono + - Material Icons font files in the desktop head. Android keeps its default sans (fonts are per-head; - only colors are shared by decision); `MonoFont` gains JetBrains Mono at the front of its fallback list. -2. **Flat sections**: the desktop hosts screen becomes one collapsible section per group (nesting - flattened into one heading per group in label order, exactly like the phone's `SidebarRows`), - ungrouped hosts under "No group" first. Per-section collapse + Collapse All. Group cards and the - breadcrumb trail leave this screen. Group edit/move/delete move to a menu on the section heading. - Collapse state is in-memory only (settings.json stays two scalars by decision). -3. **Pinned folders (QUICK ACCESS)**: full feature. `HostSecret.PinnedPaths` (ordered strings, - per-path three-way merge like `TagIds`), drawer editor section, pin-count badge on cards, and a chip - strip above the terminal for the connected host; clicking a pin opens the SFTP tab with the remote - pane at that path (a second authenticated connection, as SFTP always is here). -4. **Last connected**: computed from the synced connection log (`max(StartedAt)` per `HostId`) on - hosts-screen activation and on `SessionEnded`; relative time ("just now" / "N min ago" / hours/days), - restrung on a 1-minute tick while visible, no background re-read. - -## Deliberate deviations from the mock (do NOT implement the mock literally on these) - -- **Relay checkbox copy keeps the honest wording** including "(not wired up yet)" — the design copy - implies a working relay; the client dial path does not exist yet. Keep the current sentence structure - in HostDrawer.axaml:516-535. -- **Status dot stays two states** (green = terminal open, grey = not). The design's amber has no - truthful meaning; nothing probes hosts. -- **Card click selects** (multi-select depends on it), double-click connects, pencil opens the detail - pane. The mock's card-as-link-to-terminal is not adopted. -- **Quick connect keeps the auth word** (credential/key/password) instead of the mock's SSH/SFTP kind - column — every palette connect is SSH, and a constant printed as a reading is refused in this repo. -- **Find box stays** (restyled) — it is the only search that crosses group boundaries. -- "New group…" moves to the foot of the Group ▼ filter flyout (the mock has no group-creation control). -- The QUICK ACCESS hint sentence ships as what is true: "Pinned folders appear above the terminal for - this host." (no "sidebar" claim). - -## Palette mapping (design rgb → Palette.axaml key) - -| Key | Old | New | -| --- | --- | --- | -| CanvasColor | #0E1220 | #05050A (design body rgb(5,5,10)) | -| Chrome | #111629 | #10111E (window rgb(16,17,30)) | -| Sidebar | #0F1426 | #0D0D16 (drawer rgb(13,13,22)) | -| Panel | #111629 | #10111E | -| Raised | #151B31 | #101019 (card rgb(16,16,25)) | -| Field | #0B0F1D | #101019 (inputs rgb(16,16,25)) | -| Chip | #16204A | #1A1A28 (rgb(26,26,40)) | -| Hover/Active/ChromeHover | — | derive slightly lighter than their surfaces, e.g. Hover #14151E, Active #1E1E2C, ChromeHover #1E1E2C | -| Border | #1A2138 | #1E1E2C (rgb(30,30,44)) | -| BorderMid | #232B45 | #24243A (rgb(36,36,58)) | -| BorderHover | #2E3859 | #2C2C3E (rgb(44,44,62)) | -| BorderSubtle/BorderFaint | — | keep one step darker than Border, e.g. #17172", use judgment | -| Text | #FFFFFF | #FFFFFF | -| TextDim | #B4BCD4 | #EDEDF5 (rgb(237,237,245)) | -| TextFaint | #7B849F | #9C9EB4 (rgb(156,158,180)) | -| TextGhost | #5A6280 | #7C7F98 (rgb(124,127,152)); the design also uses #6D6F84 and #5D5F74 for deeper steps — add keys only if needed | -| AccentColor | #5B8CFF | #5D42DE (rgb(93,66,222)) | -| AccentText | #8FA7FF | #A78BFA | -| AccentInk | #0E1220 | #FFFFFF (text on accent is white in this design) | -| AccentSoft/AccentWash | 0.35/0.06 | same opacities over #5D42DE | -| NEW: AccentGradient | — | LinearGradientBrush 180deg #8D51E7 → #5D42DE | -| NEW: AccentGlow | — | for BoxShadow use: 0 0 22 0 #8D51E7 at ~35% alpha | -| Live | #3DDC97 | #34D399 (rgb(52,211,153) — the design's own green) | -| Warn | #FFB454 | #F59E0B (rgb(245,158,11)); WarnText #F5B942 | -| Danger | #E86A6A | keep | - -Radii: cards 12, controls 10, small chips 5-6. Field/control height 40. Primary buttons use -AccentGradient + glow. - -Monogram avatar hue pairs (bg/fg), assigned by stable hash of host label: -violet #241C4F/#A78BFA · green #103A2F/#34D399 · amber #423211/#F5B942 · gray #1E1E2C/#9C9EB4. - -## Raw design HTML (Hosts.dc.html) - -The ``/`{{ }}` bits are the design tool's templating; `style-hover` is a hover style. -Sample data lives in the script block at the bottom. - -```html -
- -
- -
- - Hosts -
10
- - - - - - - -
- - - - -
-
-``` - -Sample data (for tone, not to be shipped): hosts like `prod-api-01 / deploy@203.0.113.14 / pins 4 / -tags production,api / 2 min ago`; pinned = `/var/www/app`, `/etc/nginx`, `/var/log/pm2`.