15 KiB
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 inIslandStyles.axamlfor reusable styling; inline AXAML setters for one-off values only. - Icons: use
Projektion.AvaloniaPathIconwithData="{StaticResource IconKey}". Define newStreamGeometryresources inIslandStyles.axamlunder 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 toShell.Lists.SelectedList.Name.ToUpperInvariant(). - Middle: draggable strip (
PointerPressed → BeginMoveDrag). - Right: three frameless icon buttons (minimize / maximize-restore / close). Close button hover turns
BloodBrush. UsePathIconwith inlineStreamGeometryfor the Lucide-style icons:Minus,Square,X— the exact SVGdstrings are inicons.jsx.
- Left: brand block — checkbox-style green glyph +
-
Title bar height: 36px, background
DeepBrush, bottom border 1pxLineBrush. -
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
Borderfilling the whole row with a subtle radial gradient fromDeepBrush(center) toVoidBrush(edges). Use aRadialGradientBrushwith 2 stops; keep opacity light. -
In
IslandStyles.axaml, bump theBorder.islandBoxShadowto match the tokenIslandShadowvalue 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(addStreamGeometryicon resources at the top) -
src/ClaudeDo.Ui/ViewModels/Islands/ListsIslandViewModel.cs(mapIconKeystrings → resource keys, add section grouping) -
src/ClaudeDo.Ui/Views/Islands/ListsIslandView.axaml -
Extract the SVG path
dstrings fromicons.jsxfor:Sun,Activity(pulse),Star,Calendar,Eye,Inbox,Folder,Search,Plus,MoreHorizontal. Define each as anx:Key="Icon.Sun"StreamGeometryinIslandStyles.axaml. -
Change Lists eyebrow from
WORKSPACEtoNAVIGATOR. -
Add two section-header rows in the ItemsControl:
SMART LISTS(above items ofKind=Smart+Virtual) andMY LISTS(above items ofKind=User). Simplest approach: two separateItemsControls bound to filtered subsets; or wrap items in aCollectionViewSourcegrouping. Pick the simplest working approach. -
Per-item icon: bind
PathIcon Data="{DynamicResource Icon.{IconKey}}"via a tinyIconGeometryConverter(takesIconKeystring → looks up resource). Icon color:TextMutedefault;AccentBrush(moss) whenIsActive. -
User-list items: use a 6px circle with
MossBrush/PeatBrush/SageBrushdot 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 +AccentBrushicon +TextBrushtext. -
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 aGrid ColumnDefinitions="Auto,*,Auto"— leftPathIcon Data="{Icon.Search}"(14px,TextFaint), middle TextBox, rightBorder Classes="kbd"with⌘K(orCtrl Kon Win). Thekbdchip: mono 10px,Surface2bg,LineBrushborder, padding6,2, radius 4. -
Under the items list, add:
+ New listbutton — 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} / localmono dim, rightPathIcon 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 reviewto{Weekday}, {Month} {Day} · {open} opento 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. 20pattern. Title remains list name. -
Right-side icon toolbar: three
Button Classes="icon-btn"—Sorticon,Eyeicon (toggle completed),MoreHorizontal. Icons: pull paths fromicons.jsx. WireEyeto anIsShowingCompletedobservable (persist in a private field for now; no DB change). -
Add-task row: wrap the
TextBoxin aBorderwithSurface2bg, rounded 8px, 14px padding. Prepend a circularPathIcon Data="{Icon.Plus}"(20px circle,Surface3bg). Append aBorder Classes="kbd"withENTERtext (only visible whenNewTaskTitlehas focus — bind visibility toTextBox.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
MossBrushor similar) + list name. - Branch chip —
PathIcon Data="{Icon.GitBranch}"(12px) + branch name (mono 10px). - Diff chip —
+Nmoss ++−Mblood. - Tags — one chip per tag (
#refactorstyle,Surface2bg, mono 10px,TextDim).
-
Selected state: add 2px
AccentBrushleft border on the row Border whenIsSelected=true(style selectorBorder.task-row.selected). Background shifts toAccentSoftBrush. -
Done state: strike-through title + fade opacity to 0.5. Add
Border.task-row:has(.done)equivalent via the existingDonebinding — simpler: aTextBlockstyle selector that flipsTextDecorations. -
Live-tail row (only visible when
Status == RunningandLiveTail != null): aBorderunder the chip row with mono 11px ellipsized text + a slim 3px progressRectanglewithMossBrush. For now the progress is static 30% — wire it to a futureProgressFractionproperty (leave as 0.3 fallback). -
Ensure
task-rowBorder hasTransitionsforBackground+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
Itemsinto three sub-collections:OverdueItems— tasks withScheduledFor < Todayand not Done.OpenItems— remaining not-Done tasks.CompletedItems— tasks withDone=true.
-
Expose as three
ObservableCollection<TaskRowViewModel>on the VM. Recompute insideLoadForList. -
View: three
ItemsControls stacked in aStackPanel, each preceded by a section headerTextBlock—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(exposeTaskIdBadgelike#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 ofTask.Id, monoTextFaint). - Title: keep editable title
TextBoxbut reduce size and match mock.
- Eyebrow
-
Under header, a new "task strip" row:
Ellipsecheckbox (bound toTask.Donetoggle) + 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(addTurns,TokensFormatted,ElapsedFormatted,DiffAdditions,DiffDeletions,CommitsOnBranchif 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 (
RUNNINGetc.) + mono model name + right-aligned stop button (only visible when Running). - Row 2:
WORKTREEsection label + worktree path mono, with a copy-to-clipboardPathIcon Data="{Icon.Copy}"button at the end. - Row 3: Branch line —
PathIcon Data="{Icon.GitBranch}"+ branch mono + arrow←+main+ commits count chip. - Row 4:
DIFFlabel ++{additions}(moss) +−{deletions}(blood) + a slim 4px progress-meterRectangleshowing additions vs deletions ratio (moss-filled portion).
- Row 1: pulsing status dot + status label (
-
Action buttons row:
Open diff,Worktree, external-link→(opens file:// to worktree path in OS explorer). -
Agent strip should use
AgentStripStyle.Classesbound 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,LIVEchip) -
src/ClaudeDo.Ui/ViewModels/Islands/LogLineViewModel.cs(addTimestampFormattedproperty) -
Top bar of the terminal
Border: three colored dots (red/yellow/green, 8pxEllipse) +claude-session · {branch}mono text + right-alignedLIVEchip (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
TimestampFormattedtoLogLineViewModelpopulated asDateTime.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
Borderwith rounded 6px, hover background. Check is anEllipsematching the task-row style (not default WinForms-style CheckBox). Completed items get strike-through + fade. -
Notes
TextBox:Surface2bg, 12px padding, watermarkNotes..., auto-saves onLostFocus(call repositoryUpdate). -
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 callingTaskRepository.DeleteAsync). - Middle:
Created {Month Day}monoTextFaint. - Right: close-details
PathIcon Data="{Icon.X}"(clearsSelectedTaskonTasksIslandViewModel).
- Left:
-
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.