feat(coc): chat canvas — AI/user co-edited side panel (docs, code, custom extensions)#325
Merged
Merged
Conversation
…y AI and user) Adds a GitHub-Copilot-app-style canvas: a live markdown document in a resizable side panel next to the chat, edited by the AI through LLM tools and by the user through revision-checked autosave. Gated by the new canvas.enabled admin flag (default off). - CanvasStore: file-based persistence under ~/.coc/repos/<ws>/canvases/ with revision-checked updates and exact-match string edits - REST: list/get/save canvas routes; 409 + current record on stale saves; canvas-updated WebSocket broadcast on user saves - LLM tools: create_canvas / update_canvas / read_canvas (registry-listed, flag-gated in buildCanvasToolsAddon, emit canvas-updated SSE events) - coc-client: CanvasesClient domain with typed contracts - SPA: CanvasPanel (preview/edit, debounced autosave, conflict and remote-update banners) mounted in ChatDetail as a desktop resizable right column; useChatSSE surfaces canvas-updated events - Tests: canvas store, LLM tools, routes (HTTP-level), CanvasPanel (jsdom), registry gating; config fixture/snapshot updates Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, anchored comments Builds on the canvas side panel with the three phase-2 steering surfaces: - Version snapshots: CanvasStore writes versions/<rev>.json on every persisted revision (capped at 50 most recent); new REST endpoints list version metadata and serve single snapshots. The panel's revision chip becomes a stepper — older revisions render read-only with a history banner and a "Restore as latest" action that saves the snapshot as a new revision (disabled while local edits are unsaved). - Selection actions: selecting canvas text (preview or edit mode) shows an action bar. "Ask AI" prefills the chat composer with a prompt quoting the selection plus canvas id/revision; "Comment" opens an inline compose box. - Anchored comments: stored in comments.json (open|sent|resolved) with REST CRUD; the panel lists them with a "Send N to AI" batch action that delivers one follow-up message via sendFollowUp(..., 'enqueue') — so a busy AI receives it at the next turn boundary — and then marks the comments sent. coc-client gains listVersions/getVersion/listComments/addComment/ setCommentStatus/deleteComment. Tests cover snapshot pruning, comment lifecycle, the new routes, the stepper/restore flow, selection → Ask AI prompt, comment creation, and send-to-AI marking comments sent. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Code canvas type: create_canvas accepts type 'markdown'|'code' plus a language hint (normalized and persisted on the descriptor). The panel shows a language chip, renders the preview as a fenced highlighted block, and uses the shared MonacoFileEditor in Edit mode with the same revision-checked debounced autosave. Tool guidance now points the AI at Mermaid blocks in markdown canvases for diagrams/charts. - Export menu in the panel header: Copy content, Download file (extension derived from the language), and Save to Notes for markdown canvases (writes canvases/<slug>.md into the workspace Notes tree through the existing notes saveContent route, which creates files and parents). Deferred from the phase 3 roadmap: work-item-backed plan canvases (needs a sync-ownership design) and gist export. Tests cover code-canvas creation/normalization at the store and tool layers, fenced preview + Monaco editing + autosave, export copy, and save-to-Notes including its absence for code canvases. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the GitHub-Copilot-app "custom canvas" model: interactive panels
(kanban boards, checklists, dashboards) backed by JSON shared state that
both the AI and the user mutate through declared capabilities.
Server:
- canvas-store: type 'extension'; extension/{manifest.json,ui.html,
capabilities.js} documents; getExtension/saveExtension (saveExtension
bumps the revision so open panels reload).
- canvas-capability-runner: runs a declared capability as a pure
(state, params) => nextState transform in a node:vm sandbox — no
require/process, 1s wall-clock timeout, 1 MB state cap. Matches the
local trust model (same level as autopilot shell), not a hard boundary.
- routes: GET .../extension and POST .../capabilities/:name (revision-
checked write, canvas-updated WS broadcast + SSE emit; 422 on
capability error, 409 on concurrent edit).
Tools (gated by canvas.enabled, in CANVAS_LLM_TOOL_NAMES):
- create_or_update_extension_canvas authors the manifest + UI +
capabilities; invoke_canvas_capability runs one; read_canvas now
returns the manifest for extension canvases.
SPA:
- ExtensionCanvasView renders ui.html in an <iframe sandbox="allow-scripts">
with an injected window.CanvasHost bridge (onState/invoke/setState) over
postMessage. Human UI actions go through the same capability/save REST
gate as AI calls. CanvasPanel routes extension canvases to it in preview
mode and shows the raw JSON state in edit mode.
coc-client: getExtension/invokeCapability + contracts.
Tests cover vm sandbox isolation/timeout/errors, extension store + routes
+ tools, the iframe postMessage protocol, and panel routing. Docs updated
(AGENTS.md invariant, llm-tools/rest-api/dashboard-spa references).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…column, fullscreen Optimizes the activity + canvas layout per three asks: 1. Collapsible chat list: the desktop activity split (RepoChatTab) can collapse the left chat-list panel to a thin "Chats" rail via a hover affordance on the resize handle; the choice persists in localStorage['activity-list-collapsed']. 2. Full-height canvas right panel: ChatDetail now wraps the conversation and follow-up composer in a left column and mounts the canvas as a full-height sibling column of a top-level split, so the panel spans the whole detail pane height (beside the composer) like a dedicated right panel instead of only the conversation height. Canvas default width widened (520, max 1100) and stays resizable. 3. Fullscreen canvas: CanvasPanel header gains an expand toggle that re-renders the panel as a fixed inset-0 overlay covering the viewport (Esc exits). ChatDetail collapses the in-flow canvas column to 0 width while fullscreen so the conversation reclaims the space. Tests: CanvasPanel fullscreen toggle + Escape; RepoChatTab collapse/expand + persistence. Docs (dashboard-spa) updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Three canvas UX improvements: 1. Reopen a closed canvas: closing the panel no longer detaches it — ChatDetail keeps a thin right-side rail (mirroring the chat-list collapse rail) with a reopen button, so a linked canvas stays reachable. 2. CoC-native header buttons: the canvas header reuses the shared ICON_BTN style from ChatHeader (26x26 rounded, #848484, hover bg) with SVG icons for pop-out/fullscreen/close, and the Preview/Edit segmented control + Export use standard CoC tokens instead of ad-hoc styling. 3. Pop-out window: a header pop-out button opens the canvas in a standalone window via the established popout-shell pattern (PopOutCanvasShell + entry.tsx #popout/canvas route). The window maps the global WebSocket canvas-updated event into the panel's liveEvent and refetches on focus (reloadNonce) to pick up AI tool edits that stream over the chat SSE channel. Tests: pop-out button visibility/click, reloadNonce refetch, parsePopOutCanvasRoute, and the shell's WS→liveEvent mapping. Docs (dashboard-spa) updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The canvas feature shipped 5 tools whose schemas are sent to the model on every turn when canvas.enabled, consuming significant context. Consolidate to 3 and trim descriptions/guidance: - write_canvas — create (omit canvasId) or update a markdown/code canvas (merges create_canvas + update_canvas). - read_canvas — unchanged behavior, trimmed description. - extension_canvas — BUILD (manifest/ui/capabilities) or RUN (canvasId + capability + params) an extension canvas, dispatched by the presence of `capability` (merges create_or_update_extension_canvas + invoke_canvas_capability). The doc canvas tool keeps a lean schema (no extension fields); all extension complexity is isolated in one tool. Registry, CANVAS_LLM_TOOL_NAMES, the addon guidance prose, and user-facing tool-name references (capability runner error, comments-to-AI prompt) updated. REST routes, store, and the iframe capability path are unchanged. Tests + docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…g tests ChatDetail now calls isCanvasEnabled() from utils/config. Two existing tests that render real ChatDetail mock utils/config with an explicit factory, so the missing export threw "No isCanvasEnabled export is defined on the mock" (coc-test shards 2 & 4). Add the export to both mocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a chat canvas to the CoC dashboard — a live side panel next to a conversation where the AI and the user co-edit an artifact (a document, a code file, or a fully custom interactive app), in the genre of ChatGPT Canvas / Claude Artifacts / the GitHub Copilot app's canvases.
The whole feature is behind the
canvas.enabledadmin flag (default off), so nothing changes for existing users until they turn it on.What's included
Built up over several phases (one commit each), then polished:
CanvasStore(file-based,~/.coc/repos/<wsId>/canvases/), workspace REST routes, three LLM tools, live updates over the existing SSE (per-process) + WebSocket channels, thecanvas.enabledflag, and a typedcanvasesdomain incoc-client. Revision-checked saves with 409-on-conflict.type: 'code'with a language hint rendered via the shared Monaco editor; Mermaid blocks in markdown canvases render as diagrams; Export menu (copy / download / save-to-Notes).(state, params) => nextStatetransforms in anode:vmsandbox.PopOutCanvasShell, via the established#popout/...shell pattern).write_canvas,read_canvas,extension_canvas) rather than five, to limit the per-turn tool-schema context cost.write_canvascreates (omitcanvasId) or updates a markdown/code canvas;extension_canvasboth builds and runs an extension canvas, dispatched by the presence of acapability.Architecture notes for reviewers
node:vm(norequire/process, 1s timeout, 1 MB state cap) and the UI runs in<iframe sandbox="allow-scripts">(no same-origin). This matches CoC's local single-user trust level (the same level as autopilot shell) — it prevents accidental host coupling and runaway loops, but it is not a hardened boundary against a hostile author. If this ever runs multi-tenant, the capability sandbox should move to a real isolate first. Documents run no AI-authored code.canvas.enabledis off,buildCanvasToolsAddonshort-circuits to zero tools and no guidance prose, so there is no per-turn context cost; the tools are also filtered from the settings registry.Testing
node:vmcapability runner (sandbox isolation, timeout, load/throw/oversize errors), the REST routes (versions, comments, extension, capability invoke), and the three LLM tools.CanvasPanel(autosave, conflict/remote-update banners, version stepper + restore, selection → Ask AI, send-comments, code/Monaco, export, fullscreen, pop-out), the sandboxed-iframeExtensionCanvasViewpostMessage protocol,PopOutCanvasShellroute parsing + WS→liveEvent mapping, andRepoChatTabchat-list collapse.npm run build:packages); the registry/config fixtures and snapshots are updated.Not included (deliberately deferred)
ghcredential plumbing.🤖 Generated with Claude Code