Files
ClaudeDo/docs/UI Rewrite/design_handoff_claudedo/README.md

12 KiB
Raw Blame History

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: ScrollViewerStackPanel 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:

  • ItemsControlBorder 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 stripBorder 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 outputBorder 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. SubtasksItemsControl 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)

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.