Agent-native UI IR from semantic data.
ViewSpec is an agent-native UI IR for describing what data means. The compiler produces CompositionIR, provenance, and emitter artifacts. Every pixel has a birth certificate.
🌐 viewspec.dev — Live hosted compiler playground, demos, and pricing
from viewspec import ViewSpecBuilder, compile
from viewspec.emitters.html_tailwind import HtmlTailwindEmitter
builder = ViewSpecBuilder("invoice")
table = builder.add_table("items", region="main", group_id="rows")
table.add_row(label="Design System Audit", value="$4,200")
table.add_row(label="Component Library", value="$8,500")
table.add_row(label="API Integration", value="$3,100")
ast = compile(builder.build_bundle())
HtmlTailwindEmitter().emit(ast, "output/")
# That's it. Full UI. Full provenance. No CSS.Before ViewSpec: You manually bridge the gap between data and UI. Every component, every prop, every layout decision — hand-wired by a developer.
After ViewSpec: You declare what the data means. The compiler determines the visual structure. Rendering is a pluggable backend.
Data → ViewSpec (semantic intent) → Compiler → CompositionIR → Emitter
├── HTML/Tailwind (shipped)
├── React TSX (hosted compiler)
├── SwiftUI (hosted compiler)
├── Flutter (hosted compiler)
└── Your custom emitter
ViewSpec enforces three mathematical guarantees:
-
Exactly-once provenance. Every data binding is routed exactly once. Nothing dropped. Nothing duplicated. Nothing hallucinated.
-
Semantic grouping. Data is grouped by meaning, not by visual adjacency.
-
Strict ordering. The original data order is preserved as a mathematical guarantee.
pip install viewspecRequires Python 3.11+.
The beta SDK can govern existing HTML entirely offline. Raw HTML compilation is intentionally narrow: it sanitizes active content, applies local DESIGN.md tokens, writes deterministic provenance, and can report semantic diffs. It does not claim full ViewSpec IR recovery or pixel review.
viewspec compile input.html --design DESIGN.md --out dist/
viewspec lift input.html --out lift.json
viewspec diff old.html new.html --jsonExample inputs live at examples/raw_html_report.html and examples/raw_html_DESIGN.md.
Raw HTML output files are:
index.htmlprovenance_manifest.jsondiagnostics.json- optional
lift.jsonwith--lift-json
The local commands compile, lift, and diff make no SDK-process network calls. Generated raw-HTML artifacts also avoid automatic network fetches: remote image sources are replaced with inert links and disclosed in external_refs; user-clicked external anchors remain clickable with rel="noopener noreferrer".
The home page at viewspec.dev runs a live hosted compile against https://api.viewspec.dev/v1/compile. It uses anonymous free-tier requests by default and shows the request, response, measured compile_ms, active derivation tokens, and provenance chain.
Agent and crawler entrypoints are published with the static site:
https://viewspec.dev/llms.txt— concise LLM-facing product maphttps://viewspec.dev/llms-full.txt— expanded AI context and canonical factshttps://viewspec.dev/agent-system-prompt.txt— system prompt for agents that emitIntentBundleJSONhttps://viewspec.dev/agent-intent-bundle.schema.json— JSON schema for agent-authored compiler inputhttps://viewspec.dev/openapi.json— hosted compiler OpenAPI descriptionhttps://viewspec.dev/sitemap.xml— canonical page sitemap
Runtime landing-page config is read from window.VIEWSPEC_LANDING_CONFIG:
| Key | Purpose |
|---|---|
apiUrl |
Hosted compiler endpoint. Defaults to https://api.viewspec.dev/v1/compile. |
fallbackApiUrls |
Optional fallback compiler endpoints for landing-page availability during custom-domain cutovers. |
endpointStaggerMs |
Delay before starting fallback endpoint requests. Defaults to 120. |
endpointFailureTtlMs |
How long the browser session keeps a failed endpoint out of the hot path. Defaults to 300000. |
publicApiKey |
Optional browser-safe public/demo key. window.PUBLIC_LANDING_API_KEY is also accepted. Omit it to use anonymous free-tier demo traffic. |
proStripeUrl |
Pro checkout link. Defaults to the live Stripe payment link. |
enterpriseUrl |
Enterprise contact URL. scaleStripeUrl is still accepted as a legacy alias. |
signupUrl |
Free CTA or pricing URL. Defaults to https://viewspec.dev/#pricing. |
requestTimeoutMs |
Hosted compile timeout. Defaults to 6000. |
Keep secret API keys server-side; only browser-safe public/demo keys belong in static landing-page config.
The hosted playground, reference demos, and launch demos are available at viewspec.dev:
| Demo | What it shows |
|---|---|
| Same Data, Three Motifs | One dataset → table, dashboard, or comparison. Change one parameter. |
| Provenance Inspector | Hover any element. Trace DOM → IR → binding → address → raw data. |
| Live Builder | Browse ViewSpec JSON, IR tree, and rendered output in sync. |
| The Invariants | Watch the compiler enforce — and refuse — each guarantee. |
| 15 Lines → Full UI | An invoice table builds itself from 15 lines of Python. |
| Style Derivation | Same structure, different feel. Toggle four visual presets. |
| One Spec, Four Surfaces | One launch dashboard compiles to HTML, React TSX, SwiftUI, and Flutter. |
| Custom Motif Authoring | Define an MDL motif contract and lower it into portable CompositionIR. |
| Interactive Compose | Inputs, rules, and submit payloads compiled into event surfaces. |
Text rendering powered by Pretext canvas surfaces.
The raw data graph. Nodes with typed attributes, slots, and edges. This is WHAT the data is — no visual intent.
builder = ViewSpecBuilder("my_view")
builder.add_node("user_1", "person", attrs={"name": "Alice", "role": "Engineer"})
builder.add_node("user_2", "person", attrs={"name": "Bob", "role": "Designer"})The declarative intent layer. Regions (WHERE data can go), bindings (WHICH data goes WHERE), motifs (HOW it should be structured), and styles (how it should FEEL).
table = builder.add_table("team", region="main", group_id="members")
table.add_row(label="Alice", value="Engineer")
table.add_row(label="Bob", value="Designer")The compiler's output. A strict hierarchical tree of 12 UI primitives (root, stack, grid, cluster, surface, text, label, value, badge, image_slot, rule, svg) with full provenance tracking. Every IR node knows which semantic addresses and intent refs produced it.
Pluggable renderers that turn CompositionIR into concrete output. Subclass EmitterPlugin:
from viewspec.emitters.base import EmitterPlugin
class MyEmitter(EmitterPlugin):
def emit(self, ast_bundle, output_dir):
# Walk ast_bundle.result.root.root and produce output
...The included HTML/Tailwind emitter produces standalone HTML with full Tailwind styling, provenance data attributes on every DOM element, action event dispatch, and a JSON provenance manifest.
| Builder | Motif | Use case |
|---|---|---|
add_table() |
table |
Tabular data with label-value rows |
add_dashboard() |
dashboard |
KPI cards with label-value pairs |
add_outline() |
outline |
Hierarchical outlines and trees |
add_comparison() |
comparison |
Side-by-side comparisons |
Each builder returns a chained sub-builder. Compose them freely within a single ViewSpec.
Handles the four standard motifs locally. No API, no network, no LLM. Deterministic.
ast = compile(builder.build_bundle())For complex layouts, novel data shapes, and advanced derivation. The hosted compiler was evolved (not hand-written) using reinforcement learning:
- 13/13 on a static validation suite
- 50/50 on novel, randomized out-of-distribution layouts (one-shot)
- Level 2 derivation tokens — data-aware emphasis, narrative routing, palette energy
- Zero LLM calls at runtime — deterministic Python compile path; the live playground reports measured
compile_msfor each request
from viewspec import compile_auto
# Try local first, fall back to hosted for unsupported motifs
ast = compile_auto(builder.build_bundle())The local SDK can parse a strict YAML-front-matter DESIGN.md subset for offline HTML and IntentBundle compilation. Parse errors, broken token references, and cycles are fatal. Malformed ignorable tokens become diagnostics and fall back to defaults; --strict-design escalates warnings to failure.
viewspec compile input.html --design DESIGN.md --out dist/
viewspec compile bundle.json --design DESIGN.md --out dist/Python callers can use the same local parser:
from viewspec import compile, compile_html, load_design_system
design = load_design_system(path="DESIGN.md")
html_result = compile_html("<h1>Report</h1>", design=design)
ast = compile(bundle, design=design)The hosted compiler can still ingest a DESIGN.md identity file as an opaque payload for hosted-only surfaces:
from viewspec import ViewSpecBuilder, compile_remote_response
builder = ViewSpecBuilder("invoice")
builder.attach_design("DESIGN.md")
request = builder.build_compile_request()
response = compile_remote_response(request)
ast = response.ast
design_meta = response.meta.designRaw strings are also supported:
request = builder.attach_design("name: Acme\ncolor.primary: #FFFFFF\n", is_path=False).build_compile_request()The TypeScript/Node SDK contract will mirror this shape:
const result = await compiler.withDesign("DESIGN.md").compile(bundle)
const inline = await compiler.withDesign("name: Acme\n", false).compile(bundle)DESIGN.md ingestion is intentionally strict locally and in the API:
- Colors must be exact sRGB hex values such as
#FFFFFF.rgba(),#FFF, and named CSS colors are ignored and fall back to defaults. fontFamilytokens map to React/HTML CSS. Flutter and SwiftUI emitters coerce custom font families to native system defaults while preserving size, weight, and tracking.
| Tier | Price | Hosted Calls/Day |
|---|---|---|
| Free | $0 | 500 |
| Pro | $699/mo | 25,000 compile calls/day + 10,000 hosted renders/month |
| Enterprise | Custom | Custom volume, unlimited custom motifs |
The hosted compiler now exposes the May 6 launch surface: React TSX, SwiftUI, and Flutter emitters; projections; input bindings; rule bindings; submit/navigate actions; and custom motifs. The public SDK remains the stable offline/reference path.
Pro includes mobile emitters, 5 custom motif instances per compile, 25,000 hosted compile calls/day, and a 10,000 hosted renders/month entitlement.
Protocol Buffers for language-agnostic serialization. The same ViewSpec can be constructed in Python, Rust, Go, TypeScript, or any language with protobuf support.
bundle = builder.build_bundle()
json_data = bundle.to_json() # JSON round-trip
proto_bytes = bundle.to_proto().SerializeToString() # Protobuf round-tripSee examples/:
invoice_table.py— Build a table in 15 lineskpi_dashboard.py— KPI dashboard with style tokenscomparison_view.py— Side-by-side comparisonemit_html.py— Load a compiled AST and emit HTML
MIT