@12ui/design
v0.2.132
Published
The canonical 12ui CLI, SDK, MCP server, and visual interface design skills
Maintainers
Readme
@12ui/design
@12ui/design is the canonical 12ui CLI, SDK, MCP server, and agent-skill
package. The 12ui binary stays stable while package imports and installation
use the new name.
Install with Node on macOS, Linux, or Windows:
npx -y @12ui/design skill installOr use the genuinely npm-free macOS/Linux installer:
curl -fsSL https://12ui.com/install.sh | shBoth paths pin the exact CLI version, install 12ui-design into every detected
Codex, Claude Code, Grok CLI, Cursor CLI, Antigravity, and GitHub Copilot CLI
installation without requiring an API account. Ordinary corpus search and downloads
also work without an account. Run 12ui auth login for authenticated hosted
generation or explicit hedge search. Explicit authentication setup creates a user-owned credential with
design.convert and design.corpus.retrieve, is verified before use, and is
stored mode-0600 at $XDG_CONFIG_HOME/12ui/credentials.json or
~/.config/12ui/credentials.json. When those private roots are unusable and
TWELVE_UI_CONFIG is not explicit, the CLI stores the credential mode-0600 at
$TMPDIR/12ui-<uid>/config/credentials.json, reports the exact path, and
labels it as lasting only for the managed environment lifetime. Later commands
in that environment rediscover the same file; no credential is written into a
workspace.
CLI only, for a shell that does not want the skills:
npx -y @12ui/design cli installcurl -fsSL https://12ui.com/install-cli.sh | shBoth pin just the 12ui binary - no skills, no browser sign-in - into the same
managed launcher and versioned bundle, verified and installed the same way.
12ui cli status reports what is pinned. Managed installations do not update
automatically; 12ui cli install pins the version currently running.
12ui update upgrades an installation: it runs
npx -y @12ui/design@latest skill install, which pins the newest release and
refreshes every skill and plugin skill install manages, then runs
claude plugin marketplace update 12ui-plugin and
claude plugin update 12ui-design@12ui-plugin in every Claude Code home that
has the plugin.
12ui skill install adds the skills later.
Every supported agent reads SKILL.md bundles from one user-scoped directory,
so the same bundle is installed byte-identically to each:
| --client | Agent | Installs into |
| ------------- | ------------------- | ----------------------------- |
| codex | Codex CLI | ~/.codex*/skills/ (CODEX_HOME) |
| claude-code | Claude Code | ~/.claude*/skills/ (CLAUDE_CONFIG_DIR) |
| grok | Grok CLI | ~/.grok/skills/ (GROK_HOME) |
| cursor | Cursor CLI | ~/.cursor/skills/ |
| antigravity | Google Antigravity (agy) | ~/.gemini/config/skills/ |
| copilot | GitHub Copilot CLI | ~/.copilot/skills/ (XDG_CONFIG_HOME) |
skill install installs into every agent it finds and prints one line per
agent; an agent that is not on the machine is reported as a skip, not an error.
An agent counts as present when its home directory exists, its executable is on
PATH, or its home environment variable is set. Copilot CLI discovered skills
from ~/.copilot/skills/ starting in 1.0; releases before that have no skill
mechanism at all.
Codex discovery includes every local ~/.codex* profile directory, plus
CODEX_HOME when it points outside that set. Claude Code discovery includes
~/.claude, every other ~/.claude* directory that holds .claude.json
(Claude Code's own file in each config dir; other tools' ~/.claude* folders
are skipped), and CLAUDE_CONFIG_DIR. A profile that is a symlink to a
directory counts like the directory. Pass --client codex --home <path> when an operation
should target only one profile.
Where the agent can carry the Design workspace as a plugin, skill install
installs the plugin, which carries the same skill, instead of the bare skill:
- Claude Code 2.1.287 or later gets
12ui-design@12ui-pluginfrom the just-every/12ui-plugin GitHub marketplace (the skill, the hosted12ui-workspaceserver and the Design workspace pane). The marketplace is set to auto-update, so Claude Code refreshes the plugin at startup. An older Claude Code gets the skill plus one line asking forclaude update. - Codex with
codex plugingets12ui@12ui-local: the skill plus the hosted server that draws the workspace inline. The CLI writes it to~/.local/share/12ui/codex-plugin/12ui(fromcodex-plugin/andskills/12ui-design) and adds that folder as the12ui-localmarketplace. The old directory-listed12ui-designskill is switched off by marked[[skills.config]]blocks.
A skill-only copy the CLI wrote earlier is removed once the plugin is in place,
a re-run changes nothing, and 12ui skill remove (or uninstall) removes the
plugin, its marketplace and those blocks as well. The shared Codex plugin
folder goes only once no Codex home on the machine still has the 12ui-local
marketplace. A plugin step that fails (or an agent command that runs past a
minute) leaves that home with the skill instead; the other homes and the exit
code are unaffected.
Skill installation does not require an API account or start account onboarding.
Explicit authentication options still prepare credentials; hosted commands handle
authentication when used. Existing unknown or locally modified skills also fail closed
unless --force is explicit.
One visual interface skill
12ui-design is the single design entrypoint. Every client receives the same
skills/12ui-design bytes. A new screen or page the user will choose among
starts the Design workspace from a brief: the hosted plugin's design.slate.create
where the workspace shows in the conversation (the Codex app), else
12ui workspace open --concept "<brief>" --wait in a browser page. The skill also
runs 12ui branch execute, 12ui convert and 12ui improve; the CLI owns local
and hosted engine selection.
The skill's scene.md guide rebuilds a design image as a live Three.js scene.
It runs 12ui convert --engine auto (Codex when it is ready, otherwise the 12ui
API) in the background for the static page while the agent models the scene with
the local 12ui scene tools (12ui scene --help): camera solve, placement, a
starter harness with a bundled three.js, 12ui scene merge to put the converted
page into the scene, capture with the managed Chromium, and review sheets. The
12ui scene tools need no account and make no network request.
improve --apply captures the URL and edits the actual repository. Without a target it drafts candidates, stops for
an explicit choice, then applies that choice locally. Without --apply, the existing hosted implementation-kit behavior
remains. Inspect the real application after editing.
Draft, branch, convert and improve
12ui draft, 12ui branch, 12ui convert and 12ui improve are the primary
commands. Each keeps its own saved-run format. convert --engine auto, the default, runs on local Codex when it is
ready and otherwise selects the 12ui API before dispatch; a saved run keeps its
recorded engine. Local conversion uses native Codex images; --image-backend external opts in
to the user's own OPENAI_API_KEY, which alone never selects it. Pass --engine
to choose the engine yourself.
Local HTML does not produce LayerDoc. Native design formats and hosted branch assembly remain dedicated conversion workflows. React export is whole-page JSX, not a functional application. Preserve existing run directories and their recorded engine for recovery; importing an output into a workflow workspace does not migrate an in-flight dispatch.
Corpus-conditioned image generation explores visual directions. The selected image then guides editable implementation, with browser inspection against the approved target and focused correction of observed mismatches.
12ui skill status --json
12ui corpus inspire \
--query "Editorial architecture homepage with warm materiality" \
--reference-image ./current-homepage.png \
--out-dir ./references \
--count 4
12ui draft \
--concept "Editorial architecture homepage with warm materiality" \
--reference ./current-homepage.png \
--retain style \
--candidates 4
12ui next <run-dir> --wait
12ui branch execute \
--start <run-dir>/candidates/A.png \
--concept "Editorial architecture studio site" \
--scope site \
--convert html \
--prototype
12ui next <branch-run-dir> --wait
12ui improve https://example.com/dashboard \
--direction "Editorial hierarchy, compact controls, quiet blue surfaces" \
--repo ./dashboard \
--out-dir ./dashboard-improve
12ui improve https://example.com/dashboard \
--target ./approved-dashboard.png \
--repo ./dashboard \
--out-dir ./dashboard-align
12ui convert <conversion-id> \
--output html_fixed,pdf,psd,pptx,sketch \
--out-dir ./exportsdraft accepts 2-16 candidates and defaults to four. --reference accepts a
local PNG/JPEG/WebP path or HTTP(S) URL, keeps the normal corpus retrieval, and
sends the original image to every hosted candidate. Its new-page default retains
style while excluding layout, content, and assets; --retain layout is the
explicit source-geometry opt-in. It records the corpus set,
hosted run id, stable service slots, output paths, and claim key in draft.json
before dispatch. Every dispatch request stays in the foreground and posts only
slots whose durable service state is still pending; in-flight, succeeded, and
failed slots are never posted again. 12ui next <run-dir> --wait repairs only
never-started slots, then polls and downloads. Successful PNGs are digest-checked and written atomically under
<run-dir>/candidates/, with terminal evidence in draft.complete.json.
12ui draft never inspects Codex authentication, OPENAI_API_KEY, project
.env files, or another caller-side image-provider credential. This describes
12ui draft, not the workflow ledger's draft recipe.
convert <conversion-id> downloads one successful base LayerDoc once and
dispatches the chosen formats concurrently. It creates the destination
directory as needed, but refuses before any API request when a selected output,
its asset directory, or export-manifest.json already exists.
capabilities --json reports the CLI's exact inputs and outputs, model and
responsive-quality choices, duration hints, status commands, and durable
journal/receipt contracts.
The local workflow ledger
The CLI also keeps a persistent local workflow ledger, a secondary surface shared by the local CLI and MCP for hosts that drive individual operations themselves:
12ui workflow import /absolute/path/reference.png --workspace ./design-work --role reference
12ui workflow prepare ./request.json --workspace ./design-work --key homepage-direction-a
12ui workflow execute <operation-id> --workspace ./design-work --providers session --session-capabilities text
12ui workflow register <operation-id> --workspace ./design-work --attempt <handoff-attempt-number> --result ./result.json
12ui workflow status <operation-id> --workspace ./design-workThe import source is absolute in this example. Relative imported file paths are
resolved inside the workspace: ./reference.png would mean
./design-work/reference.png. Request/result JSON command arguments are read
relative to the shell's current directory; file paths inside those results and
recipe image paths are workspace-relative unless absolute.
request.json names a capability, prompt, imported input artifacts and output
contract. A host session takes only text or code: the execute result is then a
handoff, and the session registers its real files or JSON value. Image, image-edit
and convert work never goes to a session; the 12ui CLI runs it through its own
providers. Preparation, imports and registration do not need a 12ui account.
12ui workflow --help documents the complete contract, provider policy and
interruption behavior.
Provider routing considers a host session that declared text or code first, then
eligible Codex and Claude subscription CLI capabilities, then the OpenAI API when
the user opts in, then the 12ui API on the 12ui sign-in. Image, image-edit and convert work never routes to
a session. Codex text/code subprocess execution requires
--trust-codex-config: explicit trust in the existing user's CLI configuration,
including tools and hooks beyond the command sandbox. This does not opt in to
the OpenAI API and does not apply to the current session's tools. Claude requires
supported safe mode, edits code with a fixed file-tool set without shell tests,
and currently cannot attach images to text requests. Run checks in the parent
session. Image capability is never inferred from a text CLI's presence.
Session capabilities (text or code) must be
explicitly declared from available tools. 12ui credit use needs no permission. The
OpenAI API spends on the user's own key and runs only with --allow-paid openai; key
presence alone never enables it. An ambiguous dispatched operation
requires reconciliation and cannot silently replay on another provider.
An accepted job with a failed download, or a completed response with malformed
output, stays in reconciliation under the same operation and attempt. Recover
and register the original output and dependencies; do not regenerate it.
Refusals and permission denials remain terminal stops.
OpenAI requires an explicit model through request parameters.model,
OPENAI_MODEL for text/generated HTML, or OPENAI_IMAGE_MODEL for images.
OPENAI_API_KEY and TWELVE_UI_API_KEY make separately funded routes discoverable;
they never belong in request or result JSON. Native execution removes provider-key
overrides from the child environment and uses the user's normal CLI configuration.
The recipes draft, branch, edit, redesign, labels, review, convert and
implement build requests for this shared ledger. They do not imply that every
hosted pipeline or native design export has a local implementation.
After registering a recipe plan, 12ui workflow continue <operation-id> --workspace <directory>
prepares eligible child operations without inference; execute each
returned ready ID explicitly, then continue the same plan to unlock dependencies.
workflow fail records actual refusal, no-output or ambiguous-outcome evidence;
it does not turn an uncertain dispatch into permission for another call.
Both registration and failure must name the handoff's attempt number so delayed
results cannot settle a later attempt.
For project implementation, the service captures a baseline before execution.
Register the normal report and any real additional receipts; registration derives
verified changed-file artifacts and project evidence from the actual selected
project. A provider receipt or claimed no-op cannot complete implementation:
no changed files produces project_no_changes. File changes and caller-reported
browser/test evidence remain separate facts.
The ledger records returned artifacts; independent browser/image inspection
still establishes fidelity, ownership and behavior. Local execution consumes
user model allowance or authorized API budget and local hardware, rather than
making model execution free.
workflow capture <http-or-https-url> --workspace <directory> uses local Chromium
and a fresh unauthenticated browser to retain screenshot, DOM and redirect evidence.
Capture an authenticated screen with the session browser and import it instead.
workflow review <spec.json> --workspace <directory> --key <key> prepares an
image-grounded review; it does not validate runtime behavior or trigger repairs.
workflow export <operation-id> --workspace <directory> --destination <new-directory>
materializes completed outputs without inference or overwriting differing files.
Register HTML plus all its assets with logical relativePath mappings, or use the
SDK's collectBundleFiles on an isolated generated-output directory, so references
survive custody and export. This does not package an existing project from receipts.
12ui workflow layerdoc <operation-id> --workspace <directory> reads a completed
native conversion without a hosted request. The SDK exposes
loadWorkflowLayerDoc({workspace, operationId}) and
loadLayerDocBundle({directory}) for a relocated export. Both return canonical
document, absolute document/manifest paths and an assets map from artifact ID
to verified local path, relative path, SHA-256 and media type; raster dimensions
are checked against the document.
Native registration requires document.layerdoc.json, layerdoc-assets.json
and mapped raster files. The canonical document has role layerdoc; the sidecar
has role layerdoc-assets and schema 12ui.layerdoc-assets.v1, with
assets: {artifactId: {path, sha256, mediaType}}. Every file needs its portable
relativePath; referenced rasters use role asset. This local reader does not
provide an offline editor. Standard hosted 12ui convert <layerdoc.json> does
not read the sidecar: it requires hydrated HTTP asset references. Uploading and
hydrating local assets remains a separate API integration.
Design Corpus inspiration
TwelveUiCorpusClient supports public status, inspiration and logo searches,
reference metadata, and image downloads without a 12ui account or credits.
Retrieval is free for authenticated callers too; abuse and service-budget limits
still apply. Supplied credentials retain the design.corpus.retrieve scope and
are never silently discarded after an authentication failure.
import { TwelveUiCorpusClient } from '@12ui/design';
const corpus = new TwelveUiCorpusClient({
origin: process.env.TWELVE_UI_ORIGIN ?? 'https://12ui.com',
});
const status = await corpus.status();
const inspiration = await corpus.inspire({
q: 'Editorial architecture home page with warm materiality and asymmetric typography',
mode: 'balanced',
k: 4,
}, {
idempotencyKey: 'example-home-v1',
});
const first = inspiration.items[0]!;
const metadata = await corpus.reference(first.id);
const image = await corpus.download(first.id, 'ref-hd');Every inspiration request requires a stable idempotency key. See
docs/corpus-v1.openapi.yaml.
download accepts thumb, thumb-hd, ref, and ref-hd; the original
thumb/ref sizes remain supported.
For agent workflows that need four reference images,
the CLI calls /api/v1/corpus/inspire, downloads the full reference images in
parallel, verifies each WebP, and writes a content-hashed manifest:
node "$TWELVE_UI_REPO/packages/12ui/dist/cli.js" corpus inspire \
--query "Editorial architecture home page with warm materiality and asymmetric type" \
--count 4 \
--idempotency-key design-run:home-v1 \
--out-dir ./referencesbalanced is the standalone CLI, SDK and MCP default, with or without
credentials. direct stays close to the literal query, balanced weighs relevance,
quality, and variety evenly, and adventurer follows the query's most
distinctive directions toward less expected references. The modes are locked
server-side; callers cannot tune their internal ranking weights. Anonymous
requests accept up to 24 references and an 8 KB query. Explicit hedge requires
an account for hosted planning; it remains free to the customer but incurs
service model costs. For account-free planning, develop directions locally and
retrieve them through balanced, direct or adventurer.
For a redesign, add --reference-image <png|jpeg|webp>. The CLI hashes the
private source into stable idempotency and local evidence, while Design Corpus
uses one aggregated image-and-text embedding to retrieve visually relevant
directions. The source bytes are forwarded ephemerally and are not persisted
by 12ui.
The output directory must not already exist. Before dispatch, the CLI writes
inspire-attempt.json with the exact request and stable idempotency key. A
successful run also contains manifest.json, inspire-evidence.json with the
exact upstream response, four numbered reference files, and a durable digest
receipt for each image under .reference-receipts/. If the HTTP result is
ambiguous or one or more image downloads fail, finish the same allocation
while preserving its retrieval identity:
node "$TWELVE_UI_REPO/packages/12ui/dist/cli.js" corpus resume \
--out-dir ./references \
--origin https://12ui.comcorpus resume reuses the attempt's idempotency key. The Worker either returns
the original stored response or reports the still-pending request; it never
dispatches a second embedding for that identity. Reusing a key with a different
validated request returns 409 idempotency_conflict. A reference already on
disk is accepted only when its bytes match its exclusive digest receipt.
The three account-free modes are direct, balanced and adventurer; hedge is an
explicit account-only mode. Public requests do not accept random seeds, browse
grouping IDs, free-form ranking weights or corpus filters. HTTP 429 responses
include Retry-After guidance: wait, then resume the same attempt instead of
starting sign-in or buying credits. 12ui corpus status reads the release
contract without starting retrieval.
Multi-page packages
TwelveUiConversionPackageClient accepts the version-1 package manifest plus
explicit screenshot, LayerDoc, and raster-asset parts. It polls one durable
parent operation while the service converts missing viewports and exports final
pages in parallel.
import { TwelveUiConversionPackageClient } from '@12ui/design';
const packages = new TwelveUiConversionPackageClient({
origin: process.env.TWELVE_UI_ORIGIN ?? 'https://12ui.com',
apiKey: process.env.TWELVE_UI_API_KEY!,
});
const created = await packages.createPackage({
manifest: {
version: 1,
output: 'layerdoc',
pages: [{
id: 'home',
ordinal: 0,
viewports: [{ id: 'hero', ordinal: 0, screenshot_part: 'home-hero' }],
}],
},
screenshots: [{
partId: 'home-hero',
bytes: screenshotBytes,
mediaType: 'image/png',
}],
});
const completed = created.status === 'succeeded'
? created
: await packages.waitForPackage(created.id);
const home = await packages.downloadPage(completed, 'home');See docs/CONVERSION_PACKAGES.md for the
strict manifest, asset mapping, normalization, page-join, and limit contract.
CLI
export TWELVE_UI_API_KEY=your-key
12ui improve https://example.com --direction "Warm editorial precision" --out-dir ./improve-kit
12ui improve https://example.com --target ./approved.png --repo ./app --out-dir ./alignment-kit
12ui convert ./screen.png --output layerdoc --out-dir ./converted
12ui convert ./screen.png --output html --model pro --width 1440 --out-dir ./responsive --timeout-ms 7200000
12ui convert ./screen.layerdoc.json --output html_fixed --out-dir ./fixed --idempotency-key screen-html-v1
12ui convert ./screen.layerdoc.json --output pptx --out-dir ./slides --idempotency-key screen-pptx-v1
12ui convert <layerdoc-conversion-id> --output html_fixed,pdf,psd,pptx,sketch --out-dir ./exports
12ui convert package --manifest ./package.json --out-dir ./pages
12ui resume conversion-idThe hosted 12ui improve CLI composes local Playwright capture, the existing draft and convert
commands, and a local selector-anchored planning stage. It accepts a URL or
source screenshot, records each stage in improve.json, and replays settled
work. --target skips generation and aligns a supplied design image or
LayerDoc to the captured URL; --dry-run prints catalog price ceilings without
network access. The kit ends at responsive target artifacts and an annotated
implementation plan; it never applies code.
convert package submits the ordered viewports of one continuous page as ONE
package conversion per page, which is what preserves page order and stitching;
converting a continuous page one image at a time loses both. branch execute
uses that shape only for declared web_page documents; complete app states are
separate one-viewport pages. Every viewport of a
page must share one width, checked locally before anything is bought. A viewport
may name sourceConversionId to reuse an already succeeded conversion of
exactly those bytes instead of paying for it twice.
Draft dispatch and every branch network request stay in the foreground. Draft
returns after its slots are durably dispatched; 12ui next <run-dir> --wait
repairs recorded never-started slots, polls, and downloads. Branch execute and
resume remain foreground until terminal. A second shell can run 12ui next
<run-dir> for per-item state without dispatching replacement work. Do not infer
progress from a directory listing.
An HTML branch can become a clickable prototype in either of two equivalent
ways: pass --prototype to branch execute, or run 12ui prototype <run-dir>
afterward. The post-export stage splits application states, canonicalizes the
shared shell, derives navigation from branch.plan.json, preserves real
content, and creates holding pages when the plan names a route that was not
exported. Standalone 12ui prototype <run-dir> defaults to --labels none, using
deterministic links, forms and plan edges without a 12ui account. Explicitly
authorized --labels 12ui adds one hosted interaction-label call per exported
screen using the strict navigate | dropdown | modal | none schema.
12ui prototype <run-dir> --labels workflow --workspace <directory> inventories
the local HTML and prepares stable per-screen label operations, returning
awaiting_labels without inference or hosted authentication. Execute the returned
prepared IDs through workflow execute, register real outputs with the exact
attempt number, then repeat the same prototype command. Dispatched/reconciling
IDs need recovery, not execution. Completed results are checked against the exact
DOM candidates, known destinations and source HTML/plan/completion before build.
Generic image workflow labels annotations cannot be substituted. Duplicate IDs,
unknown destinations and contradictory action fields are rejected before immutable
workflow success. Native local pages permit only navigation to real pages or none.
12ui branch resume refuses to replace this pause with hosted labeling.
The manifest preserves source custody, operation IDs and actual provider/funding/
usage facts. labels.costMicros counts new hosted prototype-label calls only;
workflow model execution still consumes the chosen plan/API budget. Unchanged
source/candidate content reuses operation IDs, including after branch/workspace
relocation. Linked remote assets are not part of this source snapshot.
The output replaces
<run-dir>/branch/prototype/ only after exported HTML, a complete or explicitly
accepted partial plan, semantic viewport ids, and the hard runtime gates pass.
Load/console failures, duplicate ids, broken targets or interactions, malformed
holding composition, inconsistent canonical shells, incorrect linked active
navigation, and broken deep-link history still refuse publication. Incomplete
dropdowns are never given invented options: they become static controls, while
dropdown, zero-action-page, and majority-holding defects publish as explicit
degradations in prototype.status.json, the run journal, and
proto-manifest.json. Any holding page, skipped shell transplant, shell size
residual, or untested script direction also remains a visible warning and
measured manifest fact.
Prototype polish is opt-in for its first release: use --prototype --polish
on branch execution or 12ui prototype <run-dir> --polish. The manifest,
polish report, and run journal retain per-rule fired, silent, and reverted
counts, and runtime verification reads the polished page copies.
Playwright's Chromium registry lives at cache/ms-playwright inside the same
XDG, HOME, or session data root selected for the managed CLI. If Chromium is
absent, prototype stops before labeling and prints the exact version-matched
install command; it never starts a browser download on its own.
Each conversion also writes a verified <out>.assets/manifest.json plus every
signed raster artifact beside the requested result. Set TWELVE_UI_ORIGIN to
override https://12ui.com; waits default to the operation expiry and can be
bounded explicitly with --timeout-ms.
convert blocks and records the original filenames before admission. If its
shell is interrupted after a conversion ID is returned, bare resume restores
every artifact in that original purchase. --out-dir keeps the recorded
basenames but moves recovery into another directory. For an older or externally
created operation without a local recovery record, the power form names its
exact output and destination:
12ui resume conversion-id --output layerdoc --out ./screen.layerdoc.jsonIf admission fails before emitting an operation ID, repeat the exact request
with the same explicit --idempotency-key so the service can replay the
original operation without repeating settled paid work. When the key is
omitted, the client generates a new UUID.
Image conversions accept --model fast|standard|pro; omission selects
standard. Beginning with layerdoc-worker-v148 and still in the current
layerdoc-worker-v194, Fast, Standard, and Pro all use
gpt-5.6-terra-low for primary extraction. Fast retains color-guided
deterministic inpainting, source-grounded flat-border cutouts, and 1x source crops without
secondary metadata refinement, generative cleanup, provider background
removal, or upscaling. Standard has no secondary metadata refinement and uses
one target-scoped atomic GPT Image 2.5 Flare result for each opaque clean target, with Pixelcut-first
alpha isolation and Bria admitted only after a durable hard-invalid or
reconciled terminal Pixelcut failure. Standard performs no Luna candidate
review. Pro retains a separate gpt-5.6-terra-low metadata-refinement call, a
target-scoped atomic clean plus the shared whole-image clean for atomic-region
targets, one comparative Luna review, and the complete revision-frozen FAL
alpha portfolio. The clean-stage request requires opaque output.
Provider-routed cutouts keep enhanced upscaling; accepted deterministic Fast
cutouts are resized locally. The option is not accepted for LayerDoc input
because downstream compilation is deterministic.
Use --width <px> to set the native output canvas width. The CLI resizes image
input proportionally before upload, upscaling or downscaling as needed. The
selected model may analyze a smaller internal raster without changing that
authored width; do not switch to Pro merely because the native canvas exceeds
Standard's analysis edge. For example, an 800x600 source with --width 1000
becomes a 1000x750 conversion canvas, and a 1536x1024 Standard source remains a
1536x1024 native canvas while analysis is bounded to 1500x1000. --width 390
turns a 1170 px-wide 3x phone capture into a 390 px conversion canvas. The
option is not accepted for LayerDoc input or resume.
LayerDoc input accepts the public v2 JSON downloaded from the API. Its signed
raster references must still be valid and must belong to the same authenticated
account. The service resolves those owned artifacts directly; it does not fetch
arbitrary remote URLs. LayerDoc input produces layerdoc, html,
html_fixed, svg, png, jpg (JPEG), webp, pdf, psd, pptx,
or sketch. The typed Node client and MCP integration documented below retain
the exact-profile web_project and app_project integration surface.
Review stored work
12ui review ./my-run
12ui review --allreview starts a read-only-by-default server on an ephemeral localhost port,
prints its URL, and opens it in the browser. The page places draft candidates
side by side, shows branch screens, exported page HTML, and a branch prototype,
and renders conversion outputs. Pass one run or workspace directory, or use --all to find automatic
draft workspaces in the current system temp directory and runs under the current
project's .12ui directory. The server never binds to a network interface.
This discovery follows the CLI's existing storage contracts: automatic drafts
live under the system temp root as 12ui-* directories with draft.json and
candidates/ (src/draft-workspace.ts); branch artifacts live under
<run-dir>/branch/{screens,pages,prototype} (src/branch-run-record.ts); workspace
commands append artifacts to <workspace>/journal.jsonl (src/run-journal.ts),
and conversions write outputs beside their input unless --out-dir selects a
workspace (src/cli-convert-command.ts). An explicit directory is searched for
those markers so a parent containing several stored runs can be reviewed at
once.
Notes and marks are the only automatic writes. Each run stores them atomically
in <run-dir>/review.json, mode 0600, and reloads them whenever the page is
opened. pick and approve are deliberately separate marks; Clear records no
mark. Agents can consume the file directly:
{
"version": 1,
"updatedAt": "2026-08-21T01:23:45.000Z",
"items": {
"draft-candidate-a-0123abcd": {
"feedback": {
"note": "Keep the hierarchy and reduce the footer.",
"mark": "pick",
"updatedAt": "2026-08-21T01:23:45.000Z"
},
"edits": [
{
"id": "edit-0123abcd",
"prompt": "Change the heading to Northstar notes.",
"createdAt": "2026-08-21T01:25:00.000Z",
"editOperationId": "edit-operation-id",
"renderOperationId": "render-operation-id",
"original": "screen.layerdoc.json",
"layerdoc": "review/edits/edit-0123abcd.layerdoc.json",
"html": "review/edits/edit-0123abcd.html"
}
]
}
}
}Item keys are stable hashes of their kind and run-relative path. mark is
"pick", "approve", or null; edits is omitted until an edit succeeds.
When an item is a LayerDoc and conversion credentials are configured, the page
offers an explicit text edit. It shows the price first and asks for confirmation
before calling the existing POST /api/v1/edit contract. A Design edit is a
metered action: it uses the weekly allowance when available or costs $0.03;
requested image actions may cost another $0.02-$0.30. The review server never
applies an edit by itself. A successful edit and its deterministic fixed HTML
render are written under <run-dir>/review/edits/ and displayed beside the
original.
Every completed HTML branch has an explicit Prototype action; opening review
never starts it. Holding pages expose Fill this gap only when credentials are
configured. Its button states $0.48+ before confirmation: $0.03 for one
Standard preserve-structure image claim, $0.45 for Standard HTML conversion, plus the
cheap label calls made when the prototype is rebuilt. The new page and plan
live in a private prototype source overlay, so original branch evidence is not
rewritten, and a durable receipt records the claim, conversion, and artifacts.
The run journal
Every command that owns a workspace directory — a draft run directory, a branch
out-dir, or a convert output directory — appends what it did to
<workspace>/journal.jsonl. One JSON line when the command begins and one when
it exits, including refusals and failures. Draft and branch network work stays
in that foreground process, so a run's whole history remains in one place.
Each line carries the canonical command name, a digest of the argv (hashed with
credential values redacted, never written down), the run and idempotency
identities, whether the command can spend money, the outcome and a normalized
error class, the artifacts it produced, the ids it bought, whether the run is
terminal, whether the work detached, and the process id. 12ui capabilities
reports the field list; the file is append-only and never rewritten, so a reader
tolerates a torn final line while a writer is mid-append.
The journal exists because one command can span several service stages, and a second shell needs durable evidence rather than inference from a directory.
LayerDoc to downstream formats
The raw LayerDoc representation can be submitted directly to a second conversion. This makes it possible to edit the document's text, geometry, or style before compiling responsive HTML or a fixed export while retaining its signed asset refs:
import fs from 'node:fs';
import { TwelveUiConversionClient } from '@12ui/design';
const client = new TwelveUiConversionClient({
origin: process.env.TWELVE_UI_ORIGIN ?? 'https://12ui.com',
apiKey: process.env.TWELVE_UI_API_KEY!,
});
const created = await client.createConversion({
layerdoc: new Uint8Array(await fs.promises.readFile('./screen.layerdoc.json')),
output: 'html',
});
const completed = created.status === 'succeeded'
? created
: await client.waitForConversion(created.id, { expiresAt: created.expires_at });
const html = await client.downloadResult(completed.id, 'html');
await fs.promises.writeFile('./screen.html', html.bytes);For generated responsive layouts, derive from the successful image-to-LayerDoc operation instead of uploading the image again:
const responsive = await client.createConversion({
sourceConversionId: imageToLayerDoc.id,
output: 'html',
responsiveQuality: 'pro', // standard | pro
});This authenticated path reuses the canonical LayerDoc and raster assets from
imageToLayerDoc, consumes export quota, and starts directly at responsive
reference generation. The source conversion must still be retained and must
have been produced by the current pipeline revision.
While the operation is queued or running, poll it normally and read
responsive_preview. Its frames are returned in mobile, tablet, then compact
desktop plan order. Each frame moves through waiting, generating,
generated, extracting, binding, and complete; its signed artifact
appears immediately after the real generated reference PNG is durably
registered. completed and total count reference, extraction, and binding as
three durable steps per admitted anchor, while progress.percent remains the
overall conversion progress for a single progress bar.
Project output uses the same client method and returns a deterministic ZIP:
const createdProject = await client.createConversion({
layerdoc: new Uint8Array(await fs.promises.readFile('./screen.layerdoc.json')),
output: 'web_project',
profile: {
version: 1,
framework: 'react',
styling: 'tailwind',
language: 'typescript',
packaging: 'project',
},
});
const projectOperation = createdProject.status === 'succeeded'
? createdProject
: await client.waitForConversion(createdProject.id, {
expiresAt: createdProject.expires_at,
});
const project = await client.downloadResult(projectOperation.id, 'web_project');
await fs.promises.writeFile('./screen-react.zip', project.bytes);The complete version-1 capability matrix is documented in
docs/EXPORT_FORMATS.md. Unsupported framework,
styling, language, or packaging combinations fail before upload; the client
does not substitute a nearby target.
The 10 web framework slugs are html, react, next, vue, nuxt, svelte,
sveltekit, angular, astro, and lit. The 9 app framework slugs are expo,
react_native, flutter, ionic_capacitor, swiftui, uikit,
jetpack_compose, android_views, and compose_multiplatform. Import
WEB_PROJECT_CAPABILITIES and APP_PROJECT_CAPABILITIES from this package to
enumerate the exact styling, language, and packaging cross-products at runtime.
For web output, component is reusable component source, page is a route/page
entrypoint, and project is a runnable scaffold. For app output, screen is
integration-ready screen source for an existing app, while project adds the
runnable scaffold and build configuration.
Hosted /convert two-phase workflow
The Node client above remains API-key authenticated and may request any valid image output directly. The free hosted web page uses the same HTTP resources with an intentionally narrower contract:
- Create one image conversion with
output: "layerdoc". - Poll the operation and replace the interactive preview only when
operation.preview.revisionincreases. - Fetch the final LayerDoc representation after the source succeeds.
- Keep that hydrated JSON in memory and create independent LayerDoc-input jobs
for
html,svg,png,jpg,webp,pdf,psd,pptx,sketch,web_project, andapp_project. Different formats and project profiles can run in parallel; a repeated completed format should download the cached representation rather than create another run.
No special export endpoint is involved. The source conversion remains the web
session root, and each export is an ordinary POST /api/v1/convert followed by
polling GET /api/v1/convert/:id. LayerDoc input is accepted only after its
source run has succeeded because the Worker verifies its signed artifacts,
expiry, and ownership before importing them.
Anonymous hosted-page requests send X-12UI-Public-Preview: 1 and the same
browser-generated X-12UI-Public-Visitor value. Public image creation also
includes cf-turnstile-response in multipart form data; its verified action is
12ui_convert_image. Public quotas are 5 Fast, 2 Standard, no Pro, and 20
exports per salted IP hash per UTC week (Monday 00:00 UTC to the next Monday).
Signed-in and API accounts receive 50 Fast, 20 Standard, 2 Pro, and 200 exports
per stable account id and UTC week.
GET /api/v1/convert/quota returns all buckets; successful POST responses
identify the admitted bucket and return its limit and remaining count. Weekly
exhaustion returns 429 with Retry-After and X-RateLimit-Reset at the next
Monday 00:00 UTC.
All runs are sponsored and do not check or debit Login credits. Provider, model, request-id, reservation, actual-cost, principal-tier, and account-id records are retained in durable ledgers independent of expiring conversion artifacts. Public headers are for the trusted anonymous preview; CLI and MCP consumers should keep using a Login-issued API key.
Running LayerDoc operations may include an immutable preview: structure
contains the final layer tree and prepared source, while standard adds 1×
crops and cleaned plates. result remains reserved for the final enhanced,
manifest-last publication. progress.active reports durable completed/total
counts for each materialization lane.
Progressive Node consumer
waitForConversion intentionally returns only the terminal operation. A
consumer that wants live progress or previews should poll getConversion and
apply every operation snapshot:
import fs from 'node:fs';
import { TwelveUiConversionClient } from '@12ui/design';
declare function renderLayerDoc(document: unknown): void;
const client = new TwelveUiConversionClient({
origin: process.env.TWELVE_UI_ORIGIN ?? 'https://12ui.com',
apiKey: process.env.TWELVE_UI_API_KEY!,
});
const created = await client.createConversion({
bytes: new Uint8Array(await fs.promises.readFile('./screen.png')),
mediaType: 'image/png',
output: 'layerdoc',
model: 'standard',
});
// The upload carries the image's pixels and colour profile only, named
// `source.png`: no filename, camera, author or location leaves the machine.
let operation = created;
let displayedRevision = 0;
while (operation.status === 'queued' || operation.status === 'running') {
const progress = operation.progress;
if (progress) {
console.log(progress.percent, progress.stage, progress.milestone);
for (const lane of progress.active ?? []) {
console.log(lane.kind, lane.completed, lane.total, lane.status);
}
}
if (operation.preview && operation.preview.revision > displayedRevision) {
displayedRevision = operation.preview.revision;
renderLayerDoc(operation.preview.document);
// operation.preview.artifacts contain signed hrefs that are usable now.
}
await new Promise((resolve) => setTimeout(resolve, 1_000));
operation = await client.getConversion(operation.id);
}
if (operation.status !== 'succeeded' || operation.result?.kind !== 'layerdoc') {
throw new Error(operation.error?.detail ?? `Conversion ${operation.status}`);
}
// The terminal result is authoritative and replaces any displayed preview.
renderLayerDoc(operation.result.document);Raw HTTP consumers should use Accept: application/json while polling and
respect the response's Retry-After header (currently one second). Prefer:
wait=20 only allows the create request to wait for an early result; it is not
a streaming connection. There is no SSE or WebSocket contract.
Progress and preview rules:
progress.percentis the weighted server calculation; do not derive a percentage from the stage name.progress.activeis optional and image-specific. Do not assume every lane is present or that lane order is fixed.milestoneisstructure_readyorstandard_readyonce that preview is available.- Previews currently apply to
output=layerdoc. Downstream HTML and fixed export operations still expose progress, but publish only their terminal result. - Preview revision 1 is
quality: "structure"; revision 2 isquality: "standard". Treat revisions as immutable snapshots. - A preview artifact id is never repointed to different bytes. Signed
hrefvalues expire with the operation; re-read the operation to refresh URLs. - On
succeeded, useresultas authoritative. Onfailedorexpired, useerrorand discard any displayed preview as a non-terminal aid.
MCP
12ui mcpThe server exposes the same durable operations used by the CLI and SDK:
design.workflow.prepare,execute,register,continue,fail,status,import,capabilities,capture,exportandlayerdocuse the persistent local service. Preparation accepts either a generic request or a recipe/input pair. Local operations do not trigger 12ui onboarding; paid execution needs explicit policy. Session registration and failure include the returnedattemptNumber.design.convertcreates a conversion, waits for it, and materializes the result and verified artifact manifest. PassimagePathfor PNG/JPEG/WebP orlayerdocPathfor public LayerDoc v2 JSON. Pass the exactprofileforweb_projectorapp_project.design.convert.statusreads an existing conversion's durable progress, previews, terminal result, or error without creating or retrying work.design.corpus.statusreads corpus release/idempotency capability without authentication or running retrieval.design.corpus.inspireruns free corpus retrieval and materializes its attempt evidence, manifest, and images in a newoutDir. Balanced is the default; anonymous requests accept up to 24 references. Explicit hedge requires an account.design.corpus.resumecontinues an incomplete attempt with its recorded key. These tools never trigger automatic onboarding.design.draft.statusanddesign.branch.statusread durable workflow state. Branch status attempts a live refresh by default, then falls back to the local snapshot if the service is unavailable.
The server also starts without 12ui credentials: the local workflow tools, the corpus tools and the design workspace tools below that only read and record work need none, and the hosted tools ask to sign in when called.
The Design workspace (ui://12ui/slate.html) grows from one finished draft run into rounds of
options, edits with lineage, inspiration, and a selection of any version. Nothing in it costs
money and nothing in it shows a price: it draws with your own Codex on this Mac (your ChatGPT
plan, never an API key) and searches the free reference corpus. Twelve tools:
design.slate.showrenders the workspace in hosts that render an interactive card, such as Codex desktop, and returns the options, versions, file paths and the continuation command as text elsewhere.design.slate.datareturns the same facts without a card, so an agent can confirm a selection or a hand-off (pick.versionId,pick.sha256, and the file atpick.imagePath, a durable copy that survives the OS sweeping the temp folder;pick.imagePathis null, withpick.fileStatemissingorchangedandpick.intactfalse, when that copy is gone or no longer matches, and no file that is not there is ever named);design.slate.openis the sidebar entry for the most recent active workspace. These three are model-visible and never start work.showandopenare titled "Design workspace".- App-only tools, which the host hides from the model:
design.slate.imageanddesign.slate.reference(pictures for the card, the second from the free corpus and cached in the workspace),design.slate.inspire(a free balanced-mode reference search, at most 24 results, no account; an identical search is answered from the record) anddesign.slate.inspiration(the selected references, at most eight, that condition the next Design round),design.slate.pick(Select, with an optional reason, and a durable copy of the image),design.slate.handoff(records a Branch or Convert request) anddesign.slate.status(a free read of the workspace, its runners' liveness, and whether this Mac can draw images). design.slate.runanddesign.slate.resumeare the only tools that start work, and only from a click in the screen. Design (new options, or more like the shown version) and Edit (an instruction plus an optional scribble, sent as the version and an annotated copy of it) write an approval record outside the run directory, spawn a detached12ui slate-runnerprocess, and return at once. The runner owns the work to a terminal state, so it does not depend on how long the host keeps this server alive: it runscodex exec's native image call, one call per option, in your own Codex home (yourCODEX_HOME, else Codex's default~/.codex), so each draw's session lands beside your others. It ignores your Codex config exceptservice_tier, so a Fast lane you chose also speeds draws. The model that runs the call only wraps the image tool: it isgpt-6-lunaat low effort when your Codex model cache lists it with low reasoning (about 8-10 s faster per option), elsegpt-6-astraat medium;TWELVE_UI_CODEX_MODELnames another. The reference images reach it as short copies under/tmpthat are removed when the call ends. Every call has a stable id (slate-<opId>-<slot>), so Resume replays the same work and never draws a finished option twice. When Codex is not ready on this Mac (no CLI, no ChatGPT login, no image model) the dialogs say why and stay disabled; the workspace never buys an alternative. SetTWELVE_UI_CODEX_BINto choose the Codex executable.
A hand-off records the request durably (Branch: your words, verbatim; Convert: the format and its
options) with a verified copy of the image, and sends the agent a short message. The agent reads
the record with design.slate.data and runs the filled-in 12ui command it names (12ui branch
execute for a Branch, 12ui convert for a Convert). 12ui slate-runner is an internal command and is not
a public surface.
Without the MCP tools: the workspace as a web page. 12ui workspace open --concept "<brief>"
starts a workspace from a brief (or 12ui workspace open [<run-dir>] [--latest] opens a finished
draft run's), the route wherever the workspace does not show in the conversation ([--wait] [--port <n>] [--no-open]
apply to both). It serves the same
workspace (the same screen, built with target browser) on 127.0.0.1 only, behind a random
token in the address it prints as one JSON line ({"workspace":"ready","url":...}), and opens the
system browser unless --no-open (BROWSER names another program). A small host shim in the page stands in for the MCP Apps host:
the screen's tool calls go to the same handlers 12ui mcp uses, confined to that one run, and
Design and Edit run on this Mac through the same detached runner and local Codex, with no agent
needed to generate. With --wait the command blocks until the user Selects, or sends a Branch or
Convert request, then prints one JSON line (decision, runDir, pick, handoff, messages,
text and view, the facts design.slate.data returns) and exits 0; without it, it serves until
interrupted and prints one such line per decision. The 12ui-design skill starts a workspace this way
wherever the hosted screen does not show. Under codex exec's sandbox, read-only and workspace-write
refuse the server's port (listen EPERM: operation not permitted 127.0.0.1); workspace-write with
network access (-c sandbox_workspace_write.network_access=true) and the 12ui state folder added
(--add-dir ~/.local/share/12ui, which the workspace writes to) runs everything, Design and Edit
included; full access needs none of it.
Among these tools, only design.convert creates metered work; corpus retrieval, including the
workspace's own search, is free and subject to abuse limits.
design.workflow.execute can use a funded provider only with its explicit authorization policy.
Set an idempotencyKey for any ambiguous conversion
create that may need to be repeated; otherwise the conversion client generates
a new UUID. Corpus retrieval always persists its key before the request.
Draft and Branch execute/resume remain deliberate CLI workflows (12ui draft
and 12ui branch). They coordinate hosted service runs, durable local run
directories, progress streaming, and multi-page packaging; the MCP surface does not duplicate those
orchestrators or quietly dispatch paid work from a status call.
