Skip to content

feat: macOS menu bar icon with live agent status #25

@yankay

Description

@yankay

Feature Description

Add a macOS menu bar (system tray) icon in the top-right corner of the status bar to display real-time Agent status. Users can monitor task progress at a glance even when the main window is minimized or another app is in focus.

Requirements

Icon States

State Icon Appearance Trigger
Idle Static ClawWork icon (muted / low-saturation) No Agent running
Running Animated pulse/spin icon (accent green) ≥1 Task with active Agent streaming
Unread Icon with small green dot badge Agent completed but user hasn't viewed result
Disconnected Icon with orange exclamation badge Gateway WS connection lost

Click Behavior

  • Single click → Show mini status popover (see below)
  • Click outside popover → Dismiss popover
  • Double-click / click "Open ClawWork" inside popover → Focus / restore main window

Mini Status Popover

Lists all running Tasks with:

  • Task name (truncated + tooltip)
  • Latest streaming message snippet (≤ 60 chars)
  • Running duration (relative time, e.g. "3 min ago")

Popover footer:

  • "Open ClawWork" button → focus main window
  • "Quit" button → app.quit()

When no tasks are running, show an empty state: "No active tasks".

Implementation Notes

Electron Tray API

// packages/desktop/src/main/tray.ts
import { Tray, Menu, nativeImage } from 'electron'

let tray: Tray | null = null

export function initTray(mainWindow: BrowserWindow): void {
  const icon = nativeImage.createFromPath(/* @2x icon path */)
  tray = new Tray(icon)
  tray.setToolTip('ClawWork')
  tray.on('click', () => showTrayPopover(mainWindow))
}

export function updateTrayStatus(
  status: 'idle' | 'running' | 'unread' | 'disconnected'
): void {
  // Swap template icon or overlay badge
}

State Synchronization

Two viable approaches:

  1. Renderer (taskStore / messageStore) detects state changes and notifies main process via IPC.
  2. Main process subscribes directly to Gateway WS events in gateway-client.ts (already has awareness of active sessions).

Icon Assets

  • Provide tray-icon.png (16×16) and tray-icon@2x.png (32×32) as macOS template images (setTemplateImage: true) so they automatically adapt to Dark / Light mode.
  • For the "running" animation, either draw rotating frames dynamically with nativeImage + canvas, or cycle frames via tray.setImage() on a timer.

Acceptance Criteria

  • ClawWork icon appears in the menu bar on app launch
  • Icon shows animated state when ≥1 Agent is streaming
  • Icon shows "unread" badge after Agent completes; badge clears when the user views the result in the main window
  • Icon shows disconnection badge when the Gateway WS connection is lost
  • Clicking the icon opens a popover listing running Tasks (shows "No active tasks" when idle)
  • "Open ClawWork" in the popover focuses the main window
  • Closing the main window (window close event) hides it instead of quitting — the app stays alive in the tray; "Quit" in the popover exits cleanly

Related Files

File Notes
packages/desktop/src/main/index.ts Call initTray() here
packages/desktop/src/main/ws/gateway-client.ts Subscribe to connection state for tray badge
packages/desktop/src/renderer/stores/taskStore.ts Task running state
packages/desktop/src/renderer/stores/messageStore.ts Streaming message snippets
packages/desktop/src/preload/index.ts Add tray.updateStatus IPC bridge

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions