Commit graph

16 commits

Author SHA1 Message Date
Resham Joshi
ea78d36ccf
Windows menu bar and Capacity Dock as a Plugins card, plus desktop fixes (#1507)
Some checks are pending
Tests / desktop (push) Waiting to run
Tests / test (22) (push) Waiting to run
Tests / test (22.13.0) (push) Waiting to run
CI / semgrep (push) Waiting to run
Tests / test (24) (push) Waiting to run
Tests / test (26) (push) Waiting to run
Tests / test-platforms (macos-latest) (push) Waiting to run
Tests / test-platforms (windows-latest) (push) Waiting to run
* feat(windows): configure the menu bar and Capacity Dock as a Plugins card

Windows now configures its tray companion from a card on the Plugins page, the
same place macOS does, instead of two switches in the sidebar corner.

- WindowsCompanionCard mirrors the macOS MenuBarCard: Install/Reinstall,
  Update, Open (show the tray popover), Settings (opens the desktop Menu bar
  settings pane), Quit (stop without uninstalling), Uninstall, plus the
  Capacity Dock toggle, a Running dot, the version, and an info popup.
- MenubarCompanion.status() now reports installed/running/version/outdated/
  canInstall, and new discrete install/open/quit/uninstall actions back the
  card. uninstall only forgets the tray once the CLI removal succeeds, and a
  cancelled elevation prompt is treated as a no-op rather than a failure.
- isNewerVersion sorts a dev build below any release, so a staged release still
  lights Update and a dev build never does.
- codeburn menubar --uninstall reads the product code from the install marker
  or the uninstall registry and runs msiexec /x.
- Remove the bespoke sidebar CompanionSwitches and their dead CSS.
- A dev-only CODEBURN_MENUBAR_RESOURCES override lets the tray install run
  without packaging; the CLI still validates the staged path in full.

* feat(desktop): fold the Capacity Dock settings into the Menu bar pane

One tray rail entry instead of two: MenuBarPane and CapacityDockPane render
together in the Menu bar pane with a clear gap between the two cards, so the
card's Settings button opens both. Drops the now-unreachable 'dock' pane value.

* fix(desktop): stop the Spend cost-flow Sankey from re-rendering forever

The label-fit effect measured the already-truncated text, so a capped label
read as short, un-capped, overflowed the cap, and re-capped without end
(Maximum update depth exceeded). It now estimates each label from its full
name via data-name, and caps only tighten per layout, so the measure, cap and
remeasure cycle is guaranteed to settle instead of oscillating.

* feat(desktop): bundle Inter and JetBrains Mono for one UI on every OS

The app used each platform's system font (SF Pro on macOS, Segoe UI on
Windows), so the two looked different. Bundle Inter for text and JetBrains
Mono for figures and lead the --sans and --mono stacks with them, keeping the
per-locale CJK fallbacks, so Windows and macOS render the same faces.

* desktop: gate tray installed state on support; drop strings and channels the sidebar switches left behind
2026-09-19 18:43:27 -07:00
Resham Joshi
43608c2558
desktop and menu bar: six-language localization (#1493)
The desktop app and the macOS menu bar can now run in English, French, Japanese, Korean, Simplified Chinese and Traditional Chinese. A Language setting in the desktop Settings picks the language or follows the system, and the choice is shared so the menu bar switches with it, relaunching to pick up the language with a retry so a switch never leaves it down.

Every user-facing string in the desktop renderer goes through a local lookup table, 1,249 keys per language, no framework and no network. The menu bar gains four .lproj to join English and Simplified Chinese, 626 keys each. A completeness gate fails the build if any language is missing a key, in both the desktop catalogs and the menu bar coverage suite. Fonts fall back per language so Japanese, Korean and both Chinese render their correct glyph forms, and dates and numbers format for the locale with values unchanged. English output is byte-identical.

Strings produced by the CLI engine stay English until the CLI is localized, a separate effort. Validated live in all six languages: no missing text, no clipping, correct fonts, numbers unchanged, and the menu bar switching for every language.
2026-09-19 03:25:46 -07:00
Resham Joshi
d81a86dd02
Desktop: measured polish pass, new frame, card system, motion, onboarding (#1459)
Measured design audit fixes, one type ladder and header, zero clipping at 900 to 1680, light-theme contrast, Lucide icons, shared Escape, plurals and provider names, motion tokens with zero refresh shift, black frame with rounded content card, grouped monochrome sidebar with collapse, two-layer card system on every screen, Overview gauge, sparkline cards and chart grid, wallpaper onboarding, Windows fixes. Every number diffed identical to main on a fixed range; 1,032 app tests passing.
2026-09-16 19:48:33 +02:00
Resham Joshi
ce49dc46a6
Refresh without layout shift: hairline and status spinner instead of banners (#1445)
* fix(desktop): replace switching banners with a non-shifting hairline and footer refresh mark

* fix(desktop): round 3 review fixes for refresh indicator

- footer live region now announces empty string while idle instead of
  re-announcing the ticking elapsed-time label every second
- reserve width on the footer status box so it does not shift between
  "refreshed Ns ago" and "not refreshed yet"
- guard usePolled's fetcher() call so a synchronous throw still
  decrements the in-flight counter
- __resetPolledMemo now notifies in-flight listeners when it zeroes
  the counter
- restore the "sessions updating" suffix on the cached snapshot's
  calls sub-line
2026-09-15 23:11:34 +02:00
ksp2000
08aabc5e5d feat: standardize model capability tiers 2026-08-29 20:22:01 +05:30
Resham Joshi
dbd93fee51
app,dash: render pre-history days as no data, not a currency zero (#765)
* fix(app,dash): render pre-history days as no data, not a currency zero

Days before the first recorded day in history.daily were painted as a real
0.00/0 calls in the daily activity heatmap and the daily spend charts. That
zero is unknown, not measured, so those cells and bars now read "No data
recorded" instead of a currency zero. Genuinely idle days within recorded
history stay as real zeros.

- app: heatmap cells, the daily spend bars, and the daily-by-model columns
  get a distinct no-data style plus a "No data recorded" hover for days before
  the first recorded day.
- dash: the granular line chart drops buckets before the first recorded day so
  no flat zero line is drawn before any history exists.
- data-start is derived in the UI layer from history.daily; payload shape is
  unchanged.

Verified: app vitest 418 pass (7 new for the no-data behavior); app, dash, and
root typecheck clean; dash and app renderer vite builds succeed.

* no-data: cap guard, timezone-free dash trim, StackedBars aria

Three hardening fixes from adversarial review of the no-data rendering:

dataStartKey returns null at the payload's 365-entry history cap, where
the oldest retained entry is no longer the true data start; classifying
past it would label real aged-out history as no data on long custom
ranges. Documented that the install-to-first-use gap reading as no data
is literally accurate: nothing was recorded then either.

The dash granular chart now trims leading zero-only buckets by value
instead of comparing bucket timestamps against date keys, so producer
and viewer timezone skew can never drop a real first-day bucket, and an
all-zero series lands in the established empty state.

StackedBars no-data columns expose their state via aria-label, not only
a title on a non-focusable div.
2026-07-20 08:03:24 -07:00
iamtoruk
26ffd60e0f polish(app): retire the legacy token family
Every var(--t1/--t2/--t3/--hair/--hair2) and secondary legacy color
(--lav/--amber/--red/--mint/--blue/--purple/--cyan) repointed to the
canonical family (--ink/--mut/--mut2/--line/--line2, --warn/--bad/--ok,
--s-*); all legacy definitions and the plain.css alias shim deleted.

The explicit dark/light theme blocks carried drifted legacy literals
(including a pre-contrast-fix --t3), so this also extends the AA
contrast values to all four theme modes, which previously disagreed
with system-light.

grep-proven zero legacy usages. 235/235, build green.
2026-07-16 04:16:44 -07:00
iamtoruk
0eb0fd60d7 polish(app): spacing scale, unified insets, shared banner classes, scrollbar cues
- --sp-1..7 scale; card headers unified on 10x14, bodies on 12x14;
  orphan gaps snapped to rungs
- CapsuleChart's orphaned CSS deleted
- StaleBanner/budget/empty/range notes moved from inline styles to
  plain.css classes; Panel bodyStyle prop retired (scroll-x class)
- thin always-visible scrollbars on scrolling panels and overflow-x
  tables; Compare pickers flex below 760px instead of overflowing
- one capitalization fix (loading quota…)

235/235, build green.
2026-07-16 04:02:21 -07:00
iamtoruk
015a1a069a polish(app): shared empty states, type ladder, AA contrast, honest affordances, a11y semantics
D1 one shared EmptyNote (7 copies removed); CliErrorPanel on canonical
   tokens; dead code deleted (CapsuleChart, stale SERIES_HEX palette,
   Stat tone API, unused rail/doc-chrome CSS)
D2 type ladder tokens (--fs-*/--fw-*); panel titles unified 13/600/ink;
   high-visibility sizes/weights snapped to rungs
D3 contrast: --mut2 5.00:1 light / 5.74:1 dark (was 2.37/3.11); new
   --accent-text 5.07:1 + persistent underline on text links; last
   prose em-dash removed
D4 'See all' and 'Most expensive sessions' navigate to Sessions; 'add
   alias' jumps to Settings/Aliases; chevrons only on clickable rows;
   inline confirm replaces window.confirm; default-period setting
   honored at boot; Sankey caption no longer promises a click; Optimize
   'improving' trend badges; cursor + Space-key fixes
D5 aria-current on nav, SegTabs as real tablist, aria-pressed toggles,
   ProviderPop rebuilt on Dropdown (one tab stop, arrow keys, focus ring)

191/191, typecheck + build green. Contrast ratios independently
recomputed. Net -93 lines.
2026-07-16 02:27:41 -07:00
iamtoruk
82f0402abd feat(app): Spend chart — fixed 15-day window + date axis, no gridlines
- extract contiguousDailyWindow + formatChartDate to lib/period (shared)
- Spend "Daily spend by model": fixed contiguous 15-day window (zero-filled)
  with an .ov-xax date axis, matching the Overview chart
- remove the horizontal gridlines from both charts (they read like data lines)

typecheck clean; 129/129 tests pass.
2026-07-12 16:21:41 -07:00
iamtoruk
9d25fc3866 feat(app): Spend chart matches Overview + caps to 15 days
Drop the 11px stacked-segment cap so the bars fill their columns like the
Overview .chart; render only the last 15 days.

typecheck clean; 126/126 tests pass.
2026-07-12 15:54:44 -07:00
iamtoruk
b0299c57ac feat(app): design reskin — token unification, a11y, and chart color + shading
Design-consistency pass (from the audit):
- unify the indigo→plain grey/border token families (alias --t1/2/3, --hair/2 onto --ink/mut/mut2, --line/2); fix a leftover hardcoded chevron color
- accent :focus-visible rings on SegTabs, chart bars, calendar, buttons, Settings toggles; ov-link hover
- theme-aware Spend legend swatches + Sankey (fixes a light-mode contrast failure)
- wire Models "Compare…" → Compare and Plans "Add plan…" → Settings pane; drop the fake-affordance Plans label; Settings loading captions
- Compare card radius/title, set-input, alias CTA color, outer-border tokens, session mono numbers, frame spacing to match the mockup
- scope a date-flaky Overview test assertion to its card

Reskin batch 1 — color + shade (current tone):
- validated CVD-safe model-series palette via dedicated --s-* tokens (light+dark), applied to the daily bars, Sankey, and series dots
- subtle soft card elevation via --card-shadow on the main card containers

typecheck clean; 128/128 tests pass.
2026-07-12 12:36:50 -07:00
iamtoruk
1efc0ceb44 fix(app): daily-spend chart fills its card, bars bottom-aligned
The StackedBars chart lived in a fixed 96px band, so when the grid
stretched its card to match the taller "By project" panel the bars
floated at the top over a large empty gap. Make the panel/body flex so
the chart fills the card height (min 120px) with bars bottom-anchored;
round stacked-segment corners only at the top/bottom of each column.
2026-07-11 17:06:00 -07:00
iamtoruk
18b4b728ff feat(app): Models section — per-model pricing table with series dots, by-model/by-task, unpriced-row alias
Table from getModels(): calls/input/output/cache-read/cost/saved with a model-family series
dot (reuses the ListRow helper). Unpriced/proxy rows show the dim "add alias" affordance +
dashes. By-model / By-task SegTabs re-fetch (byTask). Compare… is a visual affordance (M2).

Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).
2026-07-10 18:47:07 -07:00
iamtoruk
d7f3ccd1f0 feat(app): Spend section — daily-by-model stacked chart, by-project, model→project Sankey
Period-sliced StackedBars (history.daily[].topModels within the selected window via
a new shared lib/period.ts), the By-project list, and a Sankey rendered dynamically
from getSpendFlow (per-model hue ribbons). Lens tabs; series palette shared with the
legend. Adds a neutral .s-other class for the rollup segment.

Implemented by Codex gpt-5.5 (high); committed by Fable (git blocked in Codex sandbox).
2026-07-10 16:43:27 -07:00
iamtoruk
5736c95324 feat(app): scaffold Electron+Vite package and codeburn CLI spawn
New standalone `app/` package (own package.json, like dash/) for the
CodeBurn Desktop Electron app. Adds the Vite 6 + React 19 + Vitest
toolchain, the verbatim wireframe CSS port (renderer/styles/indigo.css),
and the main-process `codeburn` CLI resolver + spawner:

- resolveCodeburnPath(): CODEBURN_BIN override → persisted-path file →
  PATH/brew/nvm/volta/asdf → null (mirrors mac/CodeburnCLI.swift).
- spawnCli(args, {timeoutMs}): plain argv (no shell), collect stdout,
  JSON.parse, structured CliError{kind: not-found|nonzero|bad-json|timeout}.
- cli.test.ts covers success / nonzero / bad-json / timeout / not-found.
2026-07-10 15:16:55 -07:00