Skip to content

feat: Voice Workspace canvas enrichment (Mermaid, images, panel history) + orb polish (#979) - #981

Merged
vybe merged 2 commits into
devfrom
feature/979-voice-canvas-enrich
May 29, 2026
Merged

feat: Voice Workspace canvas enrichment (Mermaid, images, panel history) + orb polish (#979)#981
vybe merged 2 commits into
devfrom
feature/979-voice-canvas-enrich

Conversation

@vybe

@vybe vybe commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds two in-process voice panel toolsshow_diagram (Mermaid) and show_image — to the Voice Workspace canvas, plus client-side panel history, orb polish, and a graceful canvas transition. Endpoint contract unchanged (still the 300ms poll of GET /voice/{session_id}/panel).
  • Mermaid renders strictly inside the existing opaque-origin sandboxed iframe (per the issue's stated security model) via a self-contained mermaid.min.js IIFE bundle that exposes window.mermaid with zero runtime chunk fetches — so the Chart.js ?url-into-iframe pattern transfers cleanly.
  • Images: web URLs render directly via Vue :src; agent-workspace file paths stream through the existing authenticated /files/preview endpoint as a blob (a bare <img src> would 401).

Changes

  • src/backend/services/gemini_voice.pyshow_diagram + show_image tool declarations, registered in _PANEL_TOOL_NAMES and WORKSPACE_PANEL_INSTRUCTIONS; _classify_image_src() path-confinement helper (rejects .., absolute escapes, the /home/developer-evil sibling, data:, non-http schemes — stricter than the agent-server prefix check).
  • src/frontend/src/views/AgentWorkspace.vuemermaid/image render branches; reactive :srcdoc iframe bindings (transition-safe); 40-snapshot history ring buffer (prev/next + dropdown, live/pinned); orb asymmetric attack/release smoothing + idle breathe + larger core/glow; prefers-reduced-motion-aware cross-fade + header "updated" flash; image objectURL cache with eviction/unmount revocation + out-of-order load guard.
  • src/frontend/package.json — add mermaid (v11).
  • tests/unit/test_voice_tools.py — new panel-tool state tests, path-confinement rejection (traversal/sibling/data:/scheme), _classify_image_src unit tests, tool registration/declaration.
  • docs/memory/requirements.md (VOICE-009) + docs/memory/feature-flows/voice-chat.md.

Security

  • Agent markup/diagrams render only inside the opaque-origin sandbox="allow-scripts" iframe; diagram text injected as a JS string (JSON.stringify + <<, no </script> breakout) with mermaid securityLevel:'strict'.
  • Images render via Vue :src (no v-html); url-kind restricted to http(s)://.
  • Path confinement enforced in-process and at the existing authenticated endpoint; the confinement gate is unit-tested.
  • Panel tools are backend+frontend only — not on the MCP surface (Invariant feat: SMARTS trading pipeline with Telegram notifications and Miro visualization #13 N/A).

Test Plan

  • New + existing unit tests pass: pytest tests/unit/test_voice_tools.py (58 passed)
  • Full unit suite green: 1811 passed, 6 skipped
  • Frontend builds (npm run build); mermaid emitted as a hashed same-origin asset
  • Manual: voice workspace — show_diagram renders a flowchart; invalid syntax shows a contained error; show_image with a web URL and a workspace path; history prev/next; orb smoothing; reduced-motion

Fixes #979

🤖 Generated with Claude Code

Eugene Vyborov and others added 2 commits May 29, 2026 19:13
… polish (#979)

Add two in-process voice panel tools and frontend canvas enrichments to the
voice workspace (VOICE-009), endpoint contract unchanged.

Backend (services/gemini_voice.py):
- show_diagram(diagram, title?) → mermaid panel type
- show_image(src, title?, caption?) → image panel type
- _classify_image_src(): in-process path confinement for show_image —
  accepts http(s) URLs and workspace-confined paths, rejects '..' traversal,
  absolute escapes, the /home/developer-evil sibling, data:, and non-http
  schemes (stricter than the agent-server prefix check)
- register both in _PANEL_TOOL_NAMES + WORKSPACE_PANEL_INSTRUCTIONS

Frontend (views/AgentWorkspace.vue):
- mermaid: strict opaque-origin sandboxed iframe via self-contained
  mermaid.min.js IIFE bundle (no chunk fetches); diagram text injected as a
  JS string (JSON.stringify + '<'→<, no </script> breakout);
  securityLevel:'strict'; invalid syntax → contained error + source
- image: web URLs via Vue :src; workspace paths fetched as authenticated
  blob via /files/preview (getFilePreviewBlob); objectURLs cached + revoked
  on eviction/unmount; out-of-order load guard
- client-side panel history: 40-snapshot ring buffer + prev/next + dropdown
- orb polish: asymmetric attack/release energy smoothing, smoothed core
  size, idle breathe floor, larger core/glow
- cross-fade canvas transition honoring prefers-reduced-motion
- iframes refactored to reactive :srcdoc bindings (transition-safe)
- mermaid added to package.json

Tests (tests/unit/test_voice_tools.py): show_diagram/show_image state,
path-confinement rejection (traversal/sibling/data:/scheme), classifier
unit tests, tool registration + declaration. Full unit suite green
(1811 passed).

Panel tools are backend+frontend only (not on MCP — Invariant #13 N/A).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The design-token linter (check:tokens) rejected `text-status-error-400` —
`error` is not a registered status-* token. The failure/error color in the
design system is `status-danger` (aliased to red). Swap the class so the
build's "Verify design tokens" step passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vybe
vybe merged commit 99cb076 into dev May 29, 2026
14 checks passed
@vybe
vybe deleted the feature/979-voice-canvas-enrich branch May 29, 2026 18:23
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