diff --git a/CLAUDE.md b/CLAUDE.md
index 64e1af4..41f77c9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -112,7 +112,7 @@ Tests live alongside the code (`#[cfg(test)] mod tests`), plus `toolpath-cli` ha
- `toolpath-pi`: ~88 unit tests (types, paths, error, reader, io, provider)
- `toolpath-dot`: 30 unit + 2 doc tests (render, visual conventions, escaping)
- `toolpath-cli`: 126 unit + 24 integration tests (all commands, track sessions, merge, validate, roundtrip, render-md snapshots)
-- `toolpath-desktop`: 13 unit tests (IPC command modules — source listing, derive validation, export round-trip, upload stub, keychain input checks)
+- `toolpath-desktop`: 17 unit tests (IPC command modules — source listing, derive validation, export round-trip, upload stub, keychain input checks; tray activity-window bucketing, stats-snapshot smoke, session-id/basename helpers)
Validate example documents: `for f in examples/*.json; do cargo run -p toolpath-cli -- validate --input "$f"; done`
@@ -138,6 +138,10 @@ Layout:
Tauri dev loop: `cargo tauri dev` spawns `bun --cwd frontend run dev` (Vite on `http://localhost:1420`), then runs the Rust binary against that URL. Frontend edits hot-reload via Vite HMR without restarting Rust; Rust edits trigger `cargo run` to restart. Production: `cargo tauri build` runs `bun --cwd frontend run build` first, bundling to `frontend/dist/`.
+Menu-bar mode: the app runs as a normal GUI app (Dock icon + app-switcher entry) *and* installs a tray icon — the tray is an accessory, not a replacement for the main window. Accessory activation policy was tried and reverted because macOS tiling window managers (yabai, Amethyst) stop managing accessory windows. A tray icon is installed in `src/tray.rs`; a background thread polls every 30s across `toolpath-claude`, `-gemini`, `-codex`, `-opencode`, and `-pi`, classifies sessions as *active* (last activity in the last 2 min) or *recent* (last 24h), updates the tray title (`● N`), and emits a `tray:stats` event. The popover is a second Tauri window (`label = "popover"`, undecorated, hidden by default) with its own Vite entry (`frontend/popover.html` → `src/popover.ts` → `routes/Popover.svelte`); left-clicking the tray toggles it via `tauri-plugin-positioner`. For an on-demand snapshot (no waiting for the next poll) the popover invokes the `tray_stats_now` IPC command.
+
+Opening a trace from the popover: clicking a recent-session row invokes `tray_open_trace { provider, project, session_id }`. The Rust side calls back into the existing `derive_claude` / `derive_pi` commands, shows the main window, and emits a `trace:opened` event to the main window with the derived `{ doc, source, filename }`. `app.svelte` listens for it and dispatches `DeriveSucceeded`, which routes to the preview. Only `claude` and `pi` have derive commands today — rows for `gemini`, `codex`, `opencode` still appear in the list (so users can see activity) but are rendered disabled.
+
Streaming pattern (Claude project/session lists): Rust command spawns a thread that emits `claude:project`, `claude:session`, `claude:projects-done`, `claude:sessions-done` events. The Svelte component subscribes with `$effect(() => { listen(...) ... return unlisten; })` — Svelte tears down listeners automatically when the effect's deps change or the component unmounts.
Package manager for the frontend is `bun` (installed at `~/.bun/bin/bun`). `bun install` to set up, `bun run check` for `svelte-check`, `bun run build` for a production Vite build. Never commit `node_modules/` or `dist/` — both are ignored.
diff --git a/Cargo.lock b/Cargo.lock
index 5b2e009..b67ce01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4662,6 +4662,21 @@ dependencies = [
"zbus",
]
+[[package]]
+name = "tauri-plugin-positioner"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02dcd56dd4797bd4d6c4c658daed40ce563176f92df90fbd2c904ce145de17ef"
+dependencies = [
+ "log",
+ "serde",
+ "serde_json",
+ "serde_repr",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.18",
+]
+
[[package]]
name = "tauri-runtime"
version = "2.10.1"
@@ -5142,12 +5157,16 @@ dependencies = [
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-opener",
+ "tauri-plugin-positioner",
"tempfile",
"thiserror 1.0.69",
"toolpath",
"toolpath-claude",
+ "toolpath-codex",
+ "toolpath-gemini",
"toolpath-git",
"toolpath-github",
+ "toolpath-opencode",
"toolpath-pi",
"uuid",
]
diff --git a/crates/toolpath-desktop/Cargo.toml b/crates/toolpath-desktop/Cargo.toml
index e022d1c..1ef284f 100644
--- a/crates/toolpath-desktop/Cargo.toml
+++ b/crates/toolpath-desktop/Cargo.toml
@@ -18,13 +18,17 @@ tauri-build = { version = "2", features = [] }
[dependencies]
toolpath = { workspace = true }
toolpath-claude = { workspace = true, features = ["watcher"] }
+toolpath-codex = { workspace = true }
+toolpath-gemini = { workspace = true }
toolpath-git = { workspace = true }
toolpath-github = { workspace = true }
+toolpath-opencode = { workspace = true }
toolpath-pi = { workspace = true }
-tauri = { version = "2", features = [] }
+tauri = { version = "2", features = ["tray-icon"] }
tauri-plugin-dialog = "2"
tauri-plugin-opener = "2"
+tauri-plugin-positioner = { version = "2", features = ["tray-icon"] }
serde = { workspace = true }
serde_json = { workspace = true }
diff --git a/crates/toolpath-desktop/README.md b/crates/toolpath-desktop/README.md
index 26239ae..8004ec7 100644
--- a/crates/toolpath-desktop/README.md
+++ b/crates/toolpath-desktop/README.md
@@ -18,6 +18,14 @@ intended for people who won't open a terminal to run the `path` CLI.
3. **Export** — save the document as a local `.path.json` file, **or** upload
it to Pathbase. The Pathbase upload is stubbed in v0.1 and logs a mock
response; the real API will be wired up in a follow-up.
+4. **Quick View (menu bar)** — a tray icon that sits alongside the regular
+ Dock-icon app. A background thread polls every 30s across all five agent
+ providers (`toolpath-claude`, `-gemini`, `-codex`, `-opencode`, `-pi`)
+ and updates the tray title with an activity count. Left-click opens a
+ small popover listing recent sessions; the menu has an "Open Toolpath"
+ item that brings up the main window, and clicking a recent session in
+ the popover opens its trace directly in the preview. See `src/tray.rs`
+ and `frontend/src/routes/Popover.svelte`.
## Architecture
diff --git a/crates/toolpath-desktop/capabilities/default.json b/crates/toolpath-desktop/capabilities/default.json
index f3f3d8f..081edb6 100644
--- a/crates/toolpath-desktop/capabilities/default.json
+++ b/crates/toolpath-desktop/capabilities/default.json
@@ -2,10 +2,17 @@
"$schema": "../gen/schemas/desktop-schema.json",
"identifier": "default",
"description": "Capabilities required by the Toolpath desktop app.",
- "windows": ["main"],
+ "windows": ["main", "popover"],
"permissions": [
"core:default",
"dialog:default",
- "opener:default"
+ "opener:default",
+ "positioner:default",
+ "core:window:allow-show",
+ "core:window:allow-hide",
+ "core:window:allow-set-focus",
+ "core:window:allow-close",
+ "core:window:allow-is-visible",
+ "core:window:allow-start-dragging"
]
}
diff --git a/crates/toolpath-desktop/frontend/popover.html b/crates/toolpath-desktop/frontend/popover.html
new file mode 100644
index 0000000..9dd8b7f
--- /dev/null
+++ b/crates/toolpath-desktop/frontend/popover.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+ Toolpath Quick View
+
+
+
+
+
+
diff --git a/crates/toolpath-desktop/frontend/src/app.svelte b/crates/toolpath-desktop/frontend/src/app.svelte
index 4c5996d..3e0285c 100644
--- a/crates/toolpath-desktop/frontend/src/app.svelte
+++ b/crates/toolpath-desktop/frontend/src/app.svelte
@@ -1,5 +1,7 @@
diff --git a/crates/toolpath-desktop/frontend/src/popover.ts b/crates/toolpath-desktop/frontend/src/popover.ts
new file mode 100644
index 0000000..3fbee35
--- /dev/null
+++ b/crates/toolpath-desktop/frontend/src/popover.ts
@@ -0,0 +1,7 @@
+import { mount } from "svelte";
+import Popover from "./routes/Popover.svelte";
+import "./styles.css";
+
+const target = document.getElementById("popover");
+if (!target) throw new Error("#popover not found");
+mount(Popover, { target });
diff --git a/crates/toolpath-desktop/frontend/src/routes/Popover.svelte b/crates/toolpath-desktop/frontend/src/routes/Popover.svelte
new file mode 100644
index 0000000..228f62e
--- /dev/null
+++ b/crates/toolpath-desktop/frontend/src/routes/Popover.svelte
@@ -0,0 +1,376 @@
+
+
+