From 82f716d3357105c7ff681dc08983701ef603db84 Mon Sep 17 00:00:00 2001 From: iamtoruk Date: Fri, 10 Jul 2026 14:28:16 -0700 Subject: [PATCH] docs(desktop): design spec + wireframes for standalone CodeBurn Desktop app Standalone Electron app rendering the v6 "indigo instrument" wireframes, fed by the codeburn CLI via the menubar JSON contract (spawn codeburn --json, decode, poll). Six sections: Overview, Spend, Optimize, Models, Plans, Settings/Devices. Data aggregation stays CLI-side; renderer never fakes data. --- codeburn-desktop-wireframes.html | 731 +++++++++++++++++++++++++++++++ docs/design/codeburn-desktop.md | 182 ++++++++ 2 files changed, 913 insertions(+) create mode 100644 codeburn-desktop-wireframes.html create mode 100644 docs/design/codeburn-desktop.md diff --git a/codeburn-desktop-wireframes.html b/codeburn-desktop-wireframes.html new file mode 100644 index 00000000..bebb5d6e --- /dev/null +++ b/codeburn-desktop-wireframes.html @@ -0,0 +1,731 @@ +CodeBurn Desktop — Wireframes + + +
+
+

CodeBurn Desktop — indigo instrument

+

Wireframes v6. Cool near-black system with clean gradients: indigo→violet as the single UI gradient, blue/purple/lavender/cyan as the model-series palette, capsule gradient bars, and a Sankey cost-flow as the premium signature. Machined base kept: hairlines, mono digits, keycaps, scope labels.

+

v6.1 · 2026-07-10 · references: Dash0 AI-coding dashboard (series palette, card headers, Sankey), gradient-capsule chart, ambient glow · 30-day default period (7 bars never balloon again) · shiny orange CodeBurn brand mark · full period vocabulary: Today / 7D / 30D / Month / 6M / Custom

+
+ +
+

Design language

+

One gradient for the UI (indigo→violet: active rail, primary button, plan tracks, toggle). Data gets the series palette. Trouble is text color only — amber pace, red overage, mint savings.

+
+
Canvas#0B0D13 + ambient glow
+
Panel#12151F / head #161A27
+
UI gradient#5B8CFF → #8B7CF6
+
Opus#5B8CFF
+
Sonnet#8B7CF6
+
Haiku#B5A8FF
+
GPT / Codex#4DD8E6
+
Savings#3ECF8E
+
Pace#E8B93E
+
Overage#F26D6D
+
+
    +
  • One UI gradientIndigo→violet marks what's interactive or active. It never colors data.
  • +
  • Series palette for dataEach model owns a hue everywhere — chart, legend, Sankey, table dot. Recognition over decoration.
  • +
  • Capsule barsHistory in muted slate capsules; the emphasized bar gets the blue→cyan gradient and a soft glow.
  • +
  • Card = header + bodyDash0-style header strip carries the title and controls, keeping bodies pure content.
  • +
  • Status is textAmber pace, red overage, mint savings. No dots, no pills, no gradient alarms.
  • +
+
+ + +
+

01 · Overview

+

Ten-second triage. Big clean numbers, one glowing capsule chart, the damage list.

+
+
+ +
+
+
Overview
+ Last 30 days · All providers · 2 devices +
+
Today7D30DMonth6MCustom
+
All providers
+
+
+
+
Today
$6.20
12 sessions
+
Month to date
$312.40
+24% vs June pace
+
Projected month
$968 est
$748 over plans
+
Waste found
$23.60 /wk
3 fixes ready
+
+
+
Daily spendbiggest driver: Opus 4.8 in codeburn, +61% — why ›
+
+
+
32
+
16
+
0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jun 11Jun 18Jun 25Jul 2Jul 10
+
+
+
+
Most expensive sessionsSee all ›
+
+
01
Refactor parser OOM buffer readercodeburn · Wed 14:32 · Opus 4.8 · 41 turns
$8.41
+
02
Device pairing e2e + mTLS handshakecodeburn · Tue 10:05 · Opus 4.8 · 33 turns
$6.12
+
03
Honeycomb logo iterationsagentseal-dash · Mon 16:48 · Sonnet 5 · 27 turns
$4.98
+
+
+
+
⌘KCommand⌘EExport viewDrill inrefreshed 30s ago
+
+
+
+

Notes. Capsule bars: slate history, gradient + glow on the peak, plain blue on second-highest — the eye finds Monday instantly. Session rows carry the model's series dot. Deltas: blue = neutral info, amber = pace, red = overage, mint = good.

+
+ + +
+

02 · Spend

+

Stacked series by model, then the signature: cost flowing model → project as a Sankey. Every ribbon is a filterable claim.

+
+
+ +
+
+
Spend
+ Last 30 days · All providers +
+
Today7D30DMonth6MCustom
+
All providers
+
+
+
ProjectsActivityToolsMCPSubagents
+
+
+
Daily spend by model
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Opus 4.8 + Sonnet 5 + Haiku 4.5 + GPT-5.5 Codex +
+
+
+
+
By project16 projects
+
+
01
codeburn124 sessions
$246.10
+
02
agentseal-dash74 sessions
$141.30
+
03
hermes-eval38 sessions
$77.85
+
04
client-api est29 sessions · Cursor
$58.60
+
05
dotfiles21 sessions
$34.20
+
+
+
+
+
Cost flow · model → projectclick a ribbon to filter
+
+ + + + + + + + + + + + + + + + + + + + + + + + Opus 4.8 · $331.20 + GPT-5.5 · $137.90 + Sonnet 5 · $108.63 + Haiku 4.5 · $34.75 + codeburn · $246.10 + agentseal-dash · $141.30 + hermes-eval · $77.85 + others · $147.23 + +
+
+
+
⌘KCommand⌘EExport viewSwitch lens
+
+
+
+

Notes. The Sankey is the premium signature — each ribbon inherits its model's hue and fades toward the project side; clicking one scopes every panel to that model × project pair. Stacked bars share the same series palette, so the legend teaches colors once for the whole app.

+
+ + +
+

03 · Optimize

+

Waste, reverts, abandoned, fixes. Findings ranked by recoverable dollars, evidence in mono, savings in mint.

+
+
+ +
+
+
Optimize
+ Last 30 days · All providers +
+
Today7D30DMonth6MCustom
+
All providers
+
+
+
Waste $94.40Reverts $107.00Abandoned $65.40Fixes 3
+
+
+
+ 01 +
+ Opus is doing your small talk + 38% of Opus 4.8 turns had zero tool calls — pure conversation. Sonnet answers the same turns at 1/5 the price. +
856 turns · 124 sessions › · 856 × $0.052 − sonnet $0.011
+
/model sonnetCopy fix
+
+ $9.10/wk +
+
+ 02 +
+ Cache hit is low in agentseal-dash + 41% vs your 68% average — sessions restart context instead of resuming. +
72 sessions › · 37.6M uncached reads × $0.93/M delta
+
claude --continueCopy fix
+
+ $8.70/wk +
+
+ 03 +
+ Twelve sessions went nowhere + $39.20 across 12 abandoned sessions with no surviving edits — nearly all started without a stated goal. +
no surviving edits vs git · 12 sessions › · full list under Abandoned
+
+ $5.80/wk +
+
+
+
+
⌘CCopy fixOpen sessions
+
+
+
+

Notes. The old Yield analysis lives under Reverts and Abandoned (git-correlated, per codeburn yield). Segments carry their dollar totals so the tab row itself is a summary.

+
+ + +
+

04 · Models

+

The pricing table with each model's series dot. Compare is a mode; unpriced rows carry their own fix.

+
+
+ +
+
+
Models
+ Last 30 days · All providers +
+
By modelBy task
+ Compare… +
+
+
+
+ + + + + + + + + +
ModelCallsInputOutputCache readCostSaved
Claude Opus 4.84,812152.6M9.64M119.4M$331.20$86.40
GPT-5.5 Codex2,70486.9M7.52M45.1M$137.90$35.10
Claude Sonnet 53,31877.7M6.08M63.3M$108.63$27.90
Claude Haiku 4.51,27032.5M2.84M16.9M$34.75$5.85
my-proxy-model add alias ›1764.8M0.4M$0.00
+
+
+
+
⌘EExport tableSpaceSelect for comparefriendly names · raw IDs on hover
+
+
+
+

Notes. Series dots make the table, charts, and Sankey one continuous vocabulary. By-task nests category rows under each model (models --by-task); Compare… slides a sheet over the table for the selected rows.

+
+ + +
+

05 · Plans

+

Vendor clocks, not analytics periods. The gradient track is UI; the warning is text.

+
+
+ +
+
+
Plans
+ Cycle Jun 15 – Jul 14 · day 26 of 30 +
+
Cycle: Jun 15 – Jul 14
+ Add plan… +
+
+
+
Claude Max$200 / month · claude$186.20 · 93%
+
+
On pace to exceed in 4 days — projected $214 by Jul 14
+
+
+
Cursor Pro$20 / month · cursor$8.20 · 41%
+
+
On track
+
+
+
API usagecodex · pay as you go, no plan$31.02 this cycle
+
+
+
+
Edit planpresets: Claude Max/Pro · Cursor Pro · custom
+
+
+
+

Notes. One overage line per provider plan, mirroring the CLI. Past 100% the fill switches to the red gradient and the overage renders in dollars beside it.

+
+ + +
+

06 · Settings — with Devices inside

+

Own rail, deep-linkable. Pairing is the only amber on screen; Approve is the view's single gradient button.

+
+
+ +
+
Settings
+
+ +
+
+
This device
+
Toruk's MacBook ProVisible on the local network as codeburn-mbp.local
Visibility: on
+
+
+
Discovered nearbylistening…
+
Mac Studiowants to pair · fingerprint 7F:2A:…:C4
Approve
+
+
+
Paired
+
+
toruk-minilast pull 2h ago · 34 sessions · $41.20 this month
Pull now
+
Combine usage from paired devicesscope captions gain “· 2 devices” when on
+
+
+
+
+
escBackpairing uses mutual TLS · approve-style, no PIN
+
+
+
+

Notes. No period chrome — configuration is timeless. Approve-style pairing mirrors the CLI: a human verifies the fingerprint, no PIN.

+
+ + +
+

07 · States & trust

+

Missing tools, denied permissions, empty data are normal for local-first scanning. Text status only.

+
+
+
Empty / permission
+
Claude1,412 sessions
+
Codex688 sessions
+
Cursorpermission denied — grant Full Disk Access
Fix…
+
Devinnot installed
+
+
+
Loading
+ Scanning 30 providers… +

First scan reads every session file; later scans are incremental via the daily cache.

+
+
+
+
+
+
Provenance
+ $31.02 est +

Cursor reports messages without token counts; cost is estimated from message sizes and LiteLLM prices.

+ + +
+
+
+ +
+

Behavior spec

+

+ Inactive window: gradient rail and selection dim to slate. + Focus: indigo focus rings, never browser outlines. + Hover: row fills at 5% lavender; chart hover = column highlight + mono value popover; Sankey ribbons brighten on hover and filter on click. + Scrolling: native overlay scrollbars. + ⌘K palette: filters, export, view jumps, session search. + Series rule: each model's hue is global — chart, Sankey, dots, legends. UI gradient never colors data. + Light theme: same ladder on paper (#FAFBFE, panels white, hairlines 6% indigo-black) — implementation deliverable, same tokens. + Export: ⌘E everywhere, scoped to current filters. + Motion (codex): bars rise on view entry, Sankey ribbons draw left→right, active rail glides — 120–180ms, no bounce. + Depth (codex): cards carry a 1px top-edge light and faint bottom occlusion; active controls get inner highlight + subtle bloom, centers stay dark; 1–2% canvas noise to kill flat Electron darkness. + Rendering (codex): charts locked to device pixel ratio, fixed bar geometry, legends exactly matching series colors — test at 1×/2×. + Numerals: aligned decimals, muted currency symbols, deltas enlarged only when meaningful. + Period control: all six options shown per product decision; codex's alternative (segmented Today·7D·30D·Month + “More ▾” holding 6M/Custom) is on file if the toolbar ever feels crowded. + Orange discipline (codex): the flame mark stays the single warm object — never data, progress, status, nav, or chart highlights. +

+
+ + +
diff --git a/docs/design/codeburn-desktop.md b/docs/design/codeburn-desktop.md new file mode 100644 index 00000000..8db2ac8f --- /dev/null +++ b/docs/design/codeburn-desktop.md @@ -0,0 +1,182 @@ +# CodeBurn Desktop — Design Spec + +**Date:** 2026-07-10 +**Branch:** `feat/desktop-app` (cut from `origin/main`, v0.9.15) +**Design reference:** `codeburn-desktop-wireframes.html` (v6.1 "indigo instrument", repo root) +**Status:** Approved design → implementation planning + +--- + +## 1. Summary + +Build **CodeBurn Desktop** — a standalone, resizable native desktop application (Electron) that +renders the approved v6 "indigo instrument" wireframes. It is a first-class app in the vein of +Claude Desktop / the Codex app, **distinct from**: + +- the `dash/` **web dashboard** (React app served by `codeburn web`) — untouched, unrelated; +- the `mac/` + `gnome/` **menubar** apps — small tray popovers. + +The desktop app is local-first: it never sends data anywhere, and it does **not** re-implement +usage analytics. All aggregation stays in the `codeburn` CLI; the app is a view layer that spawns +the CLI and renders its JSON — the same contract the menubar apps use today. + +## 2. Goals / Non-goals + +**Goals** +- A resizable window with six sections: **Overview · Spend · Optimize · Models · Plans · Settings/Devices**. +- Pixel-faithful to the approved wireframe (the wireframe CSS is the design system, ported verbatim). +- Data via the **menubar contract**: spawn `codeburn … --json`, decode, poll on a 30s timer + on demand. +- Reuse existing CLI JSON where it exists; add small new `--json` emitters where the wireframe needs + data that the CLI doesn't expose yet. **No faked/stubbed data in the renderer.** + +**Non-goals (this milestone)** +- Signed/notarized distributable (`.dmg`/`.app`/installer). Milestone 1 runs locally (`electron .`); + packaging (electron-builder + notarization) is an explicit follow-up. +- Touching `dash/`, `mac/`, `gnome/`, or the existing Tauri popover. +- Auto-update, telemetry, cloud sync, multi-window. + +## 3. Architecture + +New top-level directory `app/` in the monorepo: + +``` +app/ +├── package.json # own package: electron, vite, react 19, typescript +├── electron/ +│ ├── main.ts # BrowserWindow lifecycle, 30s poll timer, IPC handlers +│ ├── cli.ts # resolve codeburn binary path + spawn `codeburn … --json` (argv, no shell) +│ └── preload.ts # contextBridge → window.codeburn.{getOverview,getModels,getPlans,getDevices,…} +├── renderer/ +│ ├── index.html +│ ├── main.tsx +│ ├── App.tsx # sidebar nav + window chrome (period/provider), section router (local state) +│ ├── styles/ +│ │ └── indigo.css # wireframe CSS ported verbatim (tokens + component classes) +│ ├── sections/ +│ │ ├── Overview.tsx Spend.tsx Optimize.tsx Models.tsx Plans.tsx Settings.tsx +│ ├── components/ # shared: Window chrome, Panel, StatCard, CapsuleChart, StackedBars, +│ │ # Sankey, ListRow, Track, SegTabs, etc. (extracted from wireframe) +│ ├── lib/ +│ │ ├── ipc.ts # typed wrappers over window.codeburn.* +│ │ └── types.ts # TS types mirroring each CLI JSON payload +│ └── hooks/ # useOverview(period), useModels(), … (poll + cache) +└── vite.config.ts +``` + +**Process model** +- **Main process** is the only place that touches the CLI or the filesystem. It: + - resolves the `codeburn` binary path (persisted-path file → `PATH` fallback across brew/nvm/volta/asdf, + mirroring `mac/Sources/CodeBurnMenubar/Security/CodeburnCLI.swift`); + - spawns `codeburn --json …` as plain argv (no shell), with a hard timeout (~45s) and a + cap on concurrent spawns; + - exposes IPC handlers per data need; re-polls every 30s and on user action (period/provider change, + manual refresh); + - handles the "CLI not found / non-zero exit / bad JSON" states → surfaces the wireframe's empty / + permission-denied states (§7 "States & trust"). +- **Renderer** runs with `contextIsolation: true`, `nodeIntegration: false`. It receives typed data over + IPC and renders. No Node, no direct spawn. +- **preload** exposes a minimal, typed `window.codeburn` surface via `contextBridge`. + +**Binary resolution** reuses the menubar approach; if `codeburn` cannot be found, the app shows a +first-run "point me at your codeburn CLI" state rather than crashing. + +## 4. Data contract (the menubar pattern, generalized) + +The app spawns the CLI per section and decodes JSON. Aggregation is 100% CLI-side. + +| Section | CLI invocation | Payload | Exists today? | +|---|---|---|---| +| Overview | `codeburn status --format menubar-json --period 30days [--provider X]` | `MenubarPayload` (`src/menubar-json.ts`) | ✅ | +| Spend (bars, projects) | same `MenubarPayload` (`current.topProjects`, `history.daily`) | — | ✅ (⚠ per-model-per-day may need `history.daily` enrichment) | +| Spend (Sankey model→project) | **new** `codeburn spend --format flow-json` (or extend menubar payload) | model×project cost matrix | ⚠ **new emitter** | +| Optimize (waste) | `MenubarPayload.optimize` | findings | ✅ | +| Optimize (reverts/abandoned) | **new** `codeburn yield --json` | shipped-vs-reverted, abandoned sessions | ⚠ **new emitter** | +| Models | `codeburn models --json` | per-model table (calls/input/output/cache/cost/saved) | ✅ | +| Plans | **new** `codeburn plan --json` (list plans + usage vs cycle) | plans + progress | ⚠ **new emitter** (data exists in `src/config.ts`; needs JSON shape) | +| Settings/Devices | `codeburn devices --json` / `codeburn share status --json` (or the `origin/main` `/api/devices`,`/api/share/status` handlers as reference) | this device, discovered, paired | ✅ commands exist on main; ⚠ confirm/​add `--json` | + +**Rule:** every ⚠ becomes its own CLI-side task in `src/` (TypeScript). The renderer never invents data; +if an emitter isn't ready, its section shows the wireframe's honest loading/empty state, not fake numbers. + +`renderer/lib/types.ts` mirrors each payload as a hand-kept TS type (same convention `dash/` uses — no +shared type package). Where a menubar payload type already exists in `src/menubar-json.ts`, copy it. + +## 5. Styling + +Port the wireframe's hand-written CSS **verbatim** into `renderer/styles/indigo.css`: +- token block (`--pg / --panel / --blue / --purple / --lav / --cyan / --grad / --grad-bar / …`); +- component classes (`.win / .sb / .ni / .bar / .panel / .phead / .stat / .plot / .bars / .sbars / + .li / .track / .seg / .pop / .btn / .rail / .mini / …`). + +Each wireframe section (`
` blocks in the HTML) maps near-directly to a React component. Shared +repeating structures (`.panel`, `.stat`, capsule chart, stacked bars, the Sankey SVG, list rows, plan +tracks) get extracted into `renderer/components/` so the six sections compose them rather than duplicate +markup. This keeps the approved look exactly while giving each section a clean, independently-testable +boundary. **Not** rebuilding in Tailwind — that would risk drift from the approved design and cost time. + +Light theme is a follow-up (wireframe notes a light ladder using the same tokens); ship dark first. + +## 6. Section specs (what each renders) + +1. **Overview** — 4 stat cards (Today, Month-to-date, Projected month, Waste found), the daily-spend + capsule chart (30 bars, gradient+glow on peak), "Most expensive sessions" list. Source: `MenubarPayload` + (`current`, `history.daily`, `topSessions`, `optimize.savingsUSD`); projected month computed from history. +2. **Spend** — lens tabs (Projects/Activity/Tools/MCP/Subagents); stacked daily-by-model bars + "By project" + list; the **model→project Sankey** signature. Sankey needs the new flow emitter. +3. **Optimize** — segment tabs (Waste / Reverts / Abandoned / Fixes) with dollar totals; ranked findings with + evidence (mono) + copy-fix chips. Waste from `optimize`; reverts/abandoned from new `yield --json`. +4. **Models** — pricing table with per-model series dots (calls/input/output/cache/cost/saved), unpriced-row + "add alias" affordance, Compare mode. Source `models --json`. +5. **Plans** — vendor-cycle plan tracks (gradient fill; red past 100%), pace warnings as text. Source new + `plan --json`. +6. **Settings/Devices** — settings rail (General/Providers/Aliases/Plans/Devices/Export/Privacy); Devices pane: + this device, discovered-nearby (approve), paired list, "combine usage" toggle. Source devices/share JSON. +7. **States & trust** (cross-cutting) — per-provider empty/permission-denied/loading/provenance states, wired + from CLI exit codes + partial payloads. + +Window chrome (traffic lights, app mark, sidebar nav with ⌘1–⌘5 / ⌘,, period segmented control, provider +popover, ⌘K affordance, footer hints) is shared across all sections. + +## 7. Task decomposition (for delegated implementation) + +Fable orchestrates and reviews; implementers are **Opus 4.8 (primary)** and **Codex 5.6-high (alternate)**. + +- **T0 — Scaffold** (`app/` Electron+Vite+React, main/preload/renderer, IPC bridge, `indigo.css` port, + shared `components/`, window chrome + sidebar nav, `codeburn` path resolution + one working IPC call). + → Opus 4.8. **Blocks all section work.** +- **T1 — CLI JSON emitters** in `src/` (TypeScript): `spend … flow-json` (Sankey matrix), `yield --json`, + `plan --json`, confirm/add `devices/share … --json`; unit tests in `tests/`. → Codex 5.6-high. Parallel with T0. +- **T2 Overview**, **T3 Spend**, **T4 Optimize**, **T5 Models**, **T6 Plans**, **T7 Settings/Devices** — one + self-contained component + typed hook each. Split across Opus 4.8 + Codex 5.6-high. Each depends on T0; + T3/T4/T6/T7 also depend on their T1 emitter (mock the typed payload until the emitter lands, then swap). +- **T8 — Integration pass**: 30s polling, refresh-on-action, error/empty/loading states end-to-end, + README + `npm run app:dev`. → Opus 4.8. + +Every returned unit comes back to Fable for review before it counts as done. + +## 8. Milestones + +- **M1 (this effort):** app runs via `electron .` (dev), all six sections rendering real CLI data (⚠ gaps + filled by T1 emitters), dark theme, honest states. No packaging. +- **M2 (follow-up):** electron-builder packaging, code-sign + notarize, `codeburn app` launcher subcommand + (mirroring `codeburn menubar`), auto-update, light theme. + +## 9. Testing / verification + +- **CLI emitters (T1):** vitest in `tests/` against fixtures (the repo's established pattern). +- **Renderer:** component render tests for each section against typed mock payloads (Vitest + RTL; add to + `app/`). Typecheck via `tsc --noEmit`. +- **End-to-end smoke:** launch `electron .` against real local session data; verify each section populates, + period/provider switching re-polls, and CLI-missing → first-run state. Verified by Fable before sign-off. + +## 10. Risks / open items + +- **Data-shape drift:** hand-kept types between `src/` and `app/` can diverge (same risk `dash/` carries). + Mitigation: copy menubar types verbatim; T1 emitters ship with typed fixtures the renderer reuses. +- **Sankey matrix cost:** model×project aggregation may be non-trivial in `usage-aggregator.ts`; if it slips, + Spend ships bars+projects first and the Sankey lands in a fast follow. +- **Provider-name / project-path privacy:** local app shows real project names (unlike shared/sanitized data); + fine for a local-only window, but keep the sanitize boundary in mind for any future device-pull views. +- **Electron version / security defaults:** pin Electron, enable `contextIsolation`, disable `nodeIntegration`, + restrict `preload` surface; no remote content loaded. +```