Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,17 @@ jobs:

- name: Test
run: bun run test

native-build-and-test:
runs-on: macos-15
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Build, test, and render native fixtures
env:
TOKMETER_UI_QA_DIR: ${{ runner.temp }}/tokmeter-native-qa
run: |
mkdir -p "$TOKMETER_UI_QA_DIR"
swift test --package-path packages/macos-bar
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.11.0] - 2026-09-08

### Changed

- Give today's tokens and estimated API cost equal prominence in the macOS popup, with exact daily token/cost hover details.
- Add Prism, Lagoon, and Carbon styles while preserving existing saved theme identifiers; share theme renderers across popup and Hub and document their modules.
- Refine responsive Hub layouts, heatmap keyboard navigation, chart details, and readable status/cost colors.
- Align README and skill guidance with current query, provider, pricing, and web behavior; add native build, test, and fixture rendering to CI.

### Fixed

- Start the bundled web dashboard on demand from macOS Settings, verify readiness before opening it, and provide stop/cancel and app-quit cleanup; exclude private build-machine usage exports.
- Intersect date, project, and provider filters over saved daily buckets in CLI/API summaries, including sealed-only history.
- Preserve encoded project names and query strings in native daemon requests; continue Node discovery past unsupported, broken, or hung candidates.
- Quote copied Hub project commands as literal shell arguments, correct Drishti command examples, and report settings save failures without discarding the previous configuration.
- Verify daemon process identity before signalling, publish credentials only after listener ownership, and coalesce concurrent forced rescans.

## [1.10.0] — 2026-09-06

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Release builds target Apple silicon and macOS 14+. Node.js 18+ with npx is also

For published 1.10.0, install `@sriinnu/drishti`, run `drishti daemon start`, and open TokmeterBar from `/Applications`. Download the app from [GitHub Releases](https://github.com/sriinnu/tokmeter/releases). The current source improves automatic startup by resolving paired Node/npx and invoking version-matched Drishti, with prerequisite and retry controls on failure.

The popup shows today's tokens, cost, models, and projects. **Usage details** expands lifetime totals, trends, and signals; the view scrolls when it exceeds the available height. Six themes are selectable: Terminal, Paper, Nebula, Aurora, Nocturne, and Glass. Glass uses native light frost and explicit theme-based text/status colors. The Hub provides larger breakdowns and settings.
The popup gives today's tokens and estimated API cost equal prominence, with models and projects below. Chart hover cards show exact daily tokens and cost. **Usage details** expands lifetime totals, trends, and signals; the view scrolls when it exceeds the available height. Six themes are selectable: Terminal, Paper, Prism, Lagoon, Carbon, and Glass. Glass uses native light frost and explicit theme-based text/status colors. The Hub provides larger breakdowns and settings. Settings → **Open web dashboard** starts its local server on demand; **Stop web dashboard** or quitting the app stops it.

See [macOS build and runtime details](packages/macos-bar/README.md), [first-use checks](docs/macos/first-use.md), and [popover validation](docs/macos/popover-usability.md). The [completion tracker](docs/macos-completion.md) records remaining fresh-machine, reliability, update, accounting, accessibility, and trial gates.

Expand Down
8 changes: 5 additions & 3 deletions SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,16 @@ const pricing = await lookupTokmeterPricing("claude-sonnet-4-20250514");
import { TokmeterCore } from "@sriinnu/tokmeter";

const core = new TokmeterCore();
await core.scan({ providers: ["codex", "claude-code"], since: "2026-04-01" });
const summary = core.getSummary();
await core.scan();
const summary = core.getSummary({ providers: ["codex", "claude-code"], since: "2026-04-01" });
```

## Integration notes

- Tokmeter reads local session files; there is no hosted backend requirement.
- `TokmeterSummary` is the best high-level contract for downstream apps and dashboards.
- `TokmeterSummary` is the high-level contract for downstream apps and dashboards.
- Use `getSummary(options)` to scope aggregate reports; `scan(options)` alone does not filter subsequent no-argument getters. Reports use inclusive local calendar days (`week`: today plus six days); timestamps are rejected.
- Summary `records` is recent raw evidence, not a complete historical ledger.
- `light` / `--light` skips pricing lookups when token counts are enough.
- `@sriinnu/drishti` is the preferred live surface for other AI assistants.

Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"files": {
"ignoreUnknown": true,
"include": ["*.ts", "*.tsx", "*.js", "*.jsx", "*.json"],
"include": ["*.ts", "*.tsx", "*.js", "*.mjs", "*.jsx", "*.json"],
"ignore": ["**/dist/**", "**/node_modules/**", "**/*.d.ts", "**/*.min.js", "**/*.map"],
"maxSize": 10485760
},
Expand Down
12 changes: 6 additions & 6 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 14 additions & 4 deletions docs/consuming-tokmeter.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ Use these wrappers when you want the convenience of the CLI package but not the
import { TokmeterCore } from "@sriinnu/tokmeter";

const core = new TokmeterCore();
await core.scan({ since: "2026-04-01", providers: ["codex", "claude-code"] });
await core.scan();

const summary = core.getSummary();
const projects = core.getAllProjects();
const stats = core.getStats();
const summary = core.getSummary({ since: "2026-04-01", providers: ["codex", "claude-code"] });
const projects = summary.projects;
const stats = summary.stats;
```

Use core directly when you need:
Expand All @@ -94,6 +94,16 @@ Use core directly when you need:
- low-level filtering/aggregation helpers
- control over caching and pricing lifecycle

## Report filters and retained history

CLI reports, the convenience helpers, and `core.getSummary(options)` filter saved daily aggregates together with today's live aggregate. Calling `scan(options)` alone does not scope later no-argument getters. Those getters keep their all-time view.

Report dates use the machine's local calendar: `week` means today and the previous six days, `month` means the current month through today, and `year` means the selected calendar year. `since` and `until` accept inclusive `YYYY-MM-DD` bounds. Intraday timestamps are rejected because saved daily history cannot reconstruct partial days. Report `--older-than` selects complete days before the cutoff date; destructive cleanup retains its timestamp cutoff. The lower-level raw `scan()` API retains its separate timestamp filtering.

Project filters match raw names or alias display names; provider and date filters intersect with that selection. Hidden projects stay out of project lists but remain in totals. `records` contains only the available recent raw records and their original provenance; it is not a reconstruction of all historical records contributing to the totals.

Scan metadata describes the full refresh. Narrowed reports omit rolling live signals, whose time windows differ from the report. With project/provider filtering, first/last timestamps retain the original project-day bounds; per-provider intraday boundaries are not available in the saved cross-cut buckets. Costs retain their saved values and may lack provenance for a retrospective estimate/report split.

## MCP / live integrations

Use `@sriinnu/drishti` when an AI assistant should answer token/cost questions during a session.
Expand Down
16 changes: 10 additions & 6 deletions docs/macos-completion.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@ Owner: Srinivas + Codex. Started 2026-09-06 after the 1.10.0 release. Workflow:
## Current checkpoint

- Baseline: published 1.10.0 (46), Apple Silicon, macOS 14+. Release source tag `v1.10.0`; release and distribution PRs #73/#74 merged.
- Active branch: `fix/macos-first-run-and-reliability`.
- MAC-01 finding: auto-start uses `npx @sriinnu/tokmeter daemon start`, but that package dynamically imports Drishti without installing it. The monorepo masks this missing dependency. Auto-start must invoke the published daemon package directly.
- MAC-01 finding: toolchain discovery only checks `/opt/homebrew/bin/npx` and `/usr/local/bin/npx`; managed Node installations and missing prerequisites need explicit handling.
- MAC-02 finding: generic API/decode/version errors enter the same auto-start path as an unreachable daemon. Recovery and incompatible-data states need distinct treatment.
- Source checkpoint: first-run and popover fixes merged to main in PR #75 (`e2aee8c`). The fresh-machine and observed acceptance gates below remain open.
- MAC-01 finding in published 1.10.0: auto-start uses `npx @sriinnu/tokmeter daemon start`, but that package dynamically imports Drishti without installing it. The monorepo masks this missing dependency. Auto-start must invoke the published daemon package directly.
- MAC-01 finding in published 1.10.0: toolchain discovery only checks `/opt/homebrew/bin/npx` and `/usr/local/bin/npx`; managed Node installations and missing prerequisites need explicit handling.
- MAC-02 finding in published 1.10.0: generic API/decode/version errors enter the same auto-start path as an unreachable daemon. Recovery and incompatible-data states need distinct treatment.
- Spare Mac/VM availability and participant selection requested; independent implementation continues while those are identified.
- First-use fixes implemented on the active branch: invoke the version-matched Drishti package, discover paired Node/npx in system and managed installations, provide Install Node/Retry actions, preserve protocol errors, and drain bounded subprocess output continuously. Native checks: 22 passed, one optional demo render skipped. Fresh-machine acceptance remains open.
- First-use fixes merged in PR #75: invoke the version-matched Drishti package, discover paired Node/npx in system and managed installations, provide Install Node/Retry actions, preserve protocol errors, and drain bounded subprocess output continuously. Native checks: 22 passed, one optional demo render skipped. Fresh-machine acceptance remains open.
- Reproduced the published CLI-only failure from an isolated npm installation: `tokmeter daemon status` cannot resolve `@sriinnu/drishti`. No daemon or usage data was modified by this reproduction.
- MAC-05 source fixes: content-sized popup, explicit disclosure text color, readable Paper model costs, wrapping signal readings, and Noise retired from the picker. The first local build collapsed its body; the failure was reproduced with the full popup and corrected using direct geometry observations. [Review and validation](macos/popover-usability.md): 26 native tests passed, one optional render skipped, including full-popup first-layout and live disclosure-binding regressions. Installed-app interaction, keyboard, and VoiceOver acceptance remains open.
- Signed commit/PR handoff is pending the configured hardware signing key, which was unavailable on the last attempt. These branch changes have not reached main. Corrected local test build 1.10.0 (46.2) replaced 46.1 and was relaunched from `/Applications` on 2026-09-08; binary identity and local signature verified. The Git signing key is not required for local installation.
- Historical local-install checkpoint before the PR #75 merge: corrected local test build 1.10.0 (46.2) replaced 46.1 and was relaunched from `/Applications` on 2026-09-08; binary identity and local signature verified. The Git signing key is not required for local installation.
- Latest local build: 1.10.0 (46.6), installed and running from `/Applications`. Status colors and badge backgrounds now follow the selected theme explicitly. The user's 46.5 screenshot proved the previous adaptive-color fix still failed in the live popup. Opposing-host native widget captures now assert selected-theme pixels; 27 native tests passed. Binary/signature identity verified; the user accepted 46.6 contrast on 2026-09-08. Remaining interaction/accessibility acceptance stays open.

- Review follow-up candidate: 1.10.0 (46.10), installed on 2026-09-08 from `fix/review-terminal-and-cli`. Three-agent [review and remaining findings](reviews/2026-09-08.md) records responsive Terminal/Glass layouts, explicit trend/date semantics, keyboard daily values, URL/Node startup fixes, and source CLI filter corrections. Local gates: 390 JavaScript tests passed before the native-only follow-up; the latest native run passed 35 tests with one timing failure that passed on focused rerun. Build 46.8 adds prominent estimated cost and chart hover values. Build 46.9 replaces Nocturne/Aurora with Carbon/Lagoon. Build 46.10 replaces Nebula with Prism using shared popup/Hub renderers and adds a [theme development guide](macos/themes.md); seven focused native layout/render/contrast tests pass. Live candidate acceptance remains open. This follow-up branch has not merged or published a new npm release.

- 1.11.0 (48) release candidate: on-demand dashboard startup/stop, literal Hub copy commands, settings-save errors, daemon identity/ownership and rescan coalescing are fixed in source. Final local checks: 404 JavaScript tests and 41 native tests passed, with 11 existing JavaScript todos and one optional native walkthrough skipped. All five bundled web views loaded in Chrome with live data. See the [release review](reviews/2026-09-08.md). Publication and installation remain pending at this checkpoint; none of the six acceptance rows is closed by these checks.

## Evidence and closure rules

- Record commands/results and build identity in focused documents under `docs/macos/`; keep raw usage, paths identifying private projects, transcripts, and credentials out of committed evidence.
Expand Down
62 changes: 62 additions & 0 deletions docs/macos/themes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Native theme development

The popup and Hub share `AppTheme`, palettes, typography, and surface renderers. Themes change presentation; telemetry, cost provenance, refresh, and navigation remain in their existing models and views.

## Files and ownership

Paths below are relative to `packages/macos-bar/Sources/TokmeterBar/`.

| File | Responsibility |
| --- | --- |
| `Theme.swift` | Persisted identity, picker names/order, mode selection, typography, semantic status and monetary ink |
| `ThemePalettes.swift` | Six palette roles for each theme |
| `Theme+Modes.swift` | Background, header, card, and font descriptors |
| `HeroBackground.swift` | Dispatch from a header mode to its renderer |
| `CardBackground.swift` | Popup card dispatch |
| `HubCard.swift` | Hub content spacing and shared panel dispatch |
| `PrismSurface.swift` | Stateless `PrismPanel` and `PrismHeroBackdrop`; the panel is reused by popup and Hub |
| `FrostedGlass.swift` | Glass material, opacity, and accessibility fallbacks |

`PrismPanel` accepts palette colors and a corner radius. `PrismHeroBackdrop` accepts palette colors and draws facets relative to the available size. Neither reads settings, loads telemetry, starts timers, or owns interaction state.

Use the existing wrappers when adding a surface. Do not copy a complete card or dashboard to introduce a theme. Keep a distinctive surface renderer in its own file when it is shared or would enlarge a dispatch view substantially.

## Saved identifiers

The picker has six entries. Three styles were replaced while retaining their stored identifiers:

| Display name | Stored value |
| --- | --- |
| Terminal | `terminal` |
| Paper | `paper` |
| Prism | `nebula` |
| Lagoon | `aurora` |
| Carbon | `nocturne` |
| Glass | `glass` |

The stored value is the enum raw value used by `@AppStorage("appTheme")`. Legacy mode names also remain in source. Renaming a display label does not require changing stored values. Hidden enum cases remain decodable but are omitted from the explicitly curated `AppTheme.allCases` picker list.

## Add or revise a theme

1. Choose whether this is a new persisted theme or a replacement for an existing style. For a new theme, add a unique enum case and deliberately add it to the picker list; for a replacement, retain its raw value.
2. Define its six palette roles, display label, icon, typography, and background/header/card modes. Reuse an existing mode when its rendering is sufficient.
3. Put custom drawing in a small view with explicit inputs. Connect it through the popup and Hub wrappers. Avoid theme-specific copies of data views.
4. Set text and monetary/status ink for the selected theme's surface, independently of the host macOS appearance. Use named semantic status colors for warnings, success, and danger. Check long values, unavailable values, and tooltip legibility.
5. Update the theme table and macOS README. State whether the result is source-only, locally installed, or released.

## Verify

From the repository root, render synthetic fixtures and run the focused native checks:

```sh
mkdir -p /tmp/tokmeter-theme-review
TOKMETER_UI_QA_DIR=/tmp/tokmeter-theme-review \
swift test --package-path packages/macos-bar \
--filter 'DemoRenderTests/testRenderThemeReview|HubResponsiveLayoutTests|ThemeContrastTests|PopoverLayoutTests'
```

The demo renderer uses the picker list. The production Hub/layout/contrast tests have explicit theme lists; add a genuinely new theme there too. Current Hub captures cover widths of 860, 1100, and 1500 points. File names use stored identifiers, so Prism images have the `nebula` prefix.

Inspect the generated popup, expanded details, tooltip, and Hub images. Check header and value clipping, chart contrast, panel edges, and large-window spacing. Contrast tests compare semantic ink and native widget pixels; their reference swatches use the same AppKit capture path as the widgets to avoid cross-profile comparisons.

These checks use fixtures. They do not establish live pointer/VoiceOver acceptance, sustained performance, or release readiness. Keep those results separate in the [completion tracker](../macos-completion.md).
Loading