|
1 | | -# TUIkit Specs |
2 | | - |
3 | | -A spec-driven system for building UI components across programming languages. |
4 | | -Each component is defined as a language-agnostic markdown spec with behavioral |
5 | | -tests. An LLM agent acts as the "compiler" — reading specs and generating |
6 | | -idiomatic implementations per target framework. |
7 | | - |
8 | | -## How it works |
9 | | - |
10 | | -```mermaid |
11 | | -flowchart LR |
12 | | - Specs["Spec files\n(.md)"] --> Compile["compile.ts\n(prompt)"] |
13 | | - Compile --> Agent["LLM Agent\n(compiler)"] |
14 | | - Agent --> Dist["dist/{target}/\n(generated code)"] |
15 | | - Agent --> Lock["lock file\n(.json)"] |
16 | | -``` |
17 | | - |
18 | | -1. **Specs** define behavior + semantic tokens (like headless UI libraries) |
19 | | -2. **Target specs** define how to translate to a specific language/framework |
20 | | -3. **compile.ts** detects changed specs and generates a self-contained prompt |
21 | | -4. An **LLM agent** reads the prompt and generates idiomatic code |
22 | | -5. Generated code goes to **dist/** — specs stay clean |
23 | | -6. **Lock files** track which spec versions have been compiled |
24 | | - |
25 | | -## Quick start |
26 | | - |
27 | | -### Prerequisites |
28 | | - |
29 | | -- [Bun](https://bun.sh/) 1.1+ installed (`bun --version`) |
30 | | - |
31 | | -### Install dependencies |
32 | | - |
33 | | -```bash |
34 | | -bun install |
35 | | -``` |
36 | | - |
37 | | -### Common commands |
38 | | - |
39 | | -```bash |
40 | | -# Lint all specs against the schema |
41 | | -bun run lint |
42 | | - |
43 | | -# Check what needs compiling |
44 | | -bun run compile status |
45 | | - |
46 | | -# Generate a prompt for a target |
47 | | -bun run compile prompt --target go |
48 | | - |
49 | | -# The prompt is written to dist/go/_compile-prompt.md |
50 | | -# Feed it to an LLM agent (e.g. Copilot CLI, Claude, etc.) |
51 | | -# The agent writes generated code to dist/go/ |
52 | | - |
53 | | -# After verifying the generated code works, lock the hashes |
54 | | -bun run compile lock --target go |
55 | | -``` |
56 | | - |
57 | | -## Repository structure |
58 | | - |
59 | | -``` |
60 | | -TUIKit/ |
61 | | - components/ Component specs, tests, and preview definitions |
62 | | - tokens/ Semantic design tokens (colors, icons, breakpoints) |
63 | | - targets/ Target language/framework definitions |
64 | | - docs/ Meta-schema and design foundations |
65 | | - scripts/ Compiler and linter CLIs |
66 | | - dist/ Compiled output per target (gitignored) |
67 | | -``` |
68 | | - |
69 | | -## Writing specs |
70 | | - |
71 | | -### Component spec |
72 | | - |
73 | | -Each component is a markdown file with YAML frontmatter and prose body: |
74 | | - |
75 | | -````yaml |
76 | | ---- |
77 | | -kind: component |
78 | | -name: MyComponent |
79 | | -description: One-line summary. |
80 | | -version: 1 |
81 | | -category: input # input | display | navigation | layout | feedback |
82 | | - |
83 | | -tokens: |
84 | | - colors: [textPrimary, selected] |
85 | | - icons: [iconPrompt] |
86 | | - |
87 | | -props: |
88 | | - label: |
89 | | - type: string |
90 | | - required: true |
91 | | - description: Display text. |
92 | | - |
93 | | -dependencies: |
94 | | - tokens: |
95 | | - - name: textPrimary |
96 | | - kind: color |
97 | | - usage: "Label text" |
98 | | - required: true |
99 | | - components: [] |
100 | | - |
101 | | -accessibility: |
102 | | - role: button |
103 | | - announce: |
104 | | - on_mount: "Button: {label}" |
105 | | ---- |
106 | | - |
107 | | -## Visual rules |
108 | | - |
109 | | -- Label text MUST use the `textPrimary` color token |
110 | | -- Active state MUST use the `selected` color token |
111 | | - |
112 | | -## Rendering example |
113 | | - |
114 | | -Given label: "Click me" |
115 | | - |
116 | | -``` |
117 | | -Click me |
118 | | -``` |
119 | | - |
120 | | -## Dependencies |
121 | | - |
122 | | -| Dependency | Kind | Usage | Required | |
123 | | -|------------|------|-------|----------| |
124 | | -| `textPrimary` | color | Label text | Yes | |
125 | | -| `selected` | color | Active state | Yes | |
126 | | -```` |
127 | | - |
128 | | -### Test spec |
129 | | - |
130 | | -Test specs live alongside component specs and use a block-based format: |
131 | | - |
132 | | -```markdown |
133 | | ---- |
134 | | -kind: test |
135 | | -component: MyComponent |
136 | | -version: 1 |
137 | | ---- |
138 | | - |
139 | | -## renders label text |
140 | | - |
141 | | -`props |
142 | | -label: "Hello" |
143 | | -` |
144 | | - |
145 | | -`expect |
146 | | -Hello |
147 | | -` |
148 | | -``` |
149 | | - |
150 | | -See `docs/schema.md` for the full format reference, including `input`, `state`, |
151 | | -`style`, and `accessibility` test blocks. |
152 | | - |
153 | | -### Conformance language |
154 | | - |
155 | | -All normative sections (Visual rules, Behavior, Edge cases) use |
156 | | -[RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) keywords: |
157 | | - |
158 | | -- **MUST** — absolute requirement |
159 | | -- **SHOULD** — strong recommendation |
160 | | -- **MAY** — optional behavior |
161 | | -- **MUST NOT** — absolute prohibition |
162 | | - |
163 | | -## Compiling to a target |
164 | | - |
165 | | -### Available targets |
166 | | - |
167 | | -| Target | Language | Framework | File | |
168 | | -| -------- | ---------- | ---------------------- | ------------------- | |
169 | | -| `go` | Go | Bubbletea + Lipgloss | `targets/go.md` | |
170 | | -| `node` | TypeScript | Ink + React (Node.js) | `targets/node.md` | |
171 | | -| `bun` | TypeScript | OpenTUI + React (Bun) | `targets/bun.md` | |
172 | | -| `rust` | Rust | Ratatui + Crossterm | `targets/rust.md` | |
173 | | - |
174 | | -### Workflow |
175 | | - |
176 | | -```bash |
177 | | -# 1. See what's changed |
178 | | -bun run compile status |
179 | | - |
180 | | -# 2. Generate the compilation prompt |
181 | | -bun run compile prompt --target go |
182 | | - |
183 | | -# 3. Feed dist/go/_compile-prompt.md to an LLM agent |
184 | | -# The agent generates code into dist/go/ |
185 | | - |
186 | | -# 4. Verify: run tests, check the demo CLI |
187 | | -cd dist/go && go test ./... && go run ./cmd/demo |
188 | | - |
189 | | -# 5. Lock the hashes |
190 | | -bun run compile lock --target go |
191 | | -``` |
192 | | - |
193 | | -### Multi-pass compilation |
194 | | - |
195 | | -A single compilation pass across the full component suite (17 components + |
196 | | -tokens + demo) is usually not enough to reach production quality. We've found |
197 | | -that **2–3 passes** produce notably better results: |
198 | | - |
199 | | -| Pass | Focus | Typical outcome | |
200 | | -| ---- | ----- | --------------- | |
201 | | -| **1st** | Initial generation | All components scaffold correctly, most tests pass, demo wires up. Expect rough edges — missing edge cases, incomplete keybindings, demo wiring bugs. | |
202 | | -| **2nd** | Review & fix | Agent reviews its own output against specs, fixes test failures, fills in missing behavior, improves demo interactivity. Test count typically grows 30–50%. | |
203 | | -| **3rd** | Polish | Catches subtle spec violations, improves accessibility, hardens demo `--snapshot` smoke tests. Diminishing returns after this point. | |
204 | | - |
205 | | -To run a follow-up pass, generate a new prompt and tell the agent to review |
206 | | -and complete its existing work: |
207 | | - |
208 | | -```bash |
209 | | -# Generate a fresh prompt (it sees the current dist/ state) |
210 | | -bun run compile prompt --target go |
211 | | - |
212 | | -# Feed to the agent with instructions like: |
213 | | -# "Review your existing implementation against the specs. |
214 | | -# Fix any test failures, fill in missing behavior, |
215 | | -# and ensure all --snapshot smoke tests pass." |
216 | | -``` |
217 | | - |
218 | | -Each pass is fast because the agent builds on its own prior output rather than |
219 | | -starting from scratch. The demo's `--list` and `--snapshot` flags make it easy |
220 | | -for the agent to self-verify between passes. |
221 | | - |
222 | | -### Custom output directory |
223 | | - |
224 | | -By default, compiled code goes to `dist/`. Override with `--out`: |
225 | | - |
226 | | -```bash |
227 | | -# Output to a separate repo or directory |
228 | | -bun run compile prompt --target go --out ~/my-tuikit-go |
229 | | - |
230 | | -# The prompt and generated code go to ~/my-tuikit-go/go/ |
231 | | -``` |
232 | | - |
233 | | -### Adding a new target |
234 | | - |
235 | | -1. Create `targets/{name}.md` following the target spec format in `docs/schema.md` |
236 | | -2. Define: architecture pattern, type mapping, callback translation, state |
237 | | - machine pattern, token access, styling, composition, test pattern, key |
238 | | - mapping, dependencies, and demo CLI |
239 | | -3. Run `bun run compile status` — your target will show up with all specs dirty |
240 | | -4. Run `bun run compile prompt --target {name}` and compile |
241 | | - |
242 | | -## Linting |
243 | | - |
244 | | -```bash |
245 | | -# Lint all specs |
246 | | -bun run lint |
247 | | - |
248 | | -# Lint a single component |
249 | | -bun run lint --component Select |
250 | | - |
251 | | -# Show fix suggestions |
252 | | -bun run lint --fix |
253 | | - |
254 | | -# See all rules |
255 | | -bun run lint --help |
256 | | -``` |
257 | | - |
258 | | -The linter checks: |
259 | | - |
260 | | -- Required frontmatter fields and valid values (zod schemas) |
261 | | -- Naming conventions (PascalCase components, camelCase props) |
262 | | -- RFC 2119 keyword usage in normative sections |
263 | | -- ARIA accessibility structure for interactive components |
264 | | -- Token cross-references resolve to known tokens |
265 | | -- Required body sections (Visual rules, Rendering example, Dependencies) |
266 | | -- Test specs reference existing components |
267 | | -- Broken internal markdown links |
268 | | - |
269 | | -Rule definitions live in `scripts/lint-rules.ts` — edit that file to add or |
270 | | -change rules, severities, and fix hints. |
271 | | - |
272 | | -## CI checks |
273 | | - |
274 | | -The GitHub Actions workflow (`.github/workflows/specs-ci.yml`) runs on every PR: |
275 | | - |
276 | | -1. **Spec lint** — `bun run lint` |
277 | | -2. **Compiler health** — `bun run compile status` for each target |
278 | | -3. **Prompt smoke test** — `bun run compile prompt` for each target |
279 | | -4. **No generated output committed** — ensures `dist/` is not tracked |
280 | | -5. **Changed-spec completeness** — if `{Name}.md` changes, matching `.test.md` and `.preview.md` must also change |
281 | | - |
282 | | -## Design principles |
283 | | - |
284 | | -- **Specs capture intent, not implementation** — ~95% behavioral intent vs. |
285 | | - ~5% framework hints. This lets agents generate idiomatic code per framework |
286 | | - rather than awkward transliterations. |
287 | | - |
288 | | -- **Color tokens define meaning, not color values** — tokens like `textPrimary` |
289 | | - and `selected` define UI roles. The color engine (Rampa, hardcoded hex, |
290 | | - ANSI palette) is an implementation detail per target. |
291 | | - |
292 | | -- **Layout is out of scope** — specs define behavior and semantic tokens. |
293 | | - Spacing, padding, and spatial polish are per-target decisions (similar to |
294 | | - headless UI libraries like Radix or Base UI). |
295 | | - |
296 | | -- **Lock files enable incremental compilation** — only dirty specs trigger |
297 | | - regeneration. Schema changes invalidate everything. Lock files are gitignored; |
298 | | - a fresh clone starts with everything dirty. |
299 | | - |
300 | | -For TUI design foundations — color systems, typography, iconography, layout |
301 | | -grids, accessibility patterns, keybinding conventions, and buffer management — |
302 | | -see [`docs/foundations.md`](docs/foundations.md). |
| 1 | +@- |
0 commit comments