70 lines
2.9 KiB
Markdown
70 lines
2.9 KiB
Markdown
---
|
||
summary: "Menu bar status logic and what is surfaced to users"
|
||
read_when:
|
||
- Tweaking mac menu UI or status logic
|
||
---
|
||
# Menu Bar Status Logic
|
||
|
||
## What is shown
|
||
- We surface the current agent work state in the menu bar icon and in the first status row of the menu.
|
||
- Health status is hidden while work is active; it returns when all sessions are idle.
|
||
- The “Nodes” block in the menu lists **devices** only (gateway bridge nodes via `node.list`), not client/presence entries.
|
||
|
||
## State model
|
||
- Sessions: events arrive with `runId` (session key). The “main” session is the key `main`; if absent, we fall back to the most recently updated session.
|
||
- Priority: main always wins. If main is active, its state is shown immediately. If main is idle, the most recently active non‑main session is shown. We do not flip‑flop mid‑activity; we only switch when the current session goes idle or main becomes active.
|
||
- Activity kinds:
|
||
- `job`: high‑level command execution (`state: started|streaming|done|error`).
|
||
- `tool`: `phase: start|result` with `toolName` and `meta/args`.
|
||
|
||
## IconState enum (Swift)
|
||
- `idle`
|
||
- `workingMain(ActivityKind)`
|
||
- `workingOther(ActivityKind)`
|
||
- `overridden(ActivityKind)` (debug override)
|
||
|
||
### ActivityKind → glyph
|
||
- `bash` → 💻
|
||
- `read` → 📄
|
||
- `write` → ✍️
|
||
- `edit` → 📝
|
||
- `attach` → 📎
|
||
- default → 🛠️
|
||
|
||
### Visual mapping
|
||
- `idle`: normal critter.
|
||
- `workingMain`: badge with glyph, full tint, leg “working” animation.
|
||
- `workingOther`: badge with glyph, muted tint, no scurry.
|
||
- `overridden`: uses the chosen glyph/tint regardless of activity.
|
||
|
||
## Status row text (menu)
|
||
- While work is active: `<Session role> · <activity label>`
|
||
- Examples: `Main · bash: pnpm test`, `Other · read: apps/macos/Sources/Clawdis/AppState.swift`.
|
||
- When idle: falls back to the health summary.
|
||
|
||
## Event ingestion
|
||
- Source: control‑channel `agent` events (`ControlChannel.handleAgentEvent`).
|
||
- Parsed fields:
|
||
- `stream: "job"` with `data.state` for start/stop.
|
||
- `stream: "tool"` with `data.phase`, `name`, optional `meta`/`args`.
|
||
- Labels:
|
||
- `bash`: first line of `args.command`.
|
||
- `read`/`write`: shortened path.
|
||
- `edit`: path plus inferred change kind from `meta`/diff counts.
|
||
- fallback: tool name.
|
||
|
||
## Debug override
|
||
- Settings ▸ Debug ▸ “Icon override” picker:
|
||
- `System (auto)` (default)
|
||
- `Working: main` (per tool kind)
|
||
- `Working: other` (per tool kind)
|
||
- `Idle`
|
||
- Stored via `@AppStorage("iconOverride")`; mapped to `IconState.overridden`.
|
||
|
||
## Testing checklist
|
||
- Trigger main session job: verify icon switches immediately and status row shows main label.
|
||
- Trigger non‑main session job while main idle: icon/status shows non‑main; stays stable until it finishes.
|
||
- Start main while other active: icon flips to main instantly.
|
||
- Rapid tool bursts: ensure badge does not flicker (TTL grace on tool results).
|
||
- Health row reappears once all sessions idle.
|