Public Access
Snapshot the v5 hosts screen mid-restructure, with handoff notes to resume it
This commit is contained in:
@@ -0,0 +1,168 @@
|
||||
# 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; `<AvaloniaResource Include="Assets/Fonts/**"/>` 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 <proj> -- --filter-method '<name>'` — 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.
|
||||
Reference in New Issue
Block a user