254 lines
12 KiB
Markdown
254 lines
12 KiB
Markdown
# 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`:
|
||
|
||
```xml
|
||
<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` → `StackPanel` or `ItemsControl`
|
||
- All three columns are islands.
|
||
|
||
### Lists island (left)
|
||
|
||
- Search box: `TextBox Classes="search"` with left-aligned search icon (PathIcon)
|
||
- Nav items: `ItemsControl` bound to a `Lists` collection
|
||
- Each item is `Border Classes="list-item"` (toggle `active` class when selected) containing
|
||
- `PathIcon` (16px)
|
||
- `TextBlock` (list name, 13px)
|
||
- `TextBlock` (count, mono 10px, right-aligned, TextFaintBrush)
|
||
- 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:
|
||
- `TextBox` with placeholder "Add a task…"
|
||
- On Enter: dispatch new task (see ViewModel spec)
|
||
|
||
Task list:
|
||
- `ItemsControl` → `Border Classes="task-row"` per task
|
||
- Row content: `Grid` with columns `Auto,*,Auto`
|
||
- Left: `Ellipse Classes="task-check"` (toggles `done` class on completion) — use a `Button` with a templated Ellipse for keyboard support
|
||
- Middle: `StackPanel` vertical
|
||
- Title: `TextBlock` 14px, strike-through when done
|
||
- Meta row: `StackPanel` horizontal with 8px gap, children:
|
||
- `Border Classes="chip {status}"` (status chip — Running / Review / Error / Queued / Idle)
|
||
- `Border Classes="chip"` with list name
|
||
- `Border 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
|
||
- Right: `Button Classes="icon-btn"` (star)
|
||
- Selection: toggle `selected` class; add a `Rectangle` with `Width=2` as the left accent bar (child of the task-row Border)
|
||
|
||
### Details island (right)
|
||
|
||
Shown when a task is selected. Sections, top to bottom:
|
||
|
||
1. **Header**: task title (editable — `TextBox` with no visible border, `FontSize=18`), list chip, delete icon button
|
||
2. **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)
|
||
3. **Session output** — `Border Classes="terminal"` with a `ScrollViewer` auto-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 + `TextBox` to send messages to the agent
|
||
4. **Subtasks** — `ItemsControl` of checkbox + text rows
|
||
5. **Notes** — multi-line `TextBox`, `AcceptsReturn="True"`
|
||
6. **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)
|
||
|
||
```csharp
|
||
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 `Animation` with opacity + `TranslateTransform.Y`)
|
||
- **Dispatch agent**: "Start agent" button in Details → sets `Agent.Status = Running`, appends sys log "Agent dispatched."
|
||
- **Stop agent**: → `Status = Review` (or `Error` on 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 island
|
||
- `Cmd/Ctrl+N` focuses add-task
|
||
- `Space` toggles done on selected row
|
||
- `Esc` closes 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 `done` styling
|
||
- 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.
|