Releases: vicmaster/framesmith
v1.5.2 — viewer live updates for bound repos
The standalone viewer is now actually live. framesmith-viewer printed Watching ~/.framesmith for changes… but only watched the global store — never the bound repos' .framesmith/ dirs, where agents in a bound project actually write. The gallery stayed stale until a restart.
- The watcher now covers every aggregation source: the global canvases dir,
registry.json, and each registered repo's.framesmith/recursively (per-project subdirs included). A repo bound while the viewer runs starts updating live too. - Crash-proof reloads: live watching can race an agent's half-written JSON — a corrupt/mid-write repo file now skips one reload pass instead of killing the viewer process.
- Startup lists each watched bound repo, and every reload logs a line — "is it live?" is answerable from the terminal.
- Also included from #101: the discoverability test guard + fully enumerated import-report docstrings.
With this, npx -p framesmith framesmith-viewer reflects agent writes within ~1–2 seconds, no restarts. npm i -g framesmith@1.5.2.
v1.5.1 — agent-facing docs for structural reconstruction
Docs-only patch. v1.5.0's structural reconstruction was fully documented in the README — but MCP agents don't read READMEs. This release puts it on the surfaces they do see:
- Server
instructions(loaded into every client on connect) now describe the structural layer — tables → proportional columns, grids from the computed template, centering, geometry clustering — andreport.layout's contract: astack-fallbackentry means hand-fix that one container; everything else arrived structurally correct, so don't rebuild imported tables/grids by hand. initGOTCHAS carry the same guidance.canvas_import_html/canvas_import_urldocstrings now cover all of Phase 18 (grid/centering/clustering were missing).framesmith://guidelinesgains an Importing from implementation section — the import workflow, the three report sections and what to do with each, the Tailwind-snippet runtime note,tokenMatchdefaults, and auth hygiene.
No code changes; no behavior changes. npm i -g framesmith@1.5.1.
v1.5.0 — Structural reconstruction
The import's layout layer. v1.4.0 made imports token-faithful (colors, fonts, icons, controls); v1.5.0 makes them structurally right — tables come back as columns, grids as rows-of-columns, centered cards centered. Born from a real dogfooding session on a Rails + Tailwind v4 admin screen (issue #92).
Phase 18 — Structural reconstruction (#92)
<table> → real columns. table/tr/td reconstruct as a vertical frame of horizontal row frames with proportional percentage cell widths derived from the computed boxes — a 4-column User Management table imports as 40/20/25/15%, with cell content (avatars, chips, select primitives) landing inside its columns. thead/tbody unwrap, <caption> becomes a text node, row bottom-borders become exact hairline dividers, colspan is handled free via box proportion, rowspan warns.
CSS grid → rows of proportional columns. The computed grid-template-columns (resolved px, even for fr tracks) drives reconstruction; numeric grid-column spans win over box-derived ones; single-track grids import as faithful stacks with no noise; irregular templates degrade honestly.
Centered content stays centered. margin: auto and max-width don't survive as computed "center" values — the importer now detects both: auto-margin children keep their real width with the parent gaining alignItems: "center", and max-width becomes the renderer's fluid width: "100%" + maxWidth idiom. This also fixed a latent v1.4.0 bug where wrapper-collapse silently dissolved flex-center wrappers (the classic sign-in shape).
Geometry clustering for everything else. Floats, inline-block grids, and unmodeled multi-column CSS reconstruct from computed bounding boxes alone — row bands with conservative consistency guards. A wrong reconstruction is worse than an honest stack: anything that looks multi-column but won't cluster consistently imports as a stack with a stack-fallback report entry and warning, and genuinely vertical content is never touched.
report.layout records how every container was reconstructed (table | grid | centered | geometry | stack-fallback) — the same honesty contract as snapped/literals/warnings.
Intent-mapper hygiene. Unresolvable $token refs produced by Tailwind class intent now revert to the computed value they overrode (ground truth) instead of warning — killing the bogus $collapse/$body-sm noise structurally; border-style keywords (border-collapse etc.) never reach the color branch.
Upgrading
npm i -g framesmith@1.5.0 (or let npx framesmith pick it up). No breaking changes; v1.4.0 canvases and tool calls work unchanged. Existing imports simply get better structure — re-import and compare.
Spec trail: docs/specs/PHASE-18-SPEC.md — PRs #93–#98.
v1.4.0 — Faithful parity + import from implementation
Two complete phases, born from a single real-world dogfooding session (issues #77 and #78): framesmith canvases now reproduce what ships — and can import what ships.
Phase 16 — Faithful parity (#77)
Fonts load by name. Set fontFamily: "Inter" in a typography token (or on a node) and the renderer resolves it from Google Fonts automatically — at token-write time plus a render-time backstop. Binaries cache under ~/.framesmith/fonts/ (offline + deterministic after first resolve); typography.body.fontFamily becomes the document default; unresolvable families degrade to the fallback stack with an explicit Font warnings item, never silently. set_fonts now accepts families: ["Inter"] and Google Fonts css2 stylesheet URLs.
Material Symbols. A second bundled icon set (3,800+) alongside Lucide: icon: "material:check", iconStyle: "outlined" | "rounded" | "sharp", -fill suffix for filled variants.
Input primitives. toggle, checkbox, radio, select are real node types with checked / disabled / value, styled from design tokens ($accent/$border/$bg-surface) with neutral fallbacks — no more faking controls from frames + ellipses.
Component structures. apply_structure now stamps component-kind scaffolds under any targetId, repeatably, with re-keyed IDs + an idMap: data-table, form-field, toolbar, stat-card, toggle-row. A high-fidelity table is one stamp instead of ~80 hand-placed nodes.
Also: fontVariationSettings for variable-font axes, letterSpacing/textTransform surfaced everywhere agents look, and Chrome-launch hardening (FRAMESMITH_CHROME_PATH / PUPPETEER_EXECUTABLE_PATH override + actionable launch errors).
Phase 17 — Import from implementation (#78)
canvas_import_html — paste a shipped component's HTML (+ optional CSS) and get an editable canvas: flex → frames, text runs with computed type props, <img> → image, inline SVGs → icon nodes when they match a bundled glyph, checkboxes/switches/selects → the input primitives with their live state. Flatten knobs (collapseWrappers / mergeTextRuns / dropInvisible / maxDepth).
Token re-mapping — the import is a token-driven design, not a pile of hex: Tailwind classes map to intent (bg-surface → fill: "$surface", gap-4 → 16; the v4 default palette is bundled, generated from the official oklch values by Chrome itself), and remaining literal colors snap to your design system — near-ties between tokens are reported and left literal, never guessed.
canvas_import_url — import a live page: viewport, component-level selector, waitFor for client-rendered UI, and auth headers/cookies for gated pages (used in a throwaway browser context, never persisted).
canvas_sync_from_url — drift detection: ephemeral re-import + pixel diff against the design-of-record → diff image + changePercent. An unchanged page diffs at exactly 0% — the import→render round-trip is pixel-deterministic — so design ↔ code divergence is a number you can gate CI on (pattern documented in the README).
Every import returns a mapping report (snapped / literals / scaleMatches / unmatchedFonts / unmatchedIcons / warnings) — lossy by design, honestly reported.
Upgrading
npm i -g framesmith@1.4.0 (or let npx framesmith pick it up). No breaking changes to existing tools; canvases from v1.3.0 work unchanged. New dependency: @material-symbols/svg-400. Fonts and icon recognition work offline after first use.
Full spec trail: docs/specs/PHASE-16-SPEC.md · docs/specs/PHASE-17-SPEC.md — PRs #79–#91.
v1.3.0 — Cliché guardrails + Structured critique
Two phases land in v1.3.0, both built spec-first (docs/specs/PHASE-12-SPEC.md, PHASE-13-SPEC.md).
✨ Phase 12 — Cliché & craft guardrails
canvas_evaluate now scores cliché, not just craft — the visual tells that read as machine-made, all detected on the scene graph (and confirmed against the render).
- New
clichecategory (weight 15), each issue tagged with atell:- default purple/indigo accent · gradient/glow overuse · fake browser/OS chrome (traffic-light dots) · the hanging eyebrow-beside-heading header · fabricated metrics/testimonials/logos (honest-content)
- Mechanical autofixes via
canvas_autofix— swap a known-default purple accent → neutral, delete a fake-chrome strip. Taste tells (gradients, hanging header, fabricated copy) are suggestion-only. - Genre-aware loosening —
genreoption / provenance preset relaxes intentional tells (e.g.materialallows purple).
✨ Phase 13 — Structured critique & auto-revision
The LLM judge (opt-in mode:"llm") moves from one opaque number to a fixed multi-axis rubric, and can now close the loop.
- Rubric: hierarchy · execution · specificity · restraint · variety — each 1–5 with a rationale; derived 0–100 overall.
- Revision threshold: per-axis
floor(default 3,FRAMESMITH_CRITIQUE_FLOOR) →needsRevision+failingAxes. - New
canvas_revisetool: bounded judge → revise failing axes viabatch_designops → re-judge (≤3 passes); stops on pass / cap / no-improvement (reverts regressions) / apply-error. - Verdict stamping:
metadata.critique+ a compact entry on the per-project build log; viewer shows a verdict chip.
⚠️ Breaking change (experimental LLM mode only)
canvas_evaluate with mode:"llm" — the llmCritique shape changed: free-text strengths/weaknesses are replaced by the per-axis rubric (plus needsRevision / failingAxes). All heuristic categories and every other tool are unchanged.
npx framesmith # or
claude mcp add framesmith npx framesmith
Full changelog: v1.2.0...v1.3.0
v1.2.0 — Agent onboarding & dogfooding fixes
This release closes #64 — an onboarding gap plus a batch of bugs found designing a feature end-to-end from a real agent session. All changes are additive; pre-1.2 canvases load unchanged (no migration).
Onboarding
- Server
instructions— framesmith now ships an MCPinstructionsblock, so any client is oriented on connect (the Workspace › Project › Canvas model, the layered$tokensystem, "readframesmith://guidelinesfirst", and current gotchas) with zero tool calls. - New
inittool — one idempotent call to start a session: binds the repo if needed, scaffolds the convention projects (Foundations + UI), and returns the live re-keyed IDs, a workflow cheatsheet, the gotchas, and the viewer URL. The fix for "binding re-keys IDs, so docs go stale instantly." batch_designreturns a{ varName: nodeId }map — map your bound variables straight to created node IDs instead of counting result positions.
Fixes
- A CSS-string
gradient/shadowsno longer crashesscreenshot(structured form stays canonical; strings are accepted). canvas_moveon a bound repo now relocates the canvas file to the target project's directory.canvas_evaluateno longer flags a contrast ratio that rounds to exactly 4.5:1 (rounded to 2 decimals for compare + display).apply_presetpreserves tokens inherited from the workspace/project design system instead of silently overwriting them (reported aspreservedFromDesignSystem).import_design_mdnow reads list / table /name: valuetoken formats per section, honors explicit named spacing instead of injecting a default scale, and documents the accepted schema.
Docs
- A "Sharp edges" section in
framesmith://guidelinesconsolidating the operational caveats; loudercanvas_bindre-key callout.
Install: npx -y framesmith · Upgrade: already on latest.
v1.1.0 — Design variety & anti-sameness
Phase 11 makes framesmith deliberately vary page layouts instead of converging on the same hero-and-three-cards shape every session.
Highlights
- Layout structure library — six named page scaffolds (
marquee-hero,bento-grid,stat-led,editorial-longform,split-workbench,catalogue), each a partial scene tree of labeled placeholders. Distinct from color presets; they compose. list_structures/apply_structure— pick a structure, get a placeholder skeleton to populate, render, and verify. PassprojectIdtolist_structuresfor a diversification signal.- Structure taxonomy — every scaffold is tagged on four independent axes (
heroTreatment,density,rhythm,alignment), so "differs from last time" is computable. - Per-project build log — records the structure / preset / axes behind each canvas, in both the global store and repo-bound
.framesmith/dirs. - Diversification signal —
canvas_create(andlist_structureswithprojectId) surface recent work plus a "differ on ≥ 1 axis" hint when recent canvases converge. Advisory, never blocking. - Provenance — each canvas records what produced it, shown as a chip on the viewer detail page.
Upgrading from 1.0.0
- Additive — no migration, no data risk. Existing canvases load unchanged (provenance is optional); build-log files are created lazily on first use. Forward- and backward-compatible.
- Existing canvases simply have no provenance until you apply a structure/preset to them; their projects' diversification signal starts empty.
- To get 1.1.0: update via
npx framesmith@latest(or clear the npx cache) and restart your MCP client so it loads the new version.
Full changelog: v1.0.0...v1.1.0
v1.0.0 — framesmith
First public release. npx framesmith — an open-source MCP server that gives your AI coding agent a visual design canvas: sketch the UI, review it in a browser, agree on the design before any framework code gets written.
Formerly canvas-mcp — renamed to framesmith for v1.0.
Headline: your designs live in your repo
Canvases persist as open JSON in a .framesmith/ directory, checked in alongside your code — committable, diffable in code review, and self-contained on clone. The repo is the source of truth; no proprietary, encrypted store. Bind a workspace with canvas_bind and the server auto-detects it on startup.
What's in 1.0
- Scene graph → HTML/CSS → Puppeteer screenshots — agents author via MCP tools, you review real browser renders.
- Workspaces / projects and a Figma-style standalone viewer (read-only gallery across global + every bound repo).
- Design systems with workspace → project → canvas token inheritance.
- Responsive breakpoints (
stack/wrap/ fluid widths) with true reflow. - Evaluation — heuristic design scoring + optional LLM-as-judge, with auto-fix ops.
- Repo-bound storage — deterministic JSON, asset externalization (
.framesmith/assets/), external-change safety, viewer write-back.
Install
# Claude Code
claude mcp add framesmith -- npx framesmithWorks with any MCP client (Cursor, Windsurf, VS Code, Codex). See the README for per-client config.
MIT licensed.
v0.9.0 — Workspace-level design systems
Tokens declared once, inherited everywhere.
The Coide use case finally clicks: set the design system once on the workspace, every canvas under it resolves $primary / $bg / $lg automatically.
Highlights
Workspace.designSystem+ symmetricProject.designSystem— declare tokens once at the workspace level; projects can override.- Three-layer resolution at render:
canvas.variables(override) →project.designSystem→workspace.designSystem→ built-in defaults. Per-category merge — setting onlycolorsdoesn't resetspacing/radius/typography. - Six new MCP tools:
workspace_set_design_system,workspace_get_design_system,workspace_apply_preset+ symmetricproject_*trio. Presets (dark,light,material,minimal) installable at workspace OR project level. - Shared
getCanvasTokens(canvas)helper insrc/workspaces.ts— every render path (MCP tools, viewer, evaluate, canvas_diff) routes through it. The docstring warns against the anti-pattern that bit us in #40.
Authoring story
// Once per workspace — brand colors set globally
workspace_set_design_system({
workspaceId,
variables: {
colors: { primary: "#0066ff", bg: "#0a0a0a" },
spacing: { sm: 8, md: 16, lg: 24 }
}
});
// Every canvas under that workspace
batch_design({ canvasId, operations: 'I(parent, { fill: "$primary" })' });
// → resolves to #0066ff via workspace inheritance, no canvas-level redefinitionBonus
- Fix #40 — viewer / canvas_diff / evaluate were missed in the Phase 9 PR, leaving them with the old
resolveVariables(canvas.root, canvas.variables)bypass. Caught while dogfooding the release page. - Dogfood #41 —
scripts/build-phase9-showcase.tscomposes a single-canvas hero that visually documents canvas-mcp's design system AND proves three-layer inheritance works end-to-end (canvas itself hasvariables: {}— every value resolves from workspace tokens). Plus reorganized canvas-mcp's own dogfood artifacts intoDesign system / UI / Releasesprojects following Figma + atomic design + Spotify Encore conventions.
Backward compatibility
All Phase 9 additions are opt-in:
- Existing canvases without workspace/project
designSystemrender identically — the merge falls through tocanvas.variablesalone. setVariables/getVariables/ canvas-levelapply_presetunchanged.- Workspace/Project JSON gain an optional
designSystemfield; older persisted entries without it load cleanly.
No migration needed.
What's next
Only Phase 10 (Ecosystem — image generation, HTTP transport, VS Code extension, Figma import, marketplace, plugins) remains for the v1.0 push.
v0.8.0 — Renderer expressiveness
The renderer learned how to fade in, blur what's behind it, load a real typeface, draw a custom mark, and know which ancestor to anchor against.
Five primitives that expand what a canvas-mcp design can express. Every item came from a concrete Phase 7 slice-5 design moment the renderer couldn't render.
Highlights
- Auto
position: relative— frames automatically receiveposition: relativewhen a descendant usesposition: absolutewithout a positioned ancestor. Fixes the slice-5a "absolute child escapes to body coordinates" bug. (#31) - Custom fonts —
set_fonts/get_fontsMCP tools. Renderer emits@font-face+<link rel="preconnect">per origin +font-display: swap. (#32) backdropFilter— structured object withblur/saturate/brightness/contrast. Bothbackdrop-filterand-webkit-backdrop-filteremitted for Safari. LegacybackdropBlurstill works. (#33)pathnode type — custom SVG marks via rawd+viewBox. Fill/stroke apply to the path element (not the wrapper). Tight character whitelist ond. (#34)- Animations — structured
animationfield referencing built-in keyframes (fadeIn,slideUp,slideDown,scaleIn).@keyframesauto-emitted only when referenced. Structuredtransitionready for future hover/JS state changes. (#35)
Bonus
- Inline style escape fix —
"and&in CSS values now HTML-escape correctly. Found while dogfooding the release page withfontFamily: '"Inter", system-ui, sans-serif'(the inner"was prematurely closing the outerstyle="..."attribute). (#36) - Dogfood release page — single-canvas hero composed via canvas-mcp itself, exercising every Phase 8 primitive. Reproduce with
npx tsx scripts/build-phase8-release.ts, then visit/canvas/phase8-releasein the viewer to see animations play. (#37)
Backward compatibility
All Phase 8 additions are opt-in:
- Existing canvases without
fonts/animation/transition/backdropFilter/pathnodes render identically. - The legacy
backdropBlur: numberfield keeps working unchanged. - Auto
position: relativeonly applies via external stylesheet rule, so inlinepositiondeclarations always win. - The renderer escape fix is invisible to canvases that didn't trip the bug.
No migration needed.
What's next
Phase 9 — Workspace-level design systems. Promote per-canvas variables to workspace-inherited tokens with explicit per-canvas overrides. "Every Coide canvas should follow Coide's design system without redefining colors per canvas."

