viewspec 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- viewspec-0.1.0/.github/workflows/pages.yml +30 -0
- viewspec-0.1.0/.gitignore +16 -0
- viewspec-0.1.0/LICENSE +21 -0
- viewspec-0.1.0/PKG-INFO +208 -0
- viewspec-0.1.0/README.md +179 -0
- viewspec-0.1.0/demos/CNAME +1 -0
- viewspec-0.1.0/demos/SPEC-DEMO-FIFTEEN-LINES.md +96 -0
- viewspec-0.1.0/demos/SPEC-DEMO-INVARIANTS.md +89 -0
- viewspec-0.1.0/demos/SPEC-DEMO-LANDING.md +101 -0
- viewspec-0.1.0/demos/SPEC-DEMO-LIVE-BUILDER.md +102 -0
- viewspec-0.1.0/demos/SPEC-DEMO-MOTIF-SWITCHER.md +91 -0
- viewspec-0.1.0/demos/SPEC-DEMO-PROVENANCE-INSPECTOR.md +104 -0
- viewspec-0.1.0/demos/build_fifteen_lines.py +572 -0
- viewspec-0.1.0/demos/build_invariants.py +644 -0
- viewspec-0.1.0/demos/build_landing.py +83 -0
- viewspec-0.1.0/demos/build_live_builder.py +446 -0
- viewspec-0.1.0/demos/build_motif_switcher.py +343 -0
- viewspec-0.1.0/demos/build_provenance_inspector.py +424 -0
- viewspec-0.1.0/demos/build_style_derivation.py +533 -0
- viewspec-0.1.0/demos/fifteen-lines/index.html +540 -0
- viewspec-0.1.0/demos/index.html +422 -0
- viewspec-0.1.0/demos/invariants/index.html +400 -0
- viewspec-0.1.0/demos/landing-compiled/diagnostics.json +1 -0
- viewspec-0.1.0/demos/landing-compiled/index.html +25 -0
- viewspec-0.1.0/demos/landing-compiled/intent_bundle.json +408 -0
- viewspec-0.1.0/demos/landing-compiled/provenance_manifest.json +523 -0
- viewspec-0.1.0/demos/live-builder/index.html +291 -0
- viewspec-0.1.0/demos/motif-switcher/index.html +1374 -0
- viewspec-0.1.0/demos/provenance-inspector/index.html +1589 -0
- viewspec-0.1.0/demos/serve.py +13 -0
- viewspec-0.1.0/demos/shared/nav.js +63 -0
- viewspec-0.1.0/demos/shared/pretext-canvas-surfaces.js +208 -0
- viewspec-0.1.0/demos/style-derivation/index.html +545 -0
- viewspec-0.1.0/demos/vendor/pretext/LICENSE +2 -0
- viewspec-0.1.0/demos/vendor/pretext/README.md +8 -0
- viewspec-0.1.0/demos/vendor/pretext/dist/analysis.mjs +6 -0
- viewspec-0.1.0/demos/vendor/pretext/dist/line-break.mjs +3 -0
- viewspec-0.1.0/demos/vendor/pretext/dist/line-text.mjs +3 -0
- viewspec-0.1.0/demos/vendor/pretext/dist/measurement.mjs +3 -0
- viewspec-0.1.0/demos/vendor/pretext/package.json +14 -0
- viewspec-0.1.0/demos/vendor/pretext/pretext.esm.js +3 -0
- viewspec-0.1.0/examples/comparison_view.py +16 -0
- viewspec-0.1.0/examples/emit_html.py +29 -0
- viewspec-0.1.0/examples/invoice_table.py +21 -0
- viewspec-0.1.0/examples/kpi_dashboard.py +20 -0
- viewspec-0.1.0/pyproject.toml +52 -0
- viewspec-0.1.0/src/viewspec/__init__.py +77 -0
- viewspec-0.1.0/src/viewspec/compiler.py +814 -0
- viewspec-0.1.0/src/viewspec/emitters/__init__.py +1 -0
- viewspec-0.1.0/src/viewspec/emitters/base.py +16 -0
- viewspec-0.1.0/src/viewspec/emitters/html_tailwind/__init__.py +190 -0
- viewspec-0.1.0/src/viewspec/schema/__init__.py +5 -0
- viewspec-0.1.0/src/viewspec/schema/viewspec_pb2.py +87 -0
- viewspec-0.1.0/src/viewspec/sdk/__init__.py +17 -0
- viewspec-0.1.0/src/viewspec/sdk/builder.py +348 -0
- viewspec-0.1.0/src/viewspec/types.py +762 -0
- viewspec-0.1.0/tests/__init__.py +1 -0
- viewspec-0.1.0/tests/test_compiler.py +135 -0
- viewspec-0.1.0/tests/test_roundtrip.py +81 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Deploy to GitHub Pages
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
pages: write
|
|
11
|
+
id-token: write
|
|
12
|
+
|
|
13
|
+
concurrency:
|
|
14
|
+
group: pages
|
|
15
|
+
cancel-in-progress: false
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
deploy:
|
|
19
|
+
environment:
|
|
20
|
+
name: github-pages
|
|
21
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/configure-pages@v5
|
|
26
|
+
- uses: actions/upload-pages-artifact@v3
|
|
27
|
+
with:
|
|
28
|
+
path: demos
|
|
29
|
+
- id: deployment
|
|
30
|
+
uses: actions/deploy-pages@v4
|
viewspec-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 nxrobins
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
viewspec-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: viewspec
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Universal UI from semantic data. Describe what your data means — the compiler figures out how it looks.
|
|
5
|
+
Project-URL: Homepage, https://github.com/nxrobins/viewspec
|
|
6
|
+
Project-URL: Repository, https://github.com/nxrobins/viewspec
|
|
7
|
+
Project-URL: Issues, https://github.com/nxrobins/viewspec/issues
|
|
8
|
+
Author: Nigel Robinson
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: compiler,composition-ir,declarative,protobuf,semantic,ui,viewspec
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Classifier: Topic :: Software Development :: Compilers
|
|
22
|
+
Classifier: Topic :: Software Development :: User Interfaces
|
|
23
|
+
Requires-Python: >=3.11
|
|
24
|
+
Requires-Dist: protobuf>=5.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# ViewSpec
|
|
31
|
+
|
|
32
|
+
**Universal UI from semantic data.**
|
|
33
|
+
|
|
34
|
+
ViewSpec is a declarative language for describing what data means. The compiler figures out how it looks. Every pixel has a birth certificate.
|
|
35
|
+
|
|
36
|
+
🌐 **[viewspec.dev](https://viewspec.dev)** — Live demos and interactive playground
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from viewspec import ViewSpecBuilder, compile
|
|
40
|
+
from viewspec.emitters.html_tailwind import HtmlTailwindEmitter
|
|
41
|
+
|
|
42
|
+
builder = ViewSpecBuilder("invoice")
|
|
43
|
+
table = builder.add_table("items", region="main", group_id="rows")
|
|
44
|
+
table.add_row(label="Design System Audit", value="$4,200")
|
|
45
|
+
table.add_row(label="Component Library", value="$8,500")
|
|
46
|
+
table.add_row(label="API Integration", value="$3,100")
|
|
47
|
+
|
|
48
|
+
ast = compile(builder.build_bundle())
|
|
49
|
+
HtmlTailwindEmitter().emit(ast, "output/")
|
|
50
|
+
|
|
51
|
+
# That's it. Full UI. Full provenance. No CSS.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## What ViewSpec Does
|
|
55
|
+
|
|
56
|
+
**Before ViewSpec:** You manually bridge the gap between data and UI. Every component, every prop, every layout decision — hand-wired by a developer.
|
|
57
|
+
|
|
58
|
+
**After ViewSpec:** You declare what the data means. The compiler determines the visual structure. Rendering is a pluggable backend.
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Data → ViewSpec (semantic intent) → Compiler → CompositionIR → Emitter
|
|
62
|
+
├── HTML/Tailwind (shipped)
|
|
63
|
+
├── Canvas/Pretext
|
|
64
|
+
├── PDF
|
|
65
|
+
├── Native mobile
|
|
66
|
+
└── Your custom emitter
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Three Invariants
|
|
70
|
+
|
|
71
|
+
ViewSpec enforces three mathematical guarantees:
|
|
72
|
+
|
|
73
|
+
1. **Exactly-once provenance.** Every data binding is routed exactly once. Nothing dropped. Nothing duplicated. Nothing hallucinated.
|
|
74
|
+
|
|
75
|
+
2. **Semantic grouping.** Data is grouped by meaning, not by visual adjacency.
|
|
76
|
+
|
|
77
|
+
3. **Strict ordering.** The original data order is preserved as a mathematical guarantee.
|
|
78
|
+
|
|
79
|
+
## Install
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install viewspec
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Requires Python 3.11+.
|
|
86
|
+
|
|
87
|
+
## Demos
|
|
88
|
+
|
|
89
|
+
Six interactive demos at [viewspec.dev](https://viewspec.dev):
|
|
90
|
+
|
|
91
|
+
| Demo | What it shows |
|
|
92
|
+
|------|--------------|
|
|
93
|
+
| [Same Data, Three Motifs](https://viewspec.dev/motif-switcher/) | One dataset → table, dashboard, or comparison. Change one parameter. |
|
|
94
|
+
| [Provenance Inspector](https://viewspec.dev/provenance-inspector/) | Hover any element. Trace DOM → IR → binding → address → raw data. |
|
|
95
|
+
| [Live Builder](https://viewspec.dev/live-builder/) | Browse ViewSpec JSON, IR tree, and rendered output in sync. |
|
|
96
|
+
| [The Invariants](https://viewspec.dev/invariants/) | Watch the compiler enforce — and refuse — each guarantee. |
|
|
97
|
+
| [15 Lines → Full UI](https://viewspec.dev/fifteen-lines/) | An invoice table builds itself from 15 lines of Python. |
|
|
98
|
+
| [Style Derivation](https://viewspec.dev/style-derivation/) | Same structure, different feel. Toggle four visual presets. |
|
|
99
|
+
|
|
100
|
+
Text rendering powered by [Pretext](https://github.com/chenglou/pretext) canvas surfaces.
|
|
101
|
+
|
|
102
|
+
## Core Concepts
|
|
103
|
+
|
|
104
|
+
### Semantic Substrate
|
|
105
|
+
|
|
106
|
+
The raw data graph. Nodes with typed attributes, slots, and edges. This is WHAT the data is — no visual intent.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
builder = ViewSpecBuilder("my_view")
|
|
110
|
+
builder.add_node("user_1", "person", attrs={"name": "Alice", "role": "Engineer"})
|
|
111
|
+
builder.add_node("user_2", "person", attrs={"name": "Bob", "role": "Designer"})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### ViewSpec
|
|
115
|
+
|
|
116
|
+
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).
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
table = builder.add_table("team", region="main", group_id="members")
|
|
120
|
+
table.add_row(label="Alice", value="Engineer")
|
|
121
|
+
table.add_row(label="Bob", value="Designer")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### CompositionIR
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
### Emitters
|
|
129
|
+
|
|
130
|
+
Pluggable renderers that turn CompositionIR into concrete output. Subclass `EmitterPlugin`:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from viewspec.emitters.base import EmitterPlugin
|
|
134
|
+
|
|
135
|
+
class MyEmitter(EmitterPlugin):
|
|
136
|
+
def emit(self, ast_bundle, output_dir):
|
|
137
|
+
# Walk ast_bundle.result.root.root and produce output
|
|
138
|
+
...
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
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.
|
|
142
|
+
|
|
143
|
+
## Motif Types
|
|
144
|
+
|
|
145
|
+
| Builder | Motif | Use case |
|
|
146
|
+
|---------|-------|----------|
|
|
147
|
+
| `add_table()` | `table` | Tabular data with label-value rows |
|
|
148
|
+
| `add_dashboard()` | `dashboard` | KPI cards with label-value pairs |
|
|
149
|
+
| `add_outline()` | `outline` | Hierarchical outlines and trees |
|
|
150
|
+
| `add_comparison()` | `comparison` | Side-by-side comparisons |
|
|
151
|
+
|
|
152
|
+
Each builder returns a chained sub-builder. Compose them freely within a single ViewSpec.
|
|
153
|
+
|
|
154
|
+
## Compilation
|
|
155
|
+
|
|
156
|
+
### Reference Compiler (free, offline)
|
|
157
|
+
|
|
158
|
+
Handles the four standard motifs locally. No API, no network, no LLM. Deterministic.
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
ast = compile(builder.build_bundle())
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Hosted Compiler (api.viewspec.dev)
|
|
165
|
+
|
|
166
|
+
For complex layouts, novel data shapes, and advanced derivation. The hosted compiler was **evolved** (not hand-written) using reinforcement learning:
|
|
167
|
+
|
|
168
|
+
- **13/13** on a static validation suite
|
|
169
|
+
- **50/50** on novel, randomized out-of-distribution layouts (one-shot)
|
|
170
|
+
- **Level 2 derivation tokens** — data-aware emphasis, narrative routing, palette energy
|
|
171
|
+
- **Zero LLM cost at runtime** — deterministic Python, ~3ms per compile
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from viewspec import compile_auto
|
|
175
|
+
|
|
176
|
+
# Try local first, fall back to hosted for unsupported motifs
|
|
177
|
+
ast = compile_auto(builder.build_bundle())
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Tier | Price | Hosted Calls/Day |
|
|
181
|
+
|------|-------|-----------------|
|
|
182
|
+
| Free | $0 | 500 |
|
|
183
|
+
| Pro | $39/mo | 25,000 |
|
|
184
|
+
| Scale | $99/mo | 250,000 |
|
|
185
|
+
| Enterprise | Contact | Custom |
|
|
186
|
+
|
|
187
|
+
## Wire Format
|
|
188
|
+
|
|
189
|
+
Protocol Buffers for language-agnostic serialization. The same ViewSpec can be constructed in Python, Rust, Go, TypeScript, or any language with protobuf support.
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
bundle = builder.build_bundle()
|
|
193
|
+
json_data = bundle.to_json() # JSON round-trip
|
|
194
|
+
proto_bytes = bundle.to_proto().SerializeToString() # Protobuf round-trip
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Examples
|
|
198
|
+
|
|
199
|
+
See [`examples/`](examples/):
|
|
200
|
+
|
|
201
|
+
- **`invoice_table.py`** — Build a table in 15 lines
|
|
202
|
+
- **`kpi_dashboard.py`** — KPI dashboard with style tokens
|
|
203
|
+
- **`comparison_view.py`** — Side-by-side comparison
|
|
204
|
+
- **`emit_html.py`** — Load a compiled AST and emit HTML
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
viewspec-0.1.0/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# ViewSpec
|
|
2
|
+
|
|
3
|
+
**Universal UI from semantic data.**
|
|
4
|
+
|
|
5
|
+
ViewSpec is a declarative language for describing what data means. The compiler figures out how it looks. Every pixel has a birth certificate.
|
|
6
|
+
|
|
7
|
+
🌐 **[viewspec.dev](https://viewspec.dev)** — Live demos and interactive playground
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
from viewspec import ViewSpecBuilder, compile
|
|
11
|
+
from viewspec.emitters.html_tailwind import HtmlTailwindEmitter
|
|
12
|
+
|
|
13
|
+
builder = ViewSpecBuilder("invoice")
|
|
14
|
+
table = builder.add_table("items", region="main", group_id="rows")
|
|
15
|
+
table.add_row(label="Design System Audit", value="$4,200")
|
|
16
|
+
table.add_row(label="Component Library", value="$8,500")
|
|
17
|
+
table.add_row(label="API Integration", value="$3,100")
|
|
18
|
+
|
|
19
|
+
ast = compile(builder.build_bundle())
|
|
20
|
+
HtmlTailwindEmitter().emit(ast, "output/")
|
|
21
|
+
|
|
22
|
+
# That's it. Full UI. Full provenance. No CSS.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What ViewSpec Does
|
|
26
|
+
|
|
27
|
+
**Before ViewSpec:** You manually bridge the gap between data and UI. Every component, every prop, every layout decision — hand-wired by a developer.
|
|
28
|
+
|
|
29
|
+
**After ViewSpec:** You declare what the data means. The compiler determines the visual structure. Rendering is a pluggable backend.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Data → ViewSpec (semantic intent) → Compiler → CompositionIR → Emitter
|
|
33
|
+
├── HTML/Tailwind (shipped)
|
|
34
|
+
├── Canvas/Pretext
|
|
35
|
+
├── PDF
|
|
36
|
+
├── Native mobile
|
|
37
|
+
└── Your custom emitter
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Three Invariants
|
|
41
|
+
|
|
42
|
+
ViewSpec enforces three mathematical guarantees:
|
|
43
|
+
|
|
44
|
+
1. **Exactly-once provenance.** Every data binding is routed exactly once. Nothing dropped. Nothing duplicated. Nothing hallucinated.
|
|
45
|
+
|
|
46
|
+
2. **Semantic grouping.** Data is grouped by meaning, not by visual adjacency.
|
|
47
|
+
|
|
48
|
+
3. **Strict ordering.** The original data order is preserved as a mathematical guarantee.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install viewspec
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Requires Python 3.11+.
|
|
57
|
+
|
|
58
|
+
## Demos
|
|
59
|
+
|
|
60
|
+
Six interactive demos at [viewspec.dev](https://viewspec.dev):
|
|
61
|
+
|
|
62
|
+
| Demo | What it shows |
|
|
63
|
+
|------|--------------|
|
|
64
|
+
| [Same Data, Three Motifs](https://viewspec.dev/motif-switcher/) | One dataset → table, dashboard, or comparison. Change one parameter. |
|
|
65
|
+
| [Provenance Inspector](https://viewspec.dev/provenance-inspector/) | Hover any element. Trace DOM → IR → binding → address → raw data. |
|
|
66
|
+
| [Live Builder](https://viewspec.dev/live-builder/) | Browse ViewSpec JSON, IR tree, and rendered output in sync. |
|
|
67
|
+
| [The Invariants](https://viewspec.dev/invariants/) | Watch the compiler enforce — and refuse — each guarantee. |
|
|
68
|
+
| [15 Lines → Full UI](https://viewspec.dev/fifteen-lines/) | An invoice table builds itself from 15 lines of Python. |
|
|
69
|
+
| [Style Derivation](https://viewspec.dev/style-derivation/) | Same structure, different feel. Toggle four visual presets. |
|
|
70
|
+
|
|
71
|
+
Text rendering powered by [Pretext](https://github.com/chenglou/pretext) canvas surfaces.
|
|
72
|
+
|
|
73
|
+
## Core Concepts
|
|
74
|
+
|
|
75
|
+
### Semantic Substrate
|
|
76
|
+
|
|
77
|
+
The raw data graph. Nodes with typed attributes, slots, and edges. This is WHAT the data is — no visual intent.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
builder = ViewSpecBuilder("my_view")
|
|
81
|
+
builder.add_node("user_1", "person", attrs={"name": "Alice", "role": "Engineer"})
|
|
82
|
+
builder.add_node("user_2", "person", attrs={"name": "Bob", "role": "Designer"})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### ViewSpec
|
|
86
|
+
|
|
87
|
+
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).
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
table = builder.add_table("team", region="main", group_id="members")
|
|
91
|
+
table.add_row(label="Alice", value="Engineer")
|
|
92
|
+
table.add_row(label="Bob", value="Designer")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### CompositionIR
|
|
96
|
+
|
|
97
|
+
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.
|
|
98
|
+
|
|
99
|
+
### Emitters
|
|
100
|
+
|
|
101
|
+
Pluggable renderers that turn CompositionIR into concrete output. Subclass `EmitterPlugin`:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from viewspec.emitters.base import EmitterPlugin
|
|
105
|
+
|
|
106
|
+
class MyEmitter(EmitterPlugin):
|
|
107
|
+
def emit(self, ast_bundle, output_dir):
|
|
108
|
+
# Walk ast_bundle.result.root.root and produce output
|
|
109
|
+
...
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
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.
|
|
113
|
+
|
|
114
|
+
## Motif Types
|
|
115
|
+
|
|
116
|
+
| Builder | Motif | Use case |
|
|
117
|
+
|---------|-------|----------|
|
|
118
|
+
| `add_table()` | `table` | Tabular data with label-value rows |
|
|
119
|
+
| `add_dashboard()` | `dashboard` | KPI cards with label-value pairs |
|
|
120
|
+
| `add_outline()` | `outline` | Hierarchical outlines and trees |
|
|
121
|
+
| `add_comparison()` | `comparison` | Side-by-side comparisons |
|
|
122
|
+
|
|
123
|
+
Each builder returns a chained sub-builder. Compose them freely within a single ViewSpec.
|
|
124
|
+
|
|
125
|
+
## Compilation
|
|
126
|
+
|
|
127
|
+
### Reference Compiler (free, offline)
|
|
128
|
+
|
|
129
|
+
Handles the four standard motifs locally. No API, no network, no LLM. Deterministic.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
ast = compile(builder.build_bundle())
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Hosted Compiler (api.viewspec.dev)
|
|
136
|
+
|
|
137
|
+
For complex layouts, novel data shapes, and advanced derivation. The hosted compiler was **evolved** (not hand-written) using reinforcement learning:
|
|
138
|
+
|
|
139
|
+
- **13/13** on a static validation suite
|
|
140
|
+
- **50/50** on novel, randomized out-of-distribution layouts (one-shot)
|
|
141
|
+
- **Level 2 derivation tokens** — data-aware emphasis, narrative routing, palette energy
|
|
142
|
+
- **Zero LLM cost at runtime** — deterministic Python, ~3ms per compile
|
|
143
|
+
|
|
144
|
+
```python
|
|
145
|
+
from viewspec import compile_auto
|
|
146
|
+
|
|
147
|
+
# Try local first, fall back to hosted for unsupported motifs
|
|
148
|
+
ast = compile_auto(builder.build_bundle())
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Tier | Price | Hosted Calls/Day |
|
|
152
|
+
|------|-------|-----------------|
|
|
153
|
+
| Free | $0 | 500 |
|
|
154
|
+
| Pro | $39/mo | 25,000 |
|
|
155
|
+
| Scale | $99/mo | 250,000 |
|
|
156
|
+
| Enterprise | Contact | Custom |
|
|
157
|
+
|
|
158
|
+
## Wire Format
|
|
159
|
+
|
|
160
|
+
Protocol Buffers for language-agnostic serialization. The same ViewSpec can be constructed in Python, Rust, Go, TypeScript, or any language with protobuf support.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
bundle = builder.build_bundle()
|
|
164
|
+
json_data = bundle.to_json() # JSON round-trip
|
|
165
|
+
proto_bytes = bundle.to_proto().SerializeToString() # Protobuf round-trip
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Examples
|
|
169
|
+
|
|
170
|
+
See [`examples/`](examples/):
|
|
171
|
+
|
|
172
|
+
- **`invoice_table.py`** — Build a table in 15 lines
|
|
173
|
+
- **`kpi_dashboard.py`** — KPI dashboard with style tokens
|
|
174
|
+
- **`comparison_view.py`** — Side-by-side comparison
|
|
175
|
+
- **`emit_html.py`** — Load a compiled AST and emit HTML
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
viewspec.dev
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Demo: 15 Lines → Full UI
|
|
2
|
+
|
|
3
|
+
## What It Proves
|
|
4
|
+
The ratio between intent and output is extreme. Fifteen lines of semantic description produces a complete, styled, provenance-tracked UI. No components. No layout code. No CSS.
|
|
5
|
+
|
|
6
|
+
## Behavior
|
|
7
|
+
|
|
8
|
+
Split screen. Left: Python code. Right: rendered output.
|
|
9
|
+
|
|
10
|
+
The code appears line by line (typewriter effect, ~80ms per character, ~400ms pause between lines). As each semantically meaningful line completes, the rendered output on the right updates to include the new element. The build-up is progressive — the user watches the UI construct itself from semantic declarations.
|
|
11
|
+
|
|
12
|
+
### The Code (15 lines)
|
|
13
|
+
```python
|
|
14
|
+
from viewspec import ViewSpecBuilder
|
|
15
|
+
|
|
16
|
+
builder = ViewSpecBuilder("invoice")
|
|
17
|
+
|
|
18
|
+
table = builder.add_table("items", region="main", group_id="rows")
|
|
19
|
+
table.add_row(label="Design System Audit", value="$4,200")
|
|
20
|
+
table.add_row(label="Component Library", value="$8,500")
|
|
21
|
+
table.add_row(label="API Integration", value="$3,100")
|
|
22
|
+
table.add_row(label="QA & Testing", value="$2,800")
|
|
23
|
+
table.add_row(label="Documentation", value="$1,400")
|
|
24
|
+
|
|
25
|
+
builder.add_style("s1", "items_row_5_label", "tone.muted")
|
|
26
|
+
builder.add_style("s2", "items_row_5_value", "emphasis.high")
|
|
27
|
+
|
|
28
|
+
bundle = builder.build_bundle()
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Progressive Reveal Steps
|
|
32
|
+
Lines 1-2 (import + builder): Empty root container appears on right
|
|
33
|
+
Line 4 (add_table): Table structure appears (empty, with header area)
|
|
34
|
+
Lines 5-9 (add_row × 5): Each row appears as the line completes
|
|
35
|
+
Lines 11-12 (add_style): The "Documentation" row's label dims and value bolds
|
|
36
|
+
Line 14 (build_bundle): A "✓ Bundle Ready" badge animates in below the table, showing binding count and provenance status
|
|
37
|
+
|
|
38
|
+
After the animation completes, a "Replay" button appears.
|
|
39
|
+
|
|
40
|
+
## Implementation
|
|
41
|
+
|
|
42
|
+
### Pre-generation
|
|
43
|
+
Write a Python script (`demos/build_fifteen_lines.py`) that:
|
|
44
|
+
1. Builds the full IntentBundle
|
|
45
|
+
2. Hand-builds IR trees for each progressive step (empty, table shell, 1 row, 2 rows, ..., 5 rows, styled, final)
|
|
46
|
+
3. Emits HTML fragments for each step
|
|
47
|
+
4. Packages into `index.html` with typewriter JS
|
|
48
|
+
|
|
49
|
+
### HTML Structure
|
|
50
|
+
```html
|
|
51
|
+
<div class="split-screen">
|
|
52
|
+
<div class="code-panel">
|
|
53
|
+
<div class="panel-header">
|
|
54
|
+
<span class="filename">invoice.py</span>
|
|
55
|
+
<span class="lang-badge">Python</span>
|
|
56
|
+
</div>
|
|
57
|
+
<pre><code id="code-display"></code></pre>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="output-panel">
|
|
61
|
+
<div class="panel-header">
|
|
62
|
+
<span class="filename">Rendered Output</span>
|
|
63
|
+
<span class="live-badge">● Live</span>
|
|
64
|
+
</div>
|
|
65
|
+
<div id="output-display"></div>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Styling
|
|
71
|
+
- Code panel: dark (slate-900), monospace, syntax highlighting via CSS classes
|
|
72
|
+
- Keywords (from, import): blue-400
|
|
73
|
+
- Strings: green-400
|
|
74
|
+
- Method calls: teal-300
|
|
75
|
+
- Comments: slate-500
|
|
76
|
+
- Output panel: light (slate-50), the normal emitter output
|
|
77
|
+
- Split: 50/50 on desktop, stacked on mobile (code on top)
|
|
78
|
+
- Cursor: blinking block cursor at end of current line during typing
|
|
79
|
+
- "✓ Bundle Ready" badge: slides up, green background, shows "10 bindings | 5 nodes | full provenance"
|
|
80
|
+
|
|
81
|
+
### JS (~80 lines)
|
|
82
|
+
- Array of code lines with metadata: `{ text, delay, outputStep }`
|
|
83
|
+
- Typewriter loop: character by character with configurable speed
|
|
84
|
+
- On each `outputStep`, swap the output panel's innerHTML to the pre-generated fragment
|
|
85
|
+
- Replay button resets and restarts
|
|
86
|
+
- Skip button (subtle, bottom-right) jumps to final state
|
|
87
|
+
|
|
88
|
+
## Output
|
|
89
|
+
`demos/fifteen-lines/index.html` — single self-contained HTML file.
|
|
90
|
+
|
|
91
|
+
## Quality Bar
|
|
92
|
+
- The typing must feel natural (not robotic, slight variance in speed)
|
|
93
|
+
- Syntax highlighting must be accurate for Python
|
|
94
|
+
- The progressive reveal must feel like the UI is being BUILT, not just shown
|
|
95
|
+
- The final state must be a genuinely good-looking invoice table
|
|
96
|
+
- The ratio (15 lines → complete UI) must be felt, not just counted
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Demo: The Invariants
|
|
2
|
+
|
|
3
|
+
## What It Proves
|
|
4
|
+
ViewSpec doesn't just produce correct UI — it refuses to produce incorrect UI. The three invariants (exactly-once provenance, semantic grouping, strict ordering) are enforced, visible, and unbreakable.
|
|
5
|
+
|
|
6
|
+
## Behavior
|
|
7
|
+
|
|
8
|
+
Three sections, each demonstrating one invariant being enforced. Each section has:
|
|
9
|
+
- A title and one-sentence explanation
|
|
10
|
+
- A "Valid" example (green border, renders correctly)
|
|
11
|
+
- A "Violation" example (red border, shows the CompilerDiagnostic)
|
|
12
|
+
- A toggle to flip between them
|
|
13
|
+
|
|
14
|
+
### Section 1: Exactly-Once Provenance
|
|
15
|
+
|
|
16
|
+
**Valid:** A table with 4 rows. Each binding routes exactly once. All data accounted for. A counter shows "4/4 bindings routed."
|
|
17
|
+
|
|
18
|
+
**Violation:** Same table but one binding is duplicated (appears in two rows). The compiler catches it: `CompilerDiagnostic { severity: "error", code: "DUPLICATE_ROUTING", message: "Binding 'row_2_value' routed to multiple IR nodes" }`. The duplicate element renders with a red dashed border and an error badge.
|
|
19
|
+
|
|
20
|
+
### Section 2: Semantic Grouping
|
|
21
|
+
|
|
22
|
+
**Valid:** A dashboard with 3 cards. Each card's label and value are grouped by semantic boundary (same node). Groups are visually enclosed in surfaces.
|
|
23
|
+
|
|
24
|
+
**Violation:** A label from card 1 is grouped with the value from card 2 (cross-group contamination). Diagnostic: `{ code: "GROUP_VIOLATION", message: "Binding 'card_1_label' grouped with non-sibling 'card_2_value'" }`. The mismatched elements highlight with connecting red lines.
|
|
25
|
+
|
|
26
|
+
### Section 3: Strict Ordering
|
|
27
|
+
|
|
28
|
+
**Valid:** A table where rows appear in the order they were declared in the substrate. Row 1 → Row 2 → Row 3 → Row 4.
|
|
29
|
+
|
|
30
|
+
**Violation:** Rows reordered (Row 1 → Row 3 → Row 2 → Row 4). Diagnostic: `{ code: "ORDER_VIOLATION", message: "Binding 'row_3_label' precedes 'row_2_label' but was declared after it in the substrate" }`. The out-of-order rows get numbered badges showing declared order vs rendered order.
|
|
31
|
+
|
|
32
|
+
## Implementation
|
|
33
|
+
|
|
34
|
+
### Pre-generation
|
|
35
|
+
Write a Python script (`demos/build_invariants.py`) that:
|
|
36
|
+
1. For each invariant, builds a valid IntentBundle and a deliberately broken one
|
|
37
|
+
2. Hand-builds correct IR trees for valid cases
|
|
38
|
+
3. Hand-builds broken IR trees with `CompilerDiagnostic` entries for violation cases
|
|
39
|
+
4. Emits both via `HtmlTailwindEmitter`
|
|
40
|
+
5. Wraps in `index.html` with toggle JS
|
|
41
|
+
|
|
42
|
+
### HTML Structure
|
|
43
|
+
```html
|
|
44
|
+
<section class="invariant">
|
|
45
|
+
<h2>Exactly-Once Provenance</h2>
|
|
46
|
+
<p>Every data binding is routed exactly once. Nothing dropped. Nothing duplicated.</p>
|
|
47
|
+
|
|
48
|
+
<div class="toggle-group">
|
|
49
|
+
<button data-state="valid" class="active">✓ Valid</button>
|
|
50
|
+
<button data-state="violation">✗ Violation</button>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="demo-container">
|
|
54
|
+
<div class="state-valid active">
|
|
55
|
+
<!-- valid rendered HTML with green border -->
|
|
56
|
+
<div class="status-bar valid">✓ 4/4 bindings routed exactly once</div>
|
|
57
|
+
</div>
|
|
58
|
+
<div class="state-violation">
|
|
59
|
+
<!-- violation rendered HTML with red border -->
|
|
60
|
+
<div class="diagnostic">
|
|
61
|
+
<span class="severity error">ERROR</span>
|
|
62
|
+
<span class="code">DUPLICATE_ROUTING</span>
|
|
63
|
+
<span class="message">Binding 'row_2_value' routed to multiple IR nodes</span>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Styling
|
|
71
|
+
- Valid state: thin green-500 left border on the container
|
|
72
|
+
- Violation state: thin red-500 left border, diagnostic card below with monospace text
|
|
73
|
+
- Error elements within violations: red dashed border, semi-transparent red overlay
|
|
74
|
+
- Status bar: full-width bar below the rendered output showing pass/fail
|
|
75
|
+
- Diagnostic cards styled like compiler output (dark bg, monospace, severity badge)
|
|
76
|
+
- Subtle shake animation on the violation elements when toggled to (150ms, 2px)
|
|
77
|
+
|
|
78
|
+
### JS (~30 lines)
|
|
79
|
+
Per-section toggle between valid and violation states.
|
|
80
|
+
|
|
81
|
+
## Output
|
|
82
|
+
`demos/invariants/index.html` — single self-contained HTML file.
|
|
83
|
+
|
|
84
|
+
## Quality Bar
|
|
85
|
+
- The violations must look genuinely broken, not just annotated
|
|
86
|
+
- The diagnostics must read like real compiler output
|
|
87
|
+
- The valid examples must feel solid and trustworthy
|
|
88
|
+
- The contrast between valid and violation should be visceral
|
|
89
|
+
- Someone watching should think "I want my UI to have this"
|