Skip to content

feat: Call Mode — brutalist stream widget, hooks narration, participant system#10

Open
sameeeeeeep wants to merge 7 commits intomainfrom
claude/infallible-meninsky
Open

feat: Call Mode — brutalist stream widget, hooks narration, participant system#10
sameeeeeeep wants to merge 7 commits intomainfrom
claude/infallible-meninsky

Conversation

@sameeeeeeep
Copy link
Owner

Summary

This PR ships the full Call Mode feature for AutoClawd — a real-time overlay that turns Claude Code sessions into a live, story-driven group call experience.

Call Stream Widget (new floating panel)

  • CallStreamWidget.swift — always-on-top NSPanel (420×560) that overlays any app during Call Mode; slide+fade animate in/out; draggable; bottom-right default position
  • CallStreamWidgetView.swift — brutalist design language throughout:
    • No circles, no soft rounding — sharp rectangular geometry
    • Mission bar — orange accent strip shows the user's spoken goal (first message)
    • Participant strip — rectangular tiles with thick colored top-border accent; square mascot icons with NSImage(named: "mascot-{id}") PNG support + SF Symbol fallback
    • Task queue — top 3 pending StructuredTodos; active task highlighted orange
    • Stream feed — brutalist group-chat: NAME ─── time / message / inline image; generated/reaction messages render at 70% opacity with ~ marker
    • Spotlight panel — auto-surfaces latest image (full-width preview) or active filename (detected via regex); dismissable
    • Bottom bar — animated waveform bars + event count + ■ END CALL red square button

Hooks Integration (real-time narration)

  • HookNarrationService.swift — translates raw Claude Code hook events into human-readable NarrationBundles:
    • Parses mcp__server__tool names → ToolParticipant with SF Symbol mapping (Pencil, Figma, GitHub, Linear, Notion, Google Sheets)
    • Extracts image data from tool_response JSON (3 payload patterns)
    • Calls local Llama for casual narration; falls back to template summaries for all standard Claude Code tools (Read, Write, Edit, Bash, Glob, Grep, Task, WebFetch, WebSearch, TodoWrite, etc.)
    • Generates an AutoClawd reaction for MCP tool events and image results

Participant System

  • CallRoom.swiftCallParticipant model with state machine (idle / thinking / streaming / paused), tile color, mascot SF Symbol
  • AppDelegate.swift$pillMode observer auto-shows/hides widget; onHookEvent handler auto-joins tool participants and posts structured feed messages
  • CallModeRoomView.swiftCallFeedMessageRow with 2px colored left-border; solid gradient for real events, dashed for generated; inline image rendering

Supporting Changes

  • CallModeSession.swiftCallMessage gains createdAt, imageData, isGenerated; appendParticipantMessage extended accordingly
  • SettingsManager.swiftcallStreamWidgetEnabled toggle (default true)

Test plan

  • Switch pill to Call ModeCallStreamWidget animates in at bottom-right
  • Speak a phrase → appears as YOU message with timestamp in stream feed
  • Run a Claude Code session → hook events narrate as readable sentences in the feed
  • MCP tool used (e.g. Pencil) → tool auto-joins as participant tile; reaction message generated
  • Tool returns an image → inline in feed + Spotlight panel auto-opens
  • Tool reads a .swift file → filename card appears in Spotlight
  • End Call button or mode switch → widget animates out
  • Drop mascot-pencil.png into Resources/ → appears in participant tile (no rebuild needed after copy)

🤖 Generated with Claude Code

Sameep Rehlan and others added 7 commits March 8, 2026 03:50
feat: Vision-powered screen context, face recognition & speaker attribution

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…leave detection

- CallRoom.swift: Participant model (llama/claudeCode/connection kinds), state machine
  (idle/thinking/streaming/paused), gesture-based addressing (finger count → slot),
  pause/remove lifecycle, claudeCodeIsActive gate for MCP transcript
- MCPServer.swift: initialize handshake fires onJoined callback; 60s inactivity timer
  fires onLeft; autoclawd_get_audio_transcript returns standby message when paused
- AppState: callRoom singleton added
- AppDelegate: wires isPausedProvider, onJoined, onLeft into MCPServer.start()
- MainPanelView: CallModeZoomView (3-panel zoom-call layout), hides PixelWorld in callMode
- CameraPreviewView: screen preview constrained to parent frame (fixes chevron disappearing)
- PillMode, PipelineModels, ChunkManager, WidgetCanvasViews, SkillStore: call mode wiring

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CallModeRoomView: replaces CallModeZoomView as the panel's call mode view
  - Horizontal scrollable participant tiles (tap or left-hand finger count to address)
  - ParticipantTileView: slot badge, pause/remove buttons, state label, mascot
  - ParticipantMascotView: SF Symbol icon with state-driven animations
    (idle: breathing pulse, thinking: spinner, streaming: shimmer ring, paused: static)
  - ThinkingDotsView: animated three-dot thinking indicator
  - Shared call feed: voice transcript + callModeSession messages unified
  - Bottom bar: camera thumb, screen thumb, play/pause/stop, active participant indicator
- AppState: leftFingerCount gesture → callRoom.selectByGesture() when in callMode
- MainPanelView: swapped CallModeZoomView → CallModeRoomView

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4 new MCP tools let Claude Code orchestrate a live cast of participants:
- autoclawd_invite_participant(id, name, system_image)  → tile joins call
- autoclawd_set_participant_state(id, state)             → animate tile
- autoclawd_send_participant_message(id, name, text)     → attributed feed bubble
- autoclawd_remove_participant(id)                       → tile leaves

CallRoom: connection kind now carries systemImage + consistent hash-derived
tile color so each plugin always gets the same hue.

CallMessage gains participantID/participantName so feed rows show the
correct plugin name, icon, and color rather than a generic "Plugin" label.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These tools (invite/set_state/send_message/remove) would have appeared in
Claude Code's tools/list and been called during normal work, burning credits
on pure UI bookkeeping. Callbacks and CallRoom plumbing remain for future
UI-driven or stream-inferred participant management.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- HookNarrationService: parses PostToolUse/Stop hook events; calls Llama
  to produce a 1-sentence natural-language narration (e.g. "Claw'd is
  reading package.json to understand the project"); falls back to
  template descriptions if Ollama is unavailable
- MCPServer: adds POST /hook endpoint — Claude Code hooks curl here on
  every tool event; fires onHookEvent callback on @mainactor
- MCPConfigManager: writeHooksConfig() merges PostToolUse + Stop hook
  entries into ~/.claude/settings.json automatically on launch
- AppDelegate: wires onHookEvent → narrate → appendParticipantMessage
  so every Claude Code tool call appears as a Claw'd feed bubble in
  the call room; also calls writeHooksConfig() at startup

The call room now acts as a live video-call window into a Claude Code
session — each tool use becomes a narrated story beat in the feed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add CallStreamWidget.swift — always-on-top floating NSPanel (420×560)
  that overlays any session during Call Mode; animates in/out with slide+fade;
  draggable from anywhere; snaps to bottom-right by default

- Add CallStreamWidgetView.swift — full brutalist redesign:
  · No circles, no soft rounding — sharp rectangular geometry throughout
  · Header: REC dot + CALL STREAM label + session timer + close button
  · Mission bar: orange left-accent shows user's spoken goal (first message)
  · Participant strip: sharp tiles with thick colored top-border accent (no circles)
    Square mascot icon with NSImage(named: "mascot-{id}") fallback to SF Symbol
  · Task queue: top 3 pending StructuredTodos; active task highlighted orange
  · Stream feed: brutalist group-chat format — NAME ─── time / message / image
    Generated/reaction messages render at 70% opacity with ~ marker
  · Spotlight panel: auto-surfaces latest image (full-width) or filename
    detected via regex from stream messages
  · Bottom bar: animated waveform + event count + END CALL red square button

- CallModeSession.swift: add createdAt: Date to CallMessage for timestamps;
  add imageData and isGenerated fields; extend appendParticipantMessage

- HookNarrationService.swift: complete implementation — parse hook events,
  extract ToolParticipant from mcp__server__tool names, extract image data
  from 3 tool_response payload patterns, Llama narration with template
  fallback, optional AutoClawd reaction for MCP/image events

- CallModeRoomView.swift: CallFeedMessageRow with 2px left-border style;
  solid gradient for real messages, dashed segments for generated ones;
  inline image support via imageData

- AppDelegate.swift: wire $pillMode observer to show/hide CallStreamWidget;
  lazy-init on first show; animate out on mode change; onHookEvent handler
  auto-joins tool participants and posts structured feed messages

- SettingsManager.swift: add callStreamWidgetEnabled (default true)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant