Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Every screen below was built end-to-end by an agent with framesmith's own vocabu

![A specialty-coffee checkout built by an agent in framesmith, light theme: a masthead with the store mark and a secure-checkout note, a form column with Contact, Delivery and Payment sections — real radio, checkbox and input controls, a two-option delivery-speed selector — beside a sticky order summary listing two line items, subtotal, free delivery, a first-order credit, the total, and a Place order button, with a gift-code card beneath. Generated from one seed (#A24B1E) on the "soft" personality and stamped with the commerce genre.](https://raw.githubusercontent.com/vicmaster/framesmith/master/docs/framesmith-checkout.png?v=2.1)

> Not every screen is a dashboard. This checkout carries the `commerce` genre — the transactional counterpart to `dashboard` — so the evaluator reads its line prices and total as the design rather than as fabricated data. It still cleared the same bar as the two screens above, including designed **empty-basket** and **loading** states.
> Not every screen is a dashboard. This checkout carries the `commerce` genre — the transactional counterpart to `data` — so the evaluator reads its line prices and total as the design rather than as fabricated data. It still cleared the same bar as the two screens above, including designed **empty-basket** and **loading** states.

```
MCP Client → stdio → framesmith server
Expand Down Expand Up @@ -64,7 +64,7 @@ Run `npx -p framesmith framesmith-viewer` to start the standalone browser viewer

> Above: a canvas in the detail view with the **Quality inspector** on the right — the same `canvas_evaluate` score the agent sees, with per-category bars and the issue list; the tabs along the top switch to the **Design-system** and **Feedback** panels. The toolbar exposes breakpoint previews, Compare, Fit, JSON, a **Comment** toggle for point-and-tell feedback, and lifecycle actions.

**Quality panel.** The canvas detail view shows a read-only **quality inspector** on the right: the heuristic `canvas_evaluate` score (0–100), per-category bars, and the issue list — each cliché tell with its `category · tell` badge, severity, and suggestion. Issues that `canvas_autofix` can resolve carry an **auto-fixable** tag, and clicking any issue **highlights its node** in the live preview. Every gallery card also shows a color-coded score badge so weak canvases stand out at a glance. The score matches what your agent sees over MCP (same fast-mode evaluation, genre-relaxed by the canvas's preset) — it's computed for display only and never written back.
**Quality panel.** The canvas detail view shows a read-only **quality inspector** on the right: the heuristic `canvas_evaluate` score (0–100), per-category bars, and the issue list — each cliché tell with its `category · tell` badge, severity, and suggestion. Issues that `canvas_autofix` can resolve carry an **auto-fixable** tag, and clicking any issue **highlights its node** in the live preview. Every gallery card also shows a color-coded score badge so weak canvases stand out at a glance. The score matches what your agent sees over MCP (same fast-mode evaluation, relaxed by the canvas's stored genre and applied preset) — it's computed for display only and never written back.

![framesmith point-and-tell feedback — a newsletter signup card in the detail view with Comment mode active: a popover anchored to the clicked paragraph shows breadcrumb chips (Body · Signup Card · Document · whole page) and a typed note, while the Feedback inspector tab on the right lists one open comment on "Fine Print" and one resolved comment with the agent's reply beneath it.](https://raw.githubusercontent.com/vicmaster/framesmith/master/docs/framesmith-feedback.png?v=1.8)

Expand Down Expand Up @@ -582,7 +582,7 @@ List available style guide presets. No params. Returns preset names and descript

### `apply_preset`

Apply a style guide preset to a canvas. Merges preset design tokens into the canvas variables, and copies in any reusable components (`button`, `card`, `badge`) the preset defines so they can be instanced. The preset is also recorded in the canvas provenance + per-project build log. Tokens the canvas already resolves through an inherited workspace/project design system are kept rather than overwritten and reported as `preservedFromDesignSystem` — for typography this merge is field-wise (a partially-specified inherited token, e.g. just `fontSize`, is filled out with the preset's other fields rather than shadowing the whole role); set them explicitly with `set_variables` if you want the preset's values instead.
Apply a style guide preset to a canvas. Merges preset design tokens into the canvas variables, and copies in any reusable components (`button`, `card`, `badge`) the preset defines so they can be instanced. The preset is recorded in `metadata.provenance.preset` and the per-project build log. It never changes the canvas's genre (`canvas_set_genre` owns that); a genre an older framesmith stored in the preset field is moved to `provenance.genre` first. The material preset relaxes the accent-hue and pure-black-white tells on its own, whatever the genre. Tokens the canvas already resolves through an inherited workspace/project design system are kept rather than overwritten and reported as `preservedFromDesignSystem` — for typography this merge is field-wise (a partially-specified inherited token, e.g. just `fontSize`, is filled out with the preset's other fields rather than shadowing the whole role); set them explicitly with `set_variables` if you want the preset's values instead.

| Param | Type | Description |
|-------|------|-------------|
Expand Down Expand Up @@ -788,14 +788,14 @@ Comments are authored in the viewer: toggle **Comment** in the detail-page toolb

### `canvas_set_genre`

Durably declare what a canvas *is* — writes the evaluation genre to `metadata.provenance.preset` without the token/component churn of `apply_preset`, so an already-styled canvas can be calibrated in one call. The stamp is what `canvas_evaluate` / `canvas_autofix` / the viewer's quality panel read when no explicit `genre` param is passed.
Durably declare what a canvas *is* — writes the evaluation genre to `metadata.provenance.genre` without the token/component churn of `apply_preset`, so an already-styled canvas can be calibrated in one call. Genre and preset are separate fields: `apply_preset` never changes the genre, and a genre an older framesmith stored in the preset field moves to its own field on the first write. The stamp is what `canvas_evaluate` / `canvas_autofix` / the viewer's quality panel read when no explicit `genre` param is passed.

| Param | Type | Description |
|-------|------|-------------|
| `canvasId` | string | Canvas to stamp |
| `genre` | string \| null | Genre to stamp (`"dashboard"`, `"commerce"`, `"material"`, …) — `null` clears the stamp |
| `genre` | string \| null | Genre to stamp (`"data"`, `"commerce"`, `"material"`, …) — `null` clears the stamp |

Returns `{ canvasId, genre, previous, relaxes, note?, versionHashUnchanged }` — `relaxes` lists the cliché tells the stamp relaxes; an unknown genre is stored but relaxes nothing, and the `note` says so immediately. Other provenance facts (structure, `importedFrom`) are preserved, and the stamp never moves the canvas's `versionHash` (the hash covers design content only) — recorded approvals stay valid.
Returns `{ canvasId, genre, canonical, previous, relaxes, presetRelaxes?, note?, versionHashUnchanged }` — `canonical` is the genre with its alias folded (`"dashboard"` → `"data"`, `"checkout"` → `"commerce"`, what evaluate reports); `relaxes` lists the cliché tells the stamp relaxes; `presetRelaxes` (`{ preset, relaxed }`) appears when the canvas's preset relaxes tells on its own (the material preset), which a genre clear doesn't remove; an unknown genre is stored but relaxes nothing, and the `note` says so immediately. Other provenance facts (structure, `importedFrom`) are preserved, and the stamp never moves the canvas's `versionHash` (the hash covers design content only) — recorded approvals stay valid.

### `canvas_evaluate`

Expand All @@ -807,7 +807,7 @@ Open point-and-tell comments block too: when the canvas has any, the result carr

A `FRAGILE` stress run blocks too. Once `canvas_stress` has recorded a verdict for the canvas, the result carries **`stress`**: `{ verdict, warnings, current }`, where `current` says whether that run was on the canvas's present `versionHash`. A `FRAGILE` verdict keeps the directive `NOT READY` even with zero evaluate issues, until a full `CLEAN` run (all five perturbations) replaces it; a subset run can record `FRAGILE` but never clear it. When `current` is false the directive tells you to re-run stress to confirm your fix. The verdict lives in server memory, so a restart forgets it. `canvas_evaluate` never runs stress itself.

Whenever the `cliche` category runs, the result also carries a **`genre`** field auditing the genre decision: `{ active, source, relaxed, notRelaxed }` — the genre in effect, whether it came from the `genre` param or the canvas's provenance stamp, the tells it relaxed, and `notRelaxed` (`[{ tell, relaxedBy }]`) — tells still flagging that a *different* genre would relax. A score pinned by tells listed in `notRelaxed` usually means the wrong genre was declared.
Whenever the `cliche` category runs, the result also carries a **`genre`** field auditing the genre decision: `{ active, source, relaxed, notRelaxed, byPreset? }` — the genre in effect (canonical: a `"dashboard"` stamp reports `"data"`, a `"checkout"` one `"commerce"`), whether it came from the `genre` param or the canvas's provenance stamp, the tells relaxed, `notRelaxed` (`[{ tell, relaxedBy }]`) — tells still flagging that a *different* genre would relax — and `byPreset` (`{ preset, relaxed }`), present when the applied preset relaxed tells on its own (the material preset relaxes accent-hue and pure-black-white whatever the genre). A score pinned by tells listed in `notRelaxed` usually means the wrong genre was declared.

Whenever the `coverage` category runs on a **base** canvas, the result also carries a **`coverage`** field: `{ dataBearing, states, missing }` — whether the screen carries data-bearing content, which state variants (`canvas_add_variant`) already exist, and which demanded states are missing. It's absent on variant canvases (a variant *is* a designed state, so it doesn't demand states of itself).

Expand All @@ -816,7 +816,7 @@ Whenever the `coverage` category runs on a **base** canvas, the result also carr
| `canvasId` | string | Canvas ID to evaluate |
| `mode` | `"fast"` \| `"detailed"` \| `"llm"` | `"fast"` = JSON-tree analysis only (<100ms). `"detailed"` adds Puppeteer-based pixel-level overlap checks, laid out at the canvas's artboard size. `"llm"` runs fast-mode heuristics plus a vision-model critique (provider picked from `FRAMESMITH_LLM_PROVIDER` or whichever of `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` is set — costs one paid API call per invocation). Default `"fast"`. |
| `categories` | string[]? | Subset of `spacing`, `color`, `typography`, `structure`, `consistency`, `cliche`, `coverage`, `usability`. Defaults to all. |
| `genre` | string? | Style that relaxes specific `cliche` gates — `"material"` allows purple and white elevated surfaces; `"dashboard"` (alias `"data"`) allows realistic figures on data-dense screens; `"commerce"` (alias `"checkout"`) allows the money on a transactional screen — cart, checkout, order confirmation, billing history. Defaults to the canvas's provenance preset if stamped (`canvas_set_genre` stamps it durably, no token churn). Pick by what the screen is *for* (read screens with published figures → `dashboard`; a screen you complete a purchase on → `commerce`; editors/admin forms → `material`; a pricing page is marketing and gets none of them), not what it contains — the result's `genre` field audits the choice. |
| `genre` | string? | Style that relaxes specific `cliche` gates — `"material"` allows purple and white elevated surfaces; `"data"` (alias `"dashboard"`) allows realistic figures on a screen whose numbers are the user's own data (dashboards, activity, finance, fitness, logistics); `"commerce"` (alias `"checkout"`) allows the money on a transactional screen — cart, checkout, order confirmation, billing history. Defaults to the canvas's stored genre (`canvas_set_genre` stamps it durably, no token churn; `apply_preset` never changes it). Replaces only the genre: a material-preset canvas keeps the material relaxation. Pick by what the screen is *for* (read screens with the user's own figures → `data`; a screen you complete a purchase on → `commerce`; editors/admin forms → `material`; a pricing page is marketing and gets none of them), not what it contains — the result's `genre` field audits the choice. |

**Categories and what they check**

Expand Down Expand Up @@ -906,7 +906,7 @@ Runs `canvas_evaluate` in fast mode and returns just the subset of issues with a
|-------|------|-------------|
| `canvasId` | string | Canvas to autofix |
| `categories` | string[]? | Restrict to fixes from these categories (default: all) |
| `genre` | string? | Style that relaxes specific `cliche` gates — `"material"` allows purple and white elevated surfaces; `"dashboard"` (alias `"data"`) allows realistic figures on data-dense screens; `"commerce"` (alias `"checkout"`) allows the money on a transactional screen — cart, checkout, order confirmation, billing history. Defaults to the canvas's provenance preset if stamped (`canvas_set_genre` stamps it durably, no token churn). |
| `genre` | string? | Style that relaxes specific `cliche` gates — `"material"` allows purple and white elevated surfaces; `"data"` (alias `"dashboard"`) allows realistic figures on a screen whose numbers are the user's own data (dashboards, activity, finance, fitness, logistics); `"commerce"` (alias `"checkout"`) allows the money on a transactional screen — cart, checkout, order confirmation, billing history. Defaults to the canvas's stored genre (`canvas_set_genre` stamps it durably, no token churn; `apply_preset` never changes it). Replaces only the genre: a material-preset canvas keeps the material relaxation. |
| `apply` | bool? | Write the fixes to the canvas in this call (default `false`: propose only) |

**What gets auto-fixed**
Expand Down
8 changes: 4 additions & 4 deletions VISION.md
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ Authoring intent: structures are scene-graph data, not prompt text — the agent
- [x] Detectable tells (scene-graph + render): default purple / indigo accent hue, gradient / glow overuse, fake browser / phone / IDE chrome (traffic-light-dot frames), the hanging "tag-left / heading-right" header
- [x] Honest-content check — flag fabricated-looking metrics / testimonials / logos in placeholder copy; suggest a labeled placeholder convention ("metric to confirm" + neutral block) instead
- [x] Auto-fix ops where mechanical (swap a *known-default* purple accent → neutral, delete a fake-chrome strip), consistent with Phase 6 `canvas_autofix`; taste tells (gradient/glow, hanging header, fabricated copy) are suggestion-only
- [x] Genre-aware loosening — `RELAXED_BY_GENRE` map; genre from `provenance.preset` or an explicit `genre` option (today `material` relaxes the purple gate)
- [x] Genre-aware loosening — `RELAXED_BY_GENRE` map; genre from `provenance.genre` (Phase 30; originally `provenance.preset`) or an explicit `genre` option (today `material` relaxes the purple gate)
- [x] Guidelines update — `docs/GUIDELINES.md` "Cliché & craft" section + `canvas_evaluate` / `canvas_autofix` tool descriptions steer authoring away from the tells up front

_Full spec-driven breakdown in [`docs/specs/PHASE-12-SPEC.md`](docs/specs/PHASE-12-SPEC.md). Phase 12 complete._
Expand Down Expand Up @@ -451,7 +451,7 @@ Eight issues from one dogfooding session (a financial dashboard: #129–#136) sh

- [x] Slice A — rendering primitives: per-side borders (`borderTop`…, style solid/dashed/dotted) + `strokeDasharray` on paths (#131, #132)
- [x] Slice B — query + autofix: `find_nodes` (property/text/name → ids + paths), `canvas_autofix apply: true` + array-padding fixes (#136, #133)
- [x] Slice C — evaluator calibration: `dashboard` genre relaxes honest-content; type-scale pinning from typography tokens; directive/llm-gate docs (#135)
- [x] Slice C — evaluator calibration: `dashboard` genre (now `data`, with `dashboard` kept as its alias) relaxes honest-content; type-scale pinning from typography tokens; directive/llm-gate docs (#135)
- [x] Slice D — fonts: generic aliases (mono/sans → CSS generics), stylesheet-URL family label honored, authoring-time unresolved-family warning (#134)
- [x] Slice E — components: `create_component` (promote subtree) + `copy_nodes` (cross-canvas, defs travel) (#130)
- [x] Slice F — charts: data-driven `chart` node (line + bar, multi-series, domains, gridlines/labels); dashboard pattern refresh (#129)
Expand Down Expand Up @@ -545,7 +545,7 @@ _Full spec-driven breakdown in [`docs/specs/PHASE-29-SPEC.md`](docs/specs/PHASE-

The mobile attempt (2026-09-07) built one iOS screen end to end on v2.2.0 and got 100/100, READY TO PRESENT, without a single mobile check running: the touch-target rule only looks at form controls, and the screen had none. Mobile today is a screen size, not a surface. This phase gives a canvas a declared `type` (`web` or `mobile`), separates it from genre and preset so the three declarations stop colliding, and builds the three layers a surface needs: rules that judge a phone, vocabulary to build one, and starting points. Acceptance is literal: the attempt screen rebuilt from a mobile archetype, CLEAN with every tap target a primitive.

- [ ] Slice A — untangle the declarations: genre gets its own field so `apply_preset` stops overwriting it, every stored canvas keeps its verdict through a legacy read, and `data` becomes the canonical purpose genre with `dashboard` as its alias
- [x] Slice A — untangle the declarations: genre gets its own field so `apply_preset` stops overwriting it, every stored canvas keeps its verdict through a legacy read, and `data` becomes the canonical purpose genre with `dashboard` as its alias
- [ ] Slice B — the type axis: `metadata.surface` with one device profile (`iphone-390`), `canvas_create({ type })` and `canvas_set_type`, a ruleset table keyed by type, genre and preset that composes by union and can turn rules on, a `surface` report on every evaluation, and a mobile type scale from `generate_design_system`
- [ ] Slice C — the mobile ruleset: 44px targets as an error on every tappable primitive, safe areas, a type floor, undeclared phone-width canvases told to declare, hand-built tap targets and multi-column prose named as advisories, and stress that knows the type
- [ ] Slice D — vocabulary: `fixed` / `sticky` positioning, `safeArea` on the root, and the `status-bar`, `home-indicator`, `nav-bar`, `tab-bar`, `button`, `list-row` and `sheet` primitives
Expand All @@ -567,7 +567,7 @@ _Full breakdown in [`docs/specs/CI-SPEC.md`](docs/specs/CI-SPEC.md). Deliberatel
### Issue-driven improvements (post-v1.8)

- [x] `replace_matching_properties` — bulk property edit: apply one `set` to every node matching a property/value predicate (AND across keys, token refs match literally, structured values by shape) with `scope`/`type` filters and a `dryRun` preview; ends the one-`U()`-per-node grind for wide changes (issue #127)
- [x] `canvas_set_genre` — stamp (or clear) `metadata.provenance.preset` directly, without `apply_preset`'s token/component churn, so an already-styled canvas can be calibrated for cliché-tell relaxation in one call; never moves `versionHash` (issue #162)
- [x] `canvas_set_genre` — stamp (or clear) `metadata.provenance.genre` directly (originally the `preset` field; Phase 30 split them), without `apply_preset`'s token/component churn, so an already-styled canvas can be calibrated for cliché-tell relaxation in one call; never moves `versionHash` (issue #162)

---

Expand Down
Loading
Loading