diff --git a/memory.md b/memory.md index f5efa26..92d7b3e 100644 --- a/memory.md +++ b/memory.md @@ -57,6 +57,7 @@ Four-agent research pass (terminal-landscape, AI-orchestration, xterm/Tauri ecos **β†’ Exploring first (user-selected 2026-05-28):** - [x] ~~**Per-session cost / token tracking.**~~ Done (code) 2026-05-28 β€” **WSL-only v1, pending Windows runtime verify.** Backend `src-tauri/src/usage.rs` (`get_claude_usage(distros)` command): probes `$HOME` per distro via `wsl.exe`, reads `~/.claude/projects/*/*.jsonl` over the `\\wsl.localhost\` UNC share, tallies `message.usage` **per model per assistant line** (sessions can switch models). Cached by `(path,size,mtime)`; recency-capped 30d/50 sessions. Frontend: `src/lib/usage.ts` holds the editable pricing table (per-MTok, matched by opus/sonnet/haiku substring) + cost/format helpers; `UsagePanel.tsx` (MCP-panel modal pattern) lists sessions, highlights those whose transcript `cwd` matches an open pane (`[pane: label]`); titlebar πŸ’° total chip; App polls 20s (visible) / 5s (panel open); **Ctrl+Shift+U** opens it. **Design choice:** session-list attribution (not 1:1 pane binding) β€” avoids the unsolvable "2 claudes in one cwd" ambiguity. **Caveats:** cost is an estimate (cache-creation priced at 5m rate; rates hardcoded, may drift); panes with no explicit cwd (`~`) won't highlight; PowerShell/SSH show nothing. Plan: `~/.claude/plans/greedy-cooking-flask.md`. + - **Refined same day after user feedback:** (1) **Scope** β€” panel + titlebar chip now default to sessions matching open panes ("this workspace"), with an "open panes / all recent" toggle. The first cut summed *every* recent session on the distro (all projects, `/mnt` + home), which read as inflated. **Investigated the "double counting mounted folders + projects" report: NOT a real double count** β€” every transcript file is read exactly once, and no two project dirs share a cwd because claude resolves symlinks/mounts to the real path before mangling the project-dir name (e.g. the `~/claude/projects/tiletopia β†’ /mnt/d/dev/tiletopia` symlink yields only `-mnt-d-dev-tiletopia`). The inflation was purely the global scope. (2) **Metric framing** β€” user is on a Pro/Max subscription where $ is meaningless (and `/usage` rate-limit quota can't be derived from transcripts); **tokens are now the headline**, the API-cost estimate is a labeled secondary `~$` kept visible so the user can validate it against real API billing at work. **Open question:** accuracy of the $ estimate vs actual API billing β€” user will check at work. - [ ] **Smart link providers.** `terminal.registerLinkProvider()` to make file paths (`src/foo.ts:12:3`), `localhost:PORT`, and error locations clickable β€” more flexible than the regex-only web-links addon already loaded. Open file in editor / browser. Difficulty: medium. - [x] ~~**Find in scrollback.**~~ Done + **verified on Windows 2026-05-28** β€” `@xterm/addon-search` + new `src/components/SearchBar.tsx`/`.css` overlay, Ctrl+Shift+F open / Enter / Shift+Enter / Esc, regex + case toggles, decoration highlight. - [x] ~~**Unicode 11 + grapheme width.**~~ Done + **verified on Windows 2026-05-28** β€” `@xterm/addon-unicode11` loaded after CanvasAddon, `term.unicode.activeVersion = '11'`. (Skipped the separate `addon-unicode-graphemes` for now.) diff --git a/src/App.tsx b/src/App.tsx index c0a9858..2299eea 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -804,6 +804,13 @@ export default function App() { [tree], ); + // Titlebar chip total β€” scoped to the open panes ("this workspace"), matching + // the usage panel's default view, so it isn't inflated by unrelated projects. + const workspaceUsageTotal = useMemo(() => { + const cwds = new Set(openPanes.map((p) => p.cwd).filter(Boolean)); + return totalCost(usageSessions.filter((s) => cwds.has(s.cwd))); + }, [openPanes, usageSessions]); + // Outside-click dismissal for the titlebar dropdowns. Mirrors the // per-pane shell-picker pattern in LeafPane.tsx. useEffect(() => { @@ -2153,7 +2160,7 @@ export default function App() { title="claude token usage & estimated cost (Ctrl+Shift+U)" aria-label="Usage" > - πŸ’°{usageSessions.length > 0 ? ` ${formatUsd(totalCost(usageSessions))}` : ""} + πŸ’°{workspaceUsageTotal > 0 ? ` ~${formatUsd(workspaceUsageTotal)}` : ""} + + {formatTokens(shown.reduce((a, s) => a + sessionTokens(s), 0))} tok + + {" Β· ~"} + {formatUsd(total)} + + + )}

) : (