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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"name": "visual-explainer",
"source": "./plugins/visual-explainer",
"description": "Generate beautiful HTML pages for diagrams, diff reviews, plan reviews, slides, and data tables",
"version": "0.7.0",
"version": "0.8.0",
"author": {
"name": "Clayton Kim"
},
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jobs:
node-version-file: '.nvmrc'
cache: npm
- run: npm ci
- run: npm test
- run: npm run ve:check-integrity
# Plan 004 (release hygiene) landed before this workflow was written,
# so all manifest version sources already agree — this job is
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ skills-lock.json
/mono-industrial-*-geist-pixel.png
/slides-*.png

# Design-system registry (repo-local fallback). Design systems are USER-OWNED,
# usually PRIVATE artifacts; the loader also resolves
# ~/.artifacture/design-systems, and ~/.artifacture may itself BE a clone of
# this repo — in that case this repo-local directory IS the user's global
# registry, so learned/private systems landing here must never be committed by
# accident. Only the README ships.
/design-systems/*
!/design-systems/README.md

# Publish hygiene (Artifacture)
.codex-briefs/
evals/model-matrix/out/
Expand Down
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# Changelog

## [0.8.0] - 2026-07-15

### External design-system registry
- Design systems are now user-owned artifacts resolved from a registry OUTSIDE the skill/repo: `$ARTIFACTURE_DESIGN_DIR` → `~/.artifacture/design-systems/` → `<repo>/design-systems/` (first hit wins; deduped when `~/.artifacture` is itself a repo clone).
- Format: one directory per system with `tokens.css` (`--ve-*` custom properties in a `:root` block, re-scoped to `[data-ve-preset="<slug>"]` at export) and `manifest.json` (description, source provenance, font loads/fallbacks, hard-rule notes).
- `ve:export` resolves non-built-in `data-ve-preset` names against the registry and inlines the tokens into the standalone HTML; unknown names warn and fall back to built-in `mono-industrial` tokens; malformed systems fail loudly. Built-ins can never be shadowed.
- Repo-local `design-systems/` is gitignored (only its README is tracked) so learned/private systems can't be committed by accident — the repo ships the mechanism, user registries hold the (typically private) brand tokens.

### ve:learn token learning
- New `npm run ve:learn -- <source> --name <slug> [--out <dir>]` drafts a design system from a code file (hex tokens, font stacks, size ramps, grid geometry, easing), a URL (`:root` custom props, `@font-face`, dominant colors from linked CSS), or an image (palette quantization via a Playwright canvas).
- Drafts land in the registry with a manifest `extraction` report: per-token mapping decisions, size ramp, and required-token coverage. Agent-assisted refinement flow documented in `docs/design-systems.md`.
- Deterministic heuristics are eval-driven: `evals/design-systems/` (second leg of `npm run ve:eval`) pins fixture sources to golden token sets per modality plus loader resolution-order/fallback/malformed-manifest cases.

### Security
- Token values and manifest font imports are validated before they can reach a shared artifact: values containing `<` or control characters are rejected loudly (style-element breakout), font imports must be plain http(s) URLs, and the CSS injector uses a function replacer (no `$&` splicing) plus a final `</style>`/`<!--`/`<script` refusal. A hostile-tokens eval pins this behavior.
- `ve:learn` sanitizes extracted values at ingestion (angle brackets/control characters stripped) and its URL modality refuses private/loopback hosts by default (`--allow-private` to override), caps responses at ~5MB, times out fetches at 15s, and checks content types.

### Tooling
- New `npm test` (node:test) covering the loader, extractors, and diagram layout; wired into CI alongside the expanded `ve:eval`.

## [0.7.0] - 2026-07-04

### Changed
Expand Down
21 changes: 20 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,29 @@ selected via `data-ve-preset="<name>"` on the root. Add a new
tokens the existing presets set (mono-industrial, nothing, blueprint,
editorial, paper-ink, terminal, custom) — don't introduce new token names.

## Adding or changing a design system

Design systems are user-owned — usually private brand material — and live
OUTSIDE the repo (see `docs/design-systems.md`): `$ARTIFACTURE_DESIGN_DIR` →
`~/.artifacture/design-systems/` → `<repo>/design-systems/`. The repo ships
NO systems; the repo-local directory is gitignored (only its README is
tracked). Never commit a real brand's tokens, and keep eval fixtures
synthetic.

If you change the `ve:learn` extraction heuristics or the registry loader
(`scripts/ve-mdx/design-systems.mjs`, `learn-extractors.mjs`,
`learn-sources.mjs`), the eval suite in `evals/design-systems/` is the spec:
fixture sources with golden expected tokens plus loader resolution-order
cases, run as the second leg of `npm run ve:eval`. Update or extend the
fixtures/goldens under `evals/fixtures/design-systems/` with the change —
never weaken a golden to make a heuristic pass without hand-verifying the new
values against the fixture source.

## Before you open a PR

- `npm test` must pass.
- `npm run ve:check` must pass.
- `npm run ve:eval` must pass.
- `npm run ve:eval` must pass (verifier evals + design-system evals).
- If you touched or generated an HTML artifact, run the verifier on it
(`node plugins/visual-explainer/scripts/verify/ve-verify.mjs <artifact.html>`)
and fix any error-severity failures.
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Video formats (9:16 reel, 16:9 long-form) render to MP4 through Hyperframes; sam
- **`/explain-diff`**: a literate diff mode (background → intuition → walkthrough → quiz), adapted from Geoffrey Litt's prompt pattern.
- **Model-matrix eval harness** (`evals/model-matrix/`): the same briefs across Kimi, GLM, DeepSeek, Claude, and Codex, scored on deterministic compliance plus a blind screenshot judge. Point it at your own model in about ten minutes.
- **One-command team sharing**: `share.sh` deploys to Vercel (zero setup, public) or sharehtml on Cloudflare (stable update-in-place URLs, team SSO via Cloudflare Access, comments). See `docs/TEAM-SHARING.md`.
- **External design systems + `ve:learn`**: brand token sets are user-owned artifacts resolved from a registry outside the skill (`$ARTIFACTURE_DESIGN_DIR` → `~/.artifacture/design-systems/` → repo `design-systems/`) and inlined into exports by preset name. `npm run ve:learn -- <code-file|url|image> --name <slug>` drafts a system from a token source, a live page, or an image palette; deterministic heuristics are pinned by their own eval suite (`evals/design-systems/`). The repo ships the mechanism only — systems (typically private brand tokens) live in your own registry. See `docs/design-systems.md`.

## Install

Expand Down Expand Up @@ -110,6 +111,7 @@ For private team sharing setup, see [`docs/TEAM-SHARING.md`](docs/TEAM-SHARING.m
## Docs

- [Features](docs/features.md): the full capability reference, including all output modes, aesthetics, and how generation works.
- [Design systems](docs/design-systems.md): the external design-system registry format, resolution order, `ve:learn` token learning, and the agent-assisted refinement flow.
- [Team sharing](docs/TEAM-SHARING.md): one-command deploys to Vercel or a team-gated Cloudflare space.
- [Skill docs](plugins/visual-explainer/SKILL.md): what an agent actually reads, plus the per-use-case [cards](plugins/visual-explainer/cards/).
- [Verifier](plugins/visual-explainer/scripts/verify/): the deterministic design-quality gate and its [eval suite](evals/).
Expand Down
31 changes: 31 additions & 0 deletions design-systems/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Design-system registry (repo-local fallback)

This directory is the LOWEST-priority location the design-system loader
searches. Design systems are user-owned — and usually private — artifacts
that live outside the repo:

1. `$ARTIFACTURE_DESIGN_DIR` (explicit override)
2. `~/.artifacture/design-systems/` (user-global registry — the recommended
home for your systems, especially brand tokens that must not be published)
3. `<repo>/design-systems/` (this directory — repo-local fallback)

First hit wins. If `~/.artifacture` is itself a clone of this repo, locations
2 and 3 are the same directory; the loader dedupes the search list, and this
directory's contents are gitignored (see the repo `.gitignore`) so
learned/private systems can never be committed by accident. This repo
intentionally ships NO systems here — the repo ships the mechanism, your
registry holds the brand.

Each system is a directory named by its slug:

```
design-systems/<slug>/
tokens.css --ve-* custom properties in a :root { ... } block
manifest.json name, description, source provenance, fonts, notes
```

For example, a private `acme-brand/tokens.css` would set `--ve-bg`,
`--ve-text`, `--ve-accent`, the three `--ve-font-*` stacks, and friends; its
`manifest.json` records where the tokens were learned from and the brand's
hard rules. Draft one from your own sources with `npm run ve:learn` and refine
it per `docs/design-systems.md`.
160 changes: 160 additions & 0 deletions docs/design-systems.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# External design systems

Artifacture's built-in presets (`mono-industrial`, `nothing`, `blueprint`,
`editorial`, `paper-ink`, `terminal`, `custom`) live in
`visual-explainer-mdx/global.css`. Everything else is a **design system**: a
user-owned artifact maintained OUTSIDE the skill and the repo, resolved from a
registry at export time. Your brand tokens survive skill upgrades because they
were never inside the skill to begin with.

## File format

One directory per system, named by its slug:

```
<registry>/<slug>/
tokens.css the system's --ve-* custom properties
manifest.json metadata, provenance, fonts, notes
```

### tokens.css

Declaration-only CSS: `--ve-*` custom properties inside a `:root { ... }`
block (bare declarations also work; any non-custom-property rules are
ignored). At export the loader re-scopes the declarations to
`[data-ve-preset="<slug>"]`, so plain `:root` keeps the file valid,
editor-friendly CSS while guaranteeing the tokens can never leak outside the
preset scope.

Token values are inlined into shared HTML artifacts, so they may not contain
`<` or control characters — the loader rejects such values loudly rather than
inlining them (no legitimate `--ve-*` value needs them).

Cover at least the core roles — bg / surfaces (`panel`, `panel-strong`,
`row`) / text (`heading`, `text`, `muted`, `faint`) / `accent` (+ `-soft`,
`-contrast`) / `rule` / status colors / the three font stacks / weights /
`radius` — plus the extended diagram, code, and poster groups. The canonical
key list is `REQUIRED_VE_TOKENS` in `scripts/ve-mdx/design-systems.mjs`; the
exporter warns about unset recommended tokens. Four diagram tokens
(`--ve-diagram-ink/muted/frame/accent-fill`) get derived fallbacks
automatically, mirroring the built-in `custom` preset.

### manifest.json

```json
{
"name": "<slug>",
"description": "One-paragraph identity of the system.",
"source": { "kind": "code|url|image", "location": "...", "tool": "..." },
"fonts": {
"imports": ["https://fonts.googleapis.com/css2?family=..."],
"stacks": { "display": "...", "body": "...", "mono": "..." },
"note": "licensing / fallback expectations"
},
"notes": ["Hard rules and usage guidance that travel with the system."]
}
```

- `source` is provenance: where the tokens came from and how.
- `fonts.imports` are inlined as `@import` lines ahead of the tokens. Each
entry must be a plain http(s) URL with no quotes, angle brackets,
backslashes, parentheses, or whitespace (anything else is rejected loudly).
Remote fonts are a self-containment trade-off — every stack must end in a
system fallback so artifacts degrade gracefully offline.
- `notes` carry the system's hard rules (e.g. "solid fills over grid paper",
"accent color reserved for CTAs") so an agent styling with the system can
honor them.

## Registry resolution order

1. `$ARTIFACTURE_DESIGN_DIR` — explicit override, wins outright.
2. `~/.artifacture/design-systems/` — the user-global registry (recommended
home for your systems).
3. `<repo>/design-systems/` — repo-local fallback, for clones that want
project-scoped systems. The repo itself ships NO systems here (design
systems are usually private brand material); only the directory README is
tracked.

First hit wins; lookup is by directory name.

**Collision note:** `~/.artifacture` may itself BE a clone of this repo. In
that case locations 2 and 3 are the *same* directory — the loader dedupes the
search list so it is consulted exactly once (at the higher-priority slot), and
the repo's `.gitignore` excludes `design-systems/*` so systems you learn into
your global registry can never be committed to the repo by accident.

A directory that exists but is malformed (missing `tokens.css` or
`manifest.json`, unparseable manifest) fails the export loudly rather than
silently falling through to a lower-priority registry — a half-formed system
shadowing another is a debugging trap.

## Using a system

Reference its slug anywhere a preset name goes:

```mdx
<ExplainerShell preset="acme-brand" title="..." summary="...">
```

`npm run ve:export` scans the source for preset references, resolves
non-built-in names against the registry, and inlines the tokens (scoped, with
derived fallbacks and font imports) into the standalone HTML as a
`<style data-ve-design-system>` block. Built-in names never consult the
registry, so a user system named `terminal` cannot shadow the built-in.
Unknown names warn and fall back to the default built-in tokens
(`mono-industrial`) so nothing ships unstyled.

The static/Hyperframes path (`ve:export-static`) renders compositions that
carry their own styles and does not consult the registry.

## Learning a system: `ve:learn`

```
npm run ve:learn -- <source> --name <slug> [--out <dir>] [--force] [--allow-private]
```

`<source>` decides the modality:

| Modality | Source | What gets extracted |
|----------|--------|---------------------|
| code | `.ts/.tsx/.js/.css/...` file | named hex colors, font stacks, weight/size ramps, grid geometry, easing |
| url | `http(s)://...` | `:root` custom properties, `@font-face`, font-family stacks, dominant colors from the page's linked CSS. Guardrails: private/loopback hosts are refused unless `--allow-private` is passed, responses are capped at ~5MB, fetches time out after 15s, and non-text content types are rejected. Extracted values are sanitized (angle brackets and control characters stripped) before they can become tokens. |
| image | `.png/.jpg/.webp/...` | quantized palette (canvas in Playwright's Chromium), mapped by coverage/contrast/saturation |

Output is a **draft** system in the registry (default:
`$ARTIFACTURE_DESIGN_DIR`, else `~/.artifacture/design-systems/`): a full
`tokens.css` plus a manifest whose `extraction` block records every mapping
decision, the size ramp, and required-token coverage.

The heuristics are deterministic, and the eval suite in
`evals/design-systems/` is their spec — fixture sources with golden expected
tokens, run as the second leg of `npm run ve:eval`. Change a heuristic, and
the evals tell you what it broke.

### Agent-assisted refinement flow

`ve:learn` gets you a faithful first draft; taste comes from a review pass.
The intended loop, for a human or an agent:

1. **Learn**: `npm run ve:learn -- <source> --name <slug>`.
2. **Read the extraction report** in `manifest.json` — every token names the
decision that produced it (`name hint "paper"`, `mix(text 5% over bg)`,
`generic status default`). Defaulted/generic decisions are the review
queue.
3. **Render a probe**: export an existing example with the new preset name
(e.g. copy `examples/visual-explainer-mdx/preset-gallery.mdx`, set
`preset="<slug>"`) and eyeball surfaces, muted-text contrast, diagram grid,
code panel.
4. **Refine tokens.css** directly — it is the artifact, not the extractor
output. Run the exported probe through the verifier
(`npm run ve:verify -- <out.html>`) to catch contrast regressions.
5. **Annotate the manifest**: real description, font `imports` +
licensing note, and the system's hard rules under `notes`. Drop the
`"status": "draft"` marker.

The synthetic `acme-terracotta` eval fixture
(`evals/fixtures/design-systems/code/acme-terracotta-tokens.ts` and its golden
under `expected/`) is the reference shape for step 1's input and output: a
brand token module in, a full `--ve-*` set out, with every mapping decision
recorded. Real systems produced by this flow are private by default — keep
them in your user-global registry, not in a repo clone.
Loading
Loading