Skip to content

feat: surfaces — a diff part, generic publish, snippet→surface rename#26

Merged
benvinegar merged 4 commits into
mainfrom
feat/surfaces-diff
Jun 16, 2026
Merged

feat: surfaces — a diff part, generic publish, snippet→surface rename#26
benvinegar merged 4 commits into
mainfrom
feat/surfaces-diff

Conversation

@benvinegar

Copy link
Copy Markdown
Member

Surfaces + a diff surface

Generalizes "snippets" into surfaces: a published card is now an ordered list of parts, each with its own kind. Two kinds today:

  • html — arbitrary agent markup, rendered sandboxed in an iframe (exactly today's snippet).
  • diff — a unified/git patch, rendered natively in the viewer by @pierre/diffs (CodeView, shiki-js engine) as a syntax-highlighted split/unified code review.

A surface can combine parts, so [html, diff] is a diagram with its code review in one versioned, commentable card.

Why diffs render outside the iframe

A diff is data (a patch), not arbitrary code — the agent sends a patch and we own the rendering. So diff parts render in the trusted viewer, not the sandbox: less injected-content risk, the viewer controls the theme, and @pierre/diffs gets its real CSS + layout managers (bundled via Vite). The snippet sandbox invariant — allow-scripts, no allow-same-origin — is untouched; it still governs every html part.

Generic publishing, all three tiers

  • MCP: publish_surface / update_surface (parts); publish_snippet / update_snippet remain as html-sugar aliases.
  • HTTP: POST/PUT/GET/DELETE /api/surfaces; legacy /api/snippets routes alias through.
  • CLI: sideshow diff <patch>, sideshow publish --diff <patch>, sideshow comment --surface.

What changed

  • Data model (server/types.ts): Surface/SurfacePart/DiffPart; Comment.surfaceId; Store *Surface methods. htmlPart/firstHtml bridge the legacy shape.
  • Storage (storage.ts, workers/sqlStore.ts): surfaces table/array; in-place migration of legacy snippets and comments.snippetId on load (deployed DOs can't reset). Both still pass test/storeContract.ts.
  • App: publishSurface/reviseSurface flows; surface-* SSE events; /s/:id?part=N renders one html part.
  • Viewer: Card iterates surface.parts (iframe per html part; new <DiffPart> per diff part); full snippet→surface rename; feedback-loop behavior preserved verbatim.
  • Docs: DESIGN_GUIDE / AGENT_SETUP / SKILL teach surfaces, parts, and diff/combined examples; CHANGELOG + AGENTS map.

Back-compat

Old snippet endpoints, MCP tools, and the ?snippet= comment param all still work; stored boards migrate automatically. Existing agent configs keep working.

Validation

  • npm test — 68 pass (store contract runs against both stores; new coverage for combined html+diff surfaces, part rendering, and the publish_surface MCP tool).
  • npm run typecheck (node + workers + viewer) clean; npm run lint / format:check clean.
  • npm run test:e2e (chromium) — 11/11 pass.
  • Diff rendering verified headless (real grid layout + syntax colors, shiki-js, no CSP/wasm issues).

Heads-up: viewer bundle size

Bundling @pierre/diffs inlines shiki's full grammar/theme set into the single-file viewer: index.html ~10.2 MB (1.81 MB gzip), up from small. The runtime highlighter only loads the langs we preload, but vite-plugin-singlefile statically inlines all lazily-importable grammars. Lever (deferred): a constrained shiki core import or externalizing grammars — but that risks the no-static-assets single-document invariant, so flagging rather than changing the build strategy here.

Follow-ups (not in this PR)

  • Cross-card attachment (parentId) — Phase 2.
  • Split/unified toggle in the card UI; large-diff virtualization; a sideshow demo diff example; a diff-rendering e2e assertion.
  • Trim the bundled shiki grammar set if the size proves a problem.

🤖 Generated with Claude Code

benvinegar and others added 4 commits June 16, 2026 10:33
Introduce the surface model: a surface is an ordered list of parts, each
with its own kind (html | diff). A snippet becomes a surface with one html
part; a diagram-with-its-diff is [html, diff] in one card.

- types: Surface/SurfacePart/DiffPart, Comment.surfaceId; Store renamed to
  *Surface methods
- storage + sqlStore: surfaces table/array, in-place migration from legacy
  snippets + comments.snippetId (deployed DOs can't reset)
- app: publishSurface/reviseSurface flows; /api/surfaces (+ /api/snippets
  html-sugar aliases); /s/:id?part=N renders one html part; surface-* events
- mcp (http + stdio): publish_surface/update_surface + snippet aliases
- cli: sideshow diff, publish --diff, comment --surface
- diff parts are data rendered by the trusted viewer, never via the iframe

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- viewer: Card iterates surface.parts — html parts keep the sandboxed iframe
  (one per part), diff parts render via a new <DiffPart> using @pierre/diffs'
  CodeView (shiki-js engine, no wasm). Full snippet->surface rename in api/
  state/App; feedback-loop behavior preserved verbatim.
- @pierre/diffs bundled into the single-file viewer.
- docs: DESIGN_GUIDE/AGENT_SETUP/SKILL teach surfaces, parts, and diff examples.
- CHANGELOG [Unreleased]; AGENTS.md map (surfacePage, surfaces/parts).
- api.test: combined html+diff surface, part rendering, publish_surface MCP.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- write responses (POST/PUT /api/surfaces + aliases) return identifiers and
  part kinds only, not the parts the agent just sent — a large diff patch is
  never echoed back. Reads (GET, session list) still carry full parts.
- tool schemas + design guide recommend the compact `patch` form for diffs
  and mark before/after `files` as the heavier fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@benvinegar benvinegar force-pushed the feat/surfaces-diff branch from 396ddf2 to 4ebc396 Compare June 16, 2026 14:36
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​pierre/​diffs@​1.2.11801009799100

View full report

@benvinegar benvinegar merged commit 04eeb28 into main Jun 16, 2026
7 checks passed
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