12 KiB
ClaudeDo — Avalonia Handoff
Overview
ClaudeDo is an agent dispatcher for Claude Code: a Windows desktop app that presents background coding agents as tasks. Each task has a title, a list, a git worktree/branch, an agent status (idle / queued / running / review / error), a live session log, and a diff. The UI is organised as three floating islands (Lists / Tasks / Details) over a dark "sea" background, Windows-11 style.
The bundled HTML file is a design reference, not production code. Your job is to recreate it as a native Avalonia app — match the look, feel, and interaction model, but use idiomatic AXAML, Avalonia controls, and whatever MVVM / ReactiveUI / CommunityToolkit patterns your codebase already uses.
Fidelity
High-fidelity. All colors, typography, spacing, corner radii, shadows, and interaction states are final. Recreate pixel-perfectly using Avalonia primitives. The one exception: motion — CSS animations translate approximately to Avalonia Transitions / Animation; the durations and easings in Tokens.axaml are the intent.
What's in this package
| File | Purpose |
|---|---|
Tokens.axaml |
ResourceDictionary — colors, brushes, spacing, corner radii, typography, shadows, motion durations. Merge this first in App.axaml. |
IslandStyles.axaml |
Styles — classed styles for Island, Chip, Button, TextBox, TaskRow, AgentStrip, Terminal, ListItem. Depends on Tokens.axaml. |
ClaudeDo.html |
The live design reference — open it in a browser to see behavior, hover states, animations, modals. |
ClaudeDo-standalone.html |
Fully offline single-file version (no network). Ship this with the handoff. |
app.jsx, islands.jsx, modals.jsx, icons.jsx, data.jsx, styles.css |
Source of the reference. Read styles.css for any measurement you need to verify; read the JSX for component structure and state transitions. |
ComponentSpec.md |
This file section below — maps every visual element to the AXAML control you should use. |
How to wire the tokens
In App.axaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceInclude Source="avares://YourApp/Design/Tokens.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://YourApp/Design/IslandStyles.axaml" />
</Application.Styles>
Pack Inter Tight (sans) and JetBrains Mono (mono) as embedded resources and reference them via avares://YourApp/Assets/Fonts/#Inter Tight in Tokens.axaml if the system-font fallback isn't good enough.
Window chrome
The reference shows a Windows-11-style app in a chromeless window with a custom title bar and taskbar. For a native Avalonia app, use SystemDecorations="None" + ExtendClientAreaToDecorationsHint="True" and draw your own title bar, OR use the platform chrome — the islands-over-sea metaphor works either way. The taskbar strip at the bottom of the reference is decorative; drop it.
Layout
Root window:
┌─────────────────────────────────────────────────────────┐
│ TitleBar │
├─────────────┬──────────────────────────┬────────────────┤
│ Lists │ Tasks │ Details │
│ (260px) │ (1fr, min 340px) │ (320px) │
│ │ │ hides <1100px │
└─────────────┴──────────────────────────┴────────────────┘
Use a Grid with 3 columns: 260,*,320. Collapse the Details column when ActualWidth < 1100 via a bound ColumnDefinition.Width. Between columns and around the grid, add 14px gap — put each island in a Border Classes="island" with Margin="7" so you get the island-to-island gap naturally.
Background of the grid cell: apply DesktopBackgroundBrush from tokens, plus a subtle radial-gradient overlay via a Border with an opacity mask if desired.
Components
Island (base container)
Border Classes="island"- Contents: header section + scrollable body
- Header: inner
Border Classes="island-header"with an eyebrow label (mono, uppercase, tracking 1.4) and a title - Body:
ScrollViewer→StackPanelorItemsControl - All three columns are islands.
Lists island (left)
- Search box:
TextBox Classes="search"with left-aligned search icon (PathIcon) - Nav items:
ItemsControlbound to aListscollection- Each item is
Border Classes="list-item"(toggleactiveclass when selected) containingPathIcon(16px)TextBlock(list name, 13px)TextBlock(count, mono 10px, right-aligned, TextFaintBrush)
- Each item is
- Lists shown: My Day, Important, Planned, Running, Review, Tasks (by project name)
Tasks island (middle)
Header row:
- Eyebrow: weekday date ("MONDAY · APR 28")
- Title: "My Day" (or current list name) — 24px semibold
- Subtitle: "{N} open · {N} running · {N} in review" — mono 11px TextMute
- Right side: icon buttons (sort, filter, show-completed toggle)
Add-task row:
TextBoxwith placeholder "Add a task…"- On Enter: dispatch new task (see ViewModel spec)
Task list:
ItemsControl→Border Classes="task-row"per task- Row content:
Gridwith columnsAuto,*,Auto- Left:
Ellipse Classes="task-check"(togglesdoneclass on completion) — use aButtonwith a templated Ellipse for keyboard support - Middle:
StackPanelvertical- Title:
TextBlock14px, strike-through when done - Meta row:
StackPanelhorizontal with 8px gap, children:Border Classes="chip {status}"(status chip — Running / Review / Error / Queued / Idle)Border Classes="chip"with list nameBorder Classes="chip"with mono branch name (e.g.agent/auth-pool)Border Classes="chip"with diff stats (+142 −86)- Live tail of latest agent output when running — use
TextTrimming="CharacterEllipsis"in a fixed-width container
- Title:
- Right:
Button Classes="icon-btn"(star)
- Left:
- Selection: toggle
selectedclass; add aRectanglewithWidth=2as the left accent bar (child of the task-row Border)
Details island (right)
Shown when a task is selected. Sections, top to bottom:
- Header: task title (editable —
TextBoxwith no visible border,FontSize=18), list chip, delete icon button - Agent strip —
Border Classes="agent-strip {status}":- Row 1: status indicator dot + status label ("Running" / "Review" / etc.) + model name ("claude-sonnet-4.5") + turns + tokens + elapsed
- Row 2: Worktree path (mono, truncating)
- Row 3: Branch → Base ("agent/auth-pool ← main") + commit count + diff stats
- Buttons: "Open diff" / "Worktree" / "Stop" (when running) / "Approve & merge" (when review)
- Session output —
Border Classes="terminal"with aScrollViewerauto-scrolled to bottom:- Each line is a
TextBlock Classes="log-{kind}"— kinds: sys, tool, claude, stdout, stderr, done, msg - Below it, a prompt input:
[you]prefix +TextBoxto send messages to the agent
- Each line is a
- Subtasks —
ItemsControlof checkbox + text rows - Notes — multi-line
TextBox,AcceptsReturn="True" - Metadata — created date, last activity, tags (readonly chips)
Modals
Two modals in the reference: Diff and Worktree. Use Window with WindowStartupLocation="CenterOwner" and a scrim (Border over the main window with Background="#BF030504" + blur via OpacityMask or a child Grid). Or use Dialog if your shell has one.
Diff modal: left sidebar of files (each with +N −N stats), right pane with syntax-colored hunks. Use two ListBox-style panels side-by-side. For lines: del = red tinted, add = green tinted, ctx = neutral. Left gutter columns: old line number, new line number, sign (+ / − / space).
Worktree modal: folder tree with M (modified) / A (added) badges. TreeView fits naturally.
Status mapping
| Status | Chip color | Icon | When |
|---|---|---|---|
| idle | TextMute | circle | Task created, agent not dispatched |
| queued | Sage | dots | Agent queued behind others |
| running | Accent (moss) | pulse dot | Agent actively working |
| review | Peat | eye | Agent finished; awaiting approval |
| error | Blood | exclamation | Agent failed |
State & interactions
Task model (MVVM)
public class TaskItem : ReactiveObject {
string Id, Title, List;
bool Done, Starred, MyDay;
DateTime? Due, Created;
string Notes;
List<string> Tags;
List<SubTask> Subtasks;
AgentState Agent; // null if not dispatched
}
public class AgentState : ReactiveObject {
AgentStatus Status; // Idle | Queued | Running | Review | Error
string Model, Worktree, Branch, BaseBranch;
int Commits, Turns, Tokens;
DiffStats Diff; // Files, Additions, Deletions
DateTime? StartedAt, FinishedAt;
ObservableCollection<LogLine> Log;
}
Key interactions
- Toggle done: click checkbox → flip
Done, animate strike-through (0.2s ease-out) - Select task: click row → set
SelectedTask; details island rebinds - Add task: Enter in the add-task textbox → prepend new task; scroll list to top; 0.3s fade-in animation on the new row (use
Animationwith opacity +TranslateTransform.Y) - Dispatch agent: "Start agent" button in Details → sets
Agent.Status = Running, appends sys log "Agent dispatched." - Stop agent: →
Status = Review(orErroron failure), appends sys log - Send prompt: Enter in prompt input → append
[you] {msg}to log - Open diff / worktree: opens modal; Esc closes
Keyboard
/focuses the search box in the Lists islandCmd/Ctrl+Nfocuses add-taskSpacetoggles done on selected rowEsccloses any open modal
Animations
- Task-row hover: background transition 0.1s
- Task-row add: 0.3s opacity + slight Y-slide
- Task-row complete: 0.25s strike-through + fade to
donestyling - Running status dot: infinite pulse (opacity 0.4 → 1.0, 1.2s)
- Modal open: 0.18s opacity + scale (0.98 → 1.0)
- Backdrop: 0.15s opacity fade
Responsive
< 1100px: hide Details island; details open as a transient panel or modal on task select< 780px: hide Lists island; use a hamburger drawer
Design tokens (reference)
All final values live in Tokens.axaml. Reproduced here for reading:
Surfaces: #0A0E0C void · #0D1311 deep · #161D1A surface · #1C2422 surface-2 · #222B28 surface-3 · #2A3330 line
Text: #E4EBE4 primary · #9AA8A0 dim · #6B7973 mute · #4A5550 faint
Accents: #7C9166 moss (primary) · #8B9D7A sage · #D4A574 peat · #C87060 blood
Spacing: 4, 8, 12, 14 (island gap), 18, 24
Corner radii: 14 (island) · 12 (modal) · 10 (chip) · 8 (task row, input) · 6 (button) · 999 (pill)
Typography: Inter Tight (sans), JetBrains Mono (mono). Scale: 10 (eyebrow) / 11 (mono, micro) / 13 (body) / 14 (task title) / 18 (h3) / 24 (h2) / 32 (h1)
Shadows:
- Island:
0 20 40 #59000000, 0 2 4 #4D000000 - Modal:
0 40 80 #B2000000
Motion: 120ms (fast) / 180ms (base) / 300ms (slow). Easing: cubic-bezier(0.4, 0, 0.2, 1) — use Avalonia's CubicEaseOut or a custom SplineEasing.
Acceptance checklist
- Three-island layout with correct spacing and grid collapse at <1100px
- Lists sidebar with icons, counts, search, active state
- Task rows with checkbox, title, meta chips (status/list/branch/diff), star
- Task selection updates Details island
- Agent strip shows status, model, turns, tokens, elapsed, worktree, branch
- Session terminal renders all log kinds with distinct colors, auto-scrolls, accepts prompt input
- Diff modal with file sidebar and tinted add/del lines
- Worktree modal with M/A badges
- Status chip tints match the spec
- Fonts: Inter Tight + JetBrains Mono packed and applied
- Motion: task add/toggle, running pulse, modal open, hover transitions
- Keyboard shortcuts wired
Questions / contact
The HTML reference is the source of truth for any visual ambiguity. Open ClaudeDo-standalone.html and inspect directly.