docs: add UI-rewrite notes, plans, and stream-formatter spec
This commit is contained in:
209
docs/superpowers/plans/2026-04-20-ui-polish-design-parity.md
Normal file
209
docs/superpowers/plans/2026-04-20-ui-polish-design-parity.md
Normal file
@@ -0,0 +1,209 @@
|
||||
# UI Polish — Design Parity Follow-up
|
||||
|
||||
> Follow-up to the islands rewrite. Closes visible gaps between the current state and the handoff mock. Execute with subagent-driven development; phases B/C/D can run in parallel.
|
||||
|
||||
**Goal:** Bring the rewrite to pixel-level parity with `docs/UI Rewrite/design_handoff_claudedo/ClaudeDo-standalone.html`.
|
||||
|
||||
**Tech stack:** Avalonia 12, CommunityToolkit.Mvvm. No new dependencies.
|
||||
|
||||
**Reference files:**
|
||||
- Source of truth: `docs/UI Rewrite/design_handoff_claudedo/ClaudeDo-standalone.html`
|
||||
- CSS measurements: `docs/UI Rewrite/design_handoff_claudedo/styles.css`
|
||||
- JSX component structure: `docs/UI Rewrite/design_handoff_claudedo/islands.jsx`, `app.jsx`
|
||||
- Tokens: `src/ClaudeDo.Ui/Design/Tokens.axaml`
|
||||
- Styles: `src/ClaudeDo.Ui/Design/IslandStyles.axaml`
|
||||
|
||||
**Rules for all phases:**
|
||||
- Use existing token brushes (`MossBrush`, `PeatBrush`, `AccentSoftBrush`, etc.) — do NOT hard-code hex.
|
||||
- Use `Classes="foo"` + selectors in `IslandStyles.axaml` for reusable styling; inline AXAML setters for one-off values only.
|
||||
- Icons: use `Projektion.Avalonia` `PathIcon` with `Data="{StaticResource IconKey}"`. Define new `StreamGeometry` resources in `IslandStyles.axaml` under an `<Icons>` section when needed. Pull the SVG paths from the JSX reference.
|
||||
- Read the relevant JSX + CSS file in the handoff before implementing each component — those are the source of truth for exact measurements/paddings/colors.
|
||||
- Do not touch the data layer, Worker, SignalR, or command wiring. This is a view/style-only pass.
|
||||
|
||||
---
|
||||
|
||||
## Phase A — Shell + title bar (sequential, run first)
|
||||
|
||||
One subagent. Small blast radius; prerequisite for the visual "feel."
|
||||
|
||||
### Task A1 — Custom title bar
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Views/MainWindow.axaml` + `.axaml.cs`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (add title-bar styles + window-control icon-button style)
|
||||
|
||||
- [ ] Replace the current title bar Grid with a 3-section layout:
|
||||
- Left: brand block — checkbox-style green glyph + `CLAUDEDO` (mono, uppercase, tracking 1.4, 11px) + separator dot + current-list name eyebrow-style (mono uppercase, `TextDim`). Bind the list name to `Shell.Lists.SelectedList.Name.ToUpperInvariant()`.
|
||||
- Middle: draggable strip (`PointerPressed → BeginMoveDrag`).
|
||||
- Right: three frameless icon buttons (minimize / maximize-restore / close). Close button hover turns `BloodBrush`. Use `PathIcon` with inline `StreamGeometry` for the Lucide-style icons: `Minus`, `Square`, `X` — the exact SVG `d` strings are in `icons.jsx`.
|
||||
- [ ] Title bar height: 36px, background `DeepBrush`, bottom border 1px `LineBrush`.
|
||||
- [ ] Remove the character glyphs currently used for the window controls (`—`, `▢`, `✕`) — use PathIcons instead.
|
||||
- [ ] Commit: `style(ui): custom title bar with brand and window controls`
|
||||
|
||||
### Task A2 — Background + island shadow
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Views/MainWindow.axaml` (background layer)
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (island shadow adjust)
|
||||
|
||||
- [ ] Under the three-island Grid, add a `Border` filling the whole row with a subtle radial gradient from `DeepBrush` (center) to `VoidBrush` (edges). Use a `RadialGradientBrush` with 2 stops; keep opacity light.
|
||||
- [ ] In `IslandStyles.axaml`, bump the `Border.island` `BoxShadow` to match the token `IslandShadow` value exactly (`0 20 40 #59000000, 0 2 4 #4D000000`). Verify by inspecting the current style — if it's already set, no-op.
|
||||
- [ ] Commit: `style(ui): background gradient and stronger island shadow`
|
||||
|
||||
---
|
||||
|
||||
## Phase B — Lists island polish (parallel with C, D)
|
||||
|
||||
### Task B1 — Icon geometries + eyebrow rename + sections
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (add `StreamGeometry` icon resources at the top)
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs` (map `IconKey` strings → resource keys, add section grouping)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml`
|
||||
|
||||
- [ ] Extract the SVG path `d` strings from `icons.jsx` for: `Sun`, `Activity` (pulse), `Star`, `Calendar`, `Eye`, `Inbox`, `Folder`, `Search`, `Plus`, `MoreHorizontal`. Define each as an `x:Key="Icon.Sun"` `StreamGeometry` in `IslandStyles.axaml`.
|
||||
- [ ] Change Lists eyebrow from `WORKSPACE` to `NAVIGATOR`.
|
||||
- [ ] Add two section-header rows in the ItemsControl: `SMART LISTS` (above items of `Kind=Smart` + `Virtual`) and `MY LISTS` (above items of `Kind=User`). Simplest approach: two separate `ItemsControl`s bound to filtered subsets; or wrap items in a `CollectionViewSource` grouping. Pick the simplest working approach.
|
||||
- [ ] Per-item icon: bind `PathIcon Data="{DynamicResource Icon.{IconKey}}"` via a tiny `IconGeometryConverter` (takes `IconKey` string → looks up resource). Icon color: `TextMute` default; `AccentBrush` (moss) when `IsActive`.
|
||||
- [ ] User-list items: use a 6px circle with `MossBrush` / `PeatBrush` / `SageBrush` dot instead of folder icon (map per list index mod colors, or single color if simpler).
|
||||
- [ ] Active state: remove solid fill. Use `AccentSoftBrush` (~10% moss) + left 2px accent bar + `AccentBrush` icon + `TextBrush` text.
|
||||
- [ ] Commit: `style(ui): lists icons, section headers, active state`
|
||||
|
||||
### Task B2 — Search bar + keyboard hint + footer buttons
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (search style + kbd chip style)
|
||||
|
||||
- [ ] Search `TextBox`: wrap in a `Grid ColumnDefinitions="Auto,*,Auto"` — left `PathIcon Data="{Icon.Search}"` (14px, `TextFaint`), middle TextBox, right `Border Classes="kbd"` with `⌘K` (or `Ctrl K` on Win). The `kbd` chip: mono 10px, `Surface2` bg, `LineBrush` border, padding `6,2`, radius 4.
|
||||
- [ ] Under the items list, add:
|
||||
- `+ New list` button — plain icon+text row, `PathIcon Data="{Icon.Plus}"`, hover tint.
|
||||
- User profile row — avatar circle (initials fallback, seed from `Environment.UserName`), name (`Environment.UserName`), subtitle `{MachineName} / local` mono dim, right `PathIcon Data="{Icon.MoreHorizontal}"`.
|
||||
- [ ] Commit: `style(ui): lists search icon, kbd hint, footer actions`
|
||||
|
||||
---
|
||||
|
||||
## Phase C — Tasks island polish (parallel with B, D)
|
||||
|
||||
### Task C1 — Header + add-task row styling
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs` (subtitle format, header toolbar properties)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (kbd-enter, add-task row)
|
||||
|
||||
- [ ] Subtitle format: change from `{open} open · {running} running · {review} in review` to `{Weekday}, {Month} {Day} · {open} open` to match the mock. Keep the running/review counts visible but move them into a right-aligned mono pill row next to the title (or drop if cleaner).
|
||||
- [ ] Eyebrow: keep current `MONTAG · APR. 20` pattern. Title remains list name.
|
||||
- [ ] Right-side icon toolbar: three `Button Classes="icon-btn"` — `Sort` icon, `Eye` icon (toggle completed), `MoreHorizontal`. Icons: pull paths from `icons.jsx`. Wire `Eye` to an `IsShowingCompleted` observable (persist in a private field for now; no DB change).
|
||||
- [ ] Add-task row: wrap the `TextBox` in a `Border` with `Surface2` bg, rounded 8px, 14px padding. Prepend a circular `PathIcon Data="{Icon.Plus}"` (20px circle, `Surface3` bg). Append a `Border Classes="kbd"` with `ENTER` text (only visible when `NewTaskTitle` has focus — bind visibility to `TextBox.IsFocused`).
|
||||
- [ ] Commit: `style(ui): tasks header toolbar and add-task row`
|
||||
|
||||
### Task C2 — Task row chips + states
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/TaskRowViewModel.cs` (expose a few more flags: `IsOverdue`, `Tags`, `StepsCount`, `StepsCompleted`)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/TaskRowView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (chip variants, selected accent, done state, live-tail meter)
|
||||
|
||||
- [ ] Chip set per row (ItemsControl or StackPanel):
|
||||
- Status chip (already present) — ensure color maps per Status → token brush (idle/queued/running/review/error).
|
||||
- List chip — small colored bullet (6px circle in `MossBrush` or similar) + list name.
|
||||
- Branch chip — `PathIcon Data="{Icon.GitBranch}"` (12px) + branch name (mono 10px).
|
||||
- Diff chip — `+N` moss + ` ` + `−M` blood.
|
||||
- Tags — one chip per tag (`#refactor` style, `Surface2` bg, mono 10px, `TextDim`).
|
||||
- [ ] Selected state: add 2px `AccentBrush` left border on the row Border when `IsSelected=true` (style selector `Border.task-row.selected`). Background shifts to `AccentSoftBrush`.
|
||||
- [ ] Done state: strike-through title + fade opacity to 0.5. Add `Border.task-row:has(.done)` equivalent via the existing `Done` binding — simpler: a `TextBlock` style selector that flips `TextDecorations`.
|
||||
- [ ] Live-tail row (only visible when `Status == Running` and `LiveTail != null`): a `Border` under the chip row with mono 11px ellipsized text + a slim 3px progress `Rectangle` with `MossBrush`. For now the progress is static 30% — wire it to a future `ProgressFraction` property (leave as 0.3 fallback).
|
||||
- [ ] Ensure `task-row` Border has `Transitions` for `Background` + `Margin` (smooth hover + select).
|
||||
- [ ] Commit: `style(ui): task row chip set, selected/done states, live tail`
|
||||
|
||||
### Task C3 — Section dividers (OVERDUE / TASKS / COMPLETED)
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/TasksIslandViewModel.cs` (group the ObservableCollection into sections)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/TasksIslandView.axaml` (group headers)
|
||||
|
||||
- [ ] Add grouping: transform `Items` into three sub-collections:
|
||||
- `OverdueItems` — tasks with `ScheduledFor < Today` and not Done.
|
||||
- `OpenItems` — remaining not-Done tasks.
|
||||
- `CompletedItems` — tasks with `Done=true`.
|
||||
- [ ] Expose as three `ObservableCollection<TaskRowViewModel>` on the VM. Recompute inside `LoadForList`.
|
||||
- [ ] View: three `ItemsControl`s stacked in a `StackPanel`, each preceded by a section header `TextBlock` — `OVERDUE` (only if non-empty), `TASKS`, `COMPLETED · {N}`. Eyebrow style, `TextFaint`.
|
||||
- [ ] Commit: `style(ui): task section dividers overdue/tasks/completed`
|
||||
|
||||
---
|
||||
|
||||
## Phase D — Details island polish (parallel with B, C)
|
||||
|
||||
### Task D1 — Header + task row restyle
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` (expose `TaskIdBadge` like `#T1`, computed from task id prefix)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml`
|
||||
|
||||
- [ ] Top header block:
|
||||
- Eyebrow `LOGBOOK` + right-aligned `#T{shortId}` badge (first 3 hex chars of `Task.Id`, mono `TextFaint`).
|
||||
- Title: keep editable title `TextBox` but reduce size and match mock.
|
||||
- [ ] Under header, a new "task strip" row: `Ellipse` checkbox (bound to `Task.Done` toggle) + title + right-aligned star button. This is separate from the editable title (mock shows both title as editable heading AND a task-row-style strip with check/star).
|
||||
- [ ] Commit: `style(ui): details header with logbook eyebrow and task-id badge`
|
||||
|
||||
### Task D2 — Agent strip v2
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` (add `Turns`, `TokensFormatted`, `ElapsedFormatted`, `DiffAdditions`, `DiffDeletions`, `CommitsOnBranch` if not present — most exist)
|
||||
- `src/ClaudeDo.Ui/Views/Islands/AgentStripView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (diff meter bar style)
|
||||
|
||||
- [ ] Layout (three rows):
|
||||
- Row 1: pulsing status dot + status label (`RUNNING` etc.) + mono model name + right-aligned stop button (only visible when Running).
|
||||
- Row 2: `WORKTREE` section label + worktree path mono, with a copy-to-clipboard `PathIcon Data="{Icon.Copy}"` button at the end.
|
||||
- Row 3: Branch line — `PathIcon Data="{Icon.GitBranch}"` + branch mono + arrow `←` + `main` + commits count chip.
|
||||
- Row 4: `DIFF` label + `+{additions}` (moss) + `−{deletions}` (blood) + a slim 4px progress-meter `Rectangle` showing additions vs deletions ratio (moss-filled portion).
|
||||
- [ ] Action buttons row: `Open diff`, `Worktree`, external-link `→` (opens file:// to worktree path in OS explorer).
|
||||
- [ ] Agent strip should use `AgentStripStyle.Classes` bound to the running status so colors shift.
|
||||
- [ ] Commit: `style(ui): agent strip with worktree panel and diff meter`
|
||||
|
||||
### Task D3 — Session terminal styling
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Views/Islands/SessionTerminalView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (terminal header, log-line columns, `LIVE` chip)
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/LogLineViewModel.cs` (add `TimestampFormatted` property)
|
||||
|
||||
- [ ] Top bar of the terminal `Border`: three colored dots (red/yellow/green, 8px `Ellipse`) + `claude-session · {branch}` mono text + right-aligned `LIVE` chip (moss bg, white text, pulsing animation when a task is actively running).
|
||||
- [ ] Log lines: two-column layout — timestamp (mono 10px, `TextFaint`, fixed 70px width) + kind marker (e.g. `TOOL`, `CLAUDE`, `OUT`) + text. Kind marker uses attribute selector `[Tag=log-tool]`, color-mapped.
|
||||
- [ ] Line number/timestamp: add `TimestampFormatted` to `LogLineViewModel` populated as `DateTime.Now.ToString("HH:mm:ss")` on construction. (If real timestamps arrive via SignalR later, swap source.)
|
||||
- [ ] Ensure auto-scroll still works (existing logic).
|
||||
- [ ] Commit: `style(ui): session terminal header, line columns, LIVE chip`
|
||||
|
||||
### Task D4 — Subtasks, notes, metadata footer
|
||||
|
||||
**Files:**
|
||||
- `src/ClaudeDo.Ui/Views/Islands/DetailsIslandView.axaml`
|
||||
- `src/ClaudeDo.Ui/Design/IslandStyles.axaml` (subtask row style)
|
||||
- `src/ClaudeDo.Ui/ViewModels/Islands/DetailsIslandViewModel.cs` (delete-task command, close-detail command)
|
||||
|
||||
- [ ] Subtasks: each row is a compact `Border` with rounded 6px, hover background. Check is an `Ellipse` matching the task-row style (not default WinForms-style CheckBox). Completed items get strike-through + fade.
|
||||
- [ ] Notes `TextBox`: `Surface2` bg, 12px padding, watermark `Notes...`, auto-saves on `LostFocus` (call repository `Update`).
|
||||
- [ ] Bottom metadata bar (sticky at the bottom of the Details island — anchor via `DockPanel.Dock="Bottom"`):
|
||||
- Left: `PathIcon Data="{Icon.Trash}"` delete button (prompts confirmation before calling `TaskRepository.DeleteAsync`).
|
||||
- Middle: `Created {Month Day}` mono `TextFaint`.
|
||||
- Right: close-details `PathIcon Data="{Icon.X}"` (clears `SelectedTask` on `TasksIslandViewModel`).
|
||||
- [ ] Commit: `style(ui): subtasks, notes, details metadata footer`
|
||||
|
||||
---
|
||||
|
||||
## Execution order
|
||||
|
||||
```
|
||||
Phase A (A1 → A2) [sequential, 1 subagent]
|
||||
↓
|
||||
Phase B, C, D [parallel, 3 subagents, one per phase]
|
||||
↓
|
||||
Final build + smoke
|
||||
```
|
||||
|
||||
Phase A is sequential because it touches `MainWindow.axaml` and `IslandStyles.axaml` root setup.
|
||||
Phases B, C, D each own a distinct island. Only potential conflict: all three add icon geometries to `IslandStyles.axaml`. Mitigation: Phase B is responsible for adding the `StreamGeometry` icon resources (it needs the most). Phases C and D reference those keys without redefining.
|
||||
|
||||
Final pass: run the app, eyeball against the mock, note remaining gaps.
|
||||
Reference in New Issue
Block a user