openuispec 0.1.19 → 0.1.21
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.
- package/README.md +2 -2
- package/cli/init.ts +48 -211
- package/examples/todo-orbit/CLAUDE.md +32 -84
- package/examples/todo-orbit/openuispec/screens/analytics.yaml +1 -0
- package/examples/todo-orbit/openuispec/screens/home.yaml +1 -0
- package/examples/todo-orbit/openuispec/screens/settings.yaml +1 -0
- package/package.json +1 -1
- package/schema/defs/action.schema.json +1 -1
- package/schema/screen.schema.json +9 -0
- package/spec/openuispec-v0.1.md +53 -15
package/README.md
CHANGED
|
@@ -96,7 +96,7 @@ openuispec/
|
|
|
96
96
|
│ │ └── icons.schema.json # Icon token schema
|
|
97
97
|
│ ├── defs/
|
|
98
98
|
│ │ ├── common.schema.json # Shared types (icons, badges, etc.)
|
|
99
|
-
│ │ ├── action.schema.json #
|
|
99
|
+
│ │ ├── action.schema.json # 14 action types (discriminated union)
|
|
100
100
|
│ │ ├── data-binding.schema.json # Data sources, state, params
|
|
101
101
|
│ │ ├── adaptive.schema.json # Adaptive override pattern
|
|
102
102
|
│ │ └── validation.schema.json # Validation rule definitions
|
|
@@ -210,7 +210,7 @@ Paths are relative to `openuispec.yaml`. The `.openuispec-state.json` file is st
|
|
|
210
210
|
| 6. Navigation flows | Multi-screen journeys with transitions and progress |
|
|
211
211
|
| 7. Platform adaptation | Per-target overrides for iOS, Android, Web |
|
|
212
212
|
| 8. AI generation contract | Compliance levels (MUST/SHOULD/MAY), validation, drift detection |
|
|
213
|
-
| 9. Action system |
|
|
213
|
+
| 9. Action system | 14 action types, composition, optimistic updates |
|
|
214
214
|
| 10. Data binding & state | Sources, paths, format expressions, reactivity, caching |
|
|
215
215
|
| 11. Internationalization | Locale files, `$t:` references, ICU MessageFormat, RTL, platform mapping |
|
|
216
216
|
| 12. Custom contract extensions | `x_` prefixed domain-specific contracts, registration, dependencies |
|
package/cli/init.ts
CHANGED
|
@@ -128,7 +128,7 @@ function specReadmeTemplate(name: string, targets: string[]): string {
|
|
|
128
128
|
|
|
129
129
|
This directory contains the **OpenUISpec** semantic UI specification for **${name}**.
|
|
130
130
|
|
|
131
|
-
|
|
131
|
+
**Start here:** read \`openuispec.yaml\` — it defines the project structure, data model, API endpoints, and generation targets (**${targetList}**).
|
|
132
132
|
|
|
133
133
|
## Directory structure
|
|
134
134
|
|
|
@@ -137,113 +137,26 @@ OpenUISpec is a YAML-based format that describes your app's UI semantically —
|
|
|
137
137
|
| \`tokens/\` | Design tokens — colors, typography, spacing, elevation, motion, icons, themes |
|
|
138
138
|
| \`screens/\` | Screen definitions — one YAML file per screen |
|
|
139
139
|
| \`flows/\` | Navigation flows — multi-step user journeys |
|
|
140
|
-
| \`contracts/\` | Component contracts — standard extensions
|
|
140
|
+
| \`contracts/\` | Component contracts — standard extensions and custom (\`x_\` prefixed) |
|
|
141
141
|
| \`platform/\` | Platform overrides — per-target (iOS, Android, Web) behaviors |
|
|
142
142
|
| \`locales/\` | Localization — i18n strings (JSON, ICU MessageFormat) |
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
\`\`\`yaml
|
|
146
|
-
includes:
|
|
147
|
-
locales: "../../shared/locales" # resolved relative to openuispec.yaml
|
|
148
|
-
\`\`\`
|
|
149
|
-
|
|
150
|
-
## Getting started
|
|
151
|
-
|
|
152
|
-
**Start here:** read \`openuispec.yaml\` — it's the root manifest that defines the project structure, data model, API endpoints, and generation targets.
|
|
153
|
-
|
|
154
|
-
### New project (no existing UI code)
|
|
155
|
-
|
|
156
|
-
1. Define your data model and API endpoints in \`openuispec.yaml\`
|
|
157
|
-
2. Create token files in \`tokens/\` (colors, typography, spacing)
|
|
158
|
-
3. Create screen specs in \`screens/\` (one YAML per screen)
|
|
159
|
-
4. Create navigation flows in \`flows/\`
|
|
160
|
-
5. Ask AI to generate native code from the spec
|
|
161
|
-
|
|
162
|
-
### Existing project (adopting OpenUISpec)
|
|
163
|
-
|
|
164
|
-
1. Scan the codebase for existing UI screens
|
|
165
|
-
2. Create a stub for each screen in \`screens/\`:
|
|
166
|
-
\`\`\`yaml
|
|
167
|
-
screen_name:
|
|
168
|
-
semantic: "Brief description of what this screen does"
|
|
169
|
-
status: stub
|
|
170
|
-
layout:
|
|
171
|
-
type: scroll_vertical
|
|
172
|
-
\`\`\`
|
|
173
|
-
3. Extract design tokens (colors, fonts, spacing) into \`tokens/\`
|
|
174
|
-
4. Fill in \`data_model\` and \`api.endpoints\` in \`openuispec.yaml\`
|
|
175
|
-
5. Spec screens incrementally: \`stub\` → \`draft\` → \`ready\`
|
|
176
|
-
|
|
177
|
-
## Screen and flow status
|
|
178
|
-
|
|
179
|
-
- \`stub\` — placeholder, not yet specced. Drift detection skips these.
|
|
180
|
-
- \`draft\` — actively being specced. Tracked by drift.
|
|
181
|
-
- \`ready\` — fully specified (default if omitted). Tracked by drift.
|
|
182
|
-
|
|
183
|
-
## Learning OpenUISpec — where to find the docs
|
|
184
|
-
|
|
185
|
-
All documentation is included in the installed \`openuispec\` package. Search for it in this order:
|
|
186
|
-
1. **Local:** \`node_modules/openuispec/\` (if installed as a project dependency)
|
|
187
|
-
2. **Global:** run \`npm root -g\` to find the global prefix, then look in \`<prefix>/openuispec/\`
|
|
188
|
-
3. **Online fallback:** if the package is not installed at all, fetch from:
|
|
189
|
-
- \`https://openuispec.rsteam.uz/llms-full.txt\` — complete spec + all JSON schemas in one file
|
|
190
|
-
- \`https://openuispec.rsteam.uz/llms.txt\` — concise summary with links
|
|
191
|
-
|
|
192
|
-
Inside the package:
|
|
193
|
-
- **Full specification:** \`spec/openuispec-v0.1.md\`
|
|
194
|
-
- **Example app:** \`examples/taskflow/\`
|
|
195
|
-
- **JSON Schemas:** \`schema/\`
|
|
196
|
-
|
|
197
|
-
## Token file structure — root wrapper key required
|
|
198
|
-
|
|
199
|
-
Every token file must have a single root key matching the token type. Do NOT put properties at the top level.
|
|
200
|
-
|
|
201
|
-
\`\`\`yaml
|
|
202
|
-
# ✅ Correct — tokens/typography.yaml
|
|
203
|
-
typography:
|
|
204
|
-
font_family: ...
|
|
205
|
-
scale: ...
|
|
206
|
-
|
|
207
|
-
# ❌ Wrong — missing root wrapper key
|
|
208
|
-
font_family: ...
|
|
209
|
-
scale: ...
|
|
210
|
-
\`\`\`
|
|
211
|
-
|
|
212
|
-
Root keys: \`color\`, \`typography\`, \`spacing\`, \`elevation\`, \`motion\`, \`layout\`, \`themes\`, \`icons\`.
|
|
213
|
-
|
|
214
|
-
## File formats and schemas
|
|
215
|
-
|
|
216
|
-
**IMPORTANT:** Before creating or editing any spec file, read the corresponding JSON Schema to understand the valid structure. Do not guess the file format.
|
|
217
|
-
|
|
218
|
-
| File | Schema | Root key |
|
|
219
|
-
|------|--------|----------|
|
|
220
|
-
| \`openuispec.yaml\` | \`openuispec.schema.json\` | \`spec_version\` |
|
|
221
|
-
| \`screens/*.yaml\` | \`screen.schema.json\` | \`<screen_id>\` |
|
|
222
|
-
| \`flows/*.yaml\` | \`flow.schema.json\` | \`<flow_id>\` |
|
|
223
|
-
| \`platform/*.yaml\` | \`platform.schema.json\` | \`platform\` |
|
|
224
|
-
| \`locales/*.json\` | \`locale.schema.json\` | (object) |
|
|
225
|
-
| \`contracts/<name>.yaml\` | \`contract.schema.json\` | \`<contract_name>\` |
|
|
226
|
-
| \`contracts/x_*.yaml\` | \`custom-contract.schema.json\` | \`<x_name>\` |
|
|
227
|
-
| \`tokens/color.yaml\` | \`tokens/color.schema.json\` | \`color\` |
|
|
228
|
-
| \`tokens/typography.yaml\` | \`tokens/typography.schema.json\` | \`typography\` |
|
|
229
|
-
| \`tokens/spacing.yaml\` | \`tokens/spacing.schema.json\` | \`spacing\` |
|
|
230
|
-
| \`tokens/elevation.yaml\` | \`tokens/elevation.schema.json\` | \`elevation\` |
|
|
231
|
-
| \`tokens/motion.yaml\` | \`tokens/motion.schema.json\` | \`motion\` |
|
|
232
|
-
| \`tokens/layout.yaml\` | \`tokens/layout.schema.json\` | \`layout\` |
|
|
233
|
-
| \`tokens/themes.yaml\` | \`tokens/themes.schema.json\` | \`themes\` |
|
|
234
|
-
| \`tokens/icons.yaml\` | \`tokens/icons.schema.json\` | \`icons\` |
|
|
235
|
-
|
|
236
|
-
All schemas are in \`schema/\` inside the installed package. Shared type definitions (actions, data-binding, adaptive, validation, common) are in \`schema/defs/\`.
|
|
144
|
+
## IMPORTANT — Read the specification before working with spec files
|
|
237
145
|
|
|
238
|
-
|
|
146
|
+
The spec format, file schemas, and generation rules are defined in the installed \`openuispec\` package.
|
|
147
|
+
You MUST read these reference files before creating, editing, or generating from any spec file.
|
|
148
|
+
Do NOT guess the file format — skipping this step will produce invalid YAML that fails validation.
|
|
239
149
|
|
|
240
|
-
|
|
150
|
+
**Find the package in this order:**
|
|
151
|
+
1. \`node_modules/openuispec/\` (project dependency)
|
|
152
|
+
2. Run \`npm root -g\` → \`<prefix>/openuispec/\` (global install)
|
|
153
|
+
3. Online: \`https://openuispec.rsteam.uz/llms-full.txt\` (if not installed)
|
|
241
154
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
-
|
|
245
|
-
|
|
246
|
-
|
|
155
|
+
**Reference files inside the package (read in this order):**
|
|
156
|
+
1. \`README.md\` — schema tables, file format reference, root keys
|
|
157
|
+
2. \`spec/openuispec-v0.1.md\` — full specification (contracts, layout, expressions, etc.)
|
|
158
|
+
3. \`examples/taskflow/\` — complete working example with all file types
|
|
159
|
+
4. \`schema/\` — JSON Schemas for validation
|
|
247
160
|
|
|
248
161
|
## CLI commands
|
|
249
162
|
|
|
@@ -252,34 +165,11 @@ openuispec validate # Validate spec files against schemas
|
|
|
252
165
|
openuispec validate screens # Validate only screens
|
|
253
166
|
openuispec drift --target ${targets[0]} # Check for spec drift
|
|
254
167
|
openuispec drift --snapshot --target ${targets[0]} # Snapshot current state
|
|
255
|
-
openuispec drift --all # Include stubs in drift check
|
|
256
|
-
\`\`\`
|
|
257
|
-
|
|
258
|
-
## Targets and output directories
|
|
259
|
-
|
|
260
|
-
This project generates native code for: **${targetList}**
|
|
261
|
-
|
|
262
|
-
By default, drift stores state in \`generated/<target>/<project>/\`. To point targets to your actual code directories, add \`output_dir\` to \`openuispec.yaml\`:
|
|
263
|
-
|
|
264
|
-
\`\`\`yaml
|
|
265
|
-
generation:
|
|
266
|
-
targets: [ios, android, web]
|
|
267
|
-
output_dir:
|
|
268
|
-
web: "../web-ui/"
|
|
269
|
-
android: "../kmp-ui/"
|
|
270
|
-
ios: "../kmp-ui/iosApp/"
|
|
271
168
|
\`\`\`
|
|
272
169
|
|
|
273
|
-
Paths are relative to \`openuispec.yaml\`.
|
|
274
|
-
|
|
275
170
|
## Learn more
|
|
276
171
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
- Full spec: \`spec/openuispec-v0.1.md\`
|
|
280
|
-
- Example app: \`examples/taskflow/\`
|
|
281
|
-
- JSON Schemas: \`schema/\`
|
|
282
|
-
- Online reference: \`https://openuispec.rsteam.uz/llms-full.txt\`
|
|
172
|
+
Docs: https://openuispec.rsteam.uz
|
|
283
173
|
`;
|
|
284
174
|
}
|
|
285
175
|
|
|
@@ -292,26 +182,47 @@ function aiRulesBlock(specDir: string, targets: string[]): string {
|
|
|
292
182
|
# Spec files are the single source of truth for all UI across platforms.
|
|
293
183
|
# Targets: ${targetList}
|
|
294
184
|
|
|
185
|
+
## IMPORTANT — Read the specification before working with spec files
|
|
186
|
+
|
|
187
|
+
The spec format, file schemas, and generation rules are defined in the installed \`openuispec\` package.
|
|
188
|
+
You MUST read the reference files listed below before creating, editing, or generating from any spec file.
|
|
189
|
+
Do NOT guess the file format — skipping this step will produce invalid YAML that fails validation.
|
|
190
|
+
|
|
191
|
+
**Find the package in this order:**
|
|
192
|
+
1. \`node_modules/openuispec/\` (project dependency)
|
|
193
|
+
2. Run \`npm root -g\` → \`<prefix>/openuispec/\` (global install)
|
|
194
|
+
3. Online: \`https://openuispec.rsteam.uz/llms-full.txt\` (if not installed)
|
|
195
|
+
|
|
196
|
+
**Reference files inside the package (read in this order):**
|
|
197
|
+
1. \`README.md\` — schema tables, file format reference, root wrapper keys
|
|
198
|
+
2. \`spec/openuispec-v0.1.md\` — full specification (contracts, layout, expressions, adaptive, etc.)
|
|
199
|
+
3. \`examples/taskflow/\` — complete working example with all file types
|
|
200
|
+
4. \`schema/\` — JSON Schemas for every file type
|
|
201
|
+
|
|
202
|
+
These files are updated with each package version. Always read from the installed package,
|
|
203
|
+
not from cached or memorized content, to ensure you use the latest spec.
|
|
204
|
+
|
|
295
205
|
## What is OpenUISpec
|
|
296
206
|
OpenUISpec is a YAML-based spec format that describes an app's UI semantically — tokens, screens, flows, and platform overrides. AI reads the spec and generates native code (SwiftUI, Compose, React). AI reads native code and updates the spec. The spec is the sync layer between platforms.
|
|
297
207
|
|
|
298
208
|
## Spec location
|
|
299
209
|
- Spec root: \`${specDir}/\`
|
|
300
210
|
- Manifest: \`${specDir}/openuispec.yaml\` — always read this first.
|
|
301
|
-
- Tokens: \`${specDir}/tokens/\`
|
|
302
|
-
- Screens: \`${specDir}/screens/\`
|
|
303
|
-
- Flows: \`${specDir}/flows/\`
|
|
304
|
-
- Contracts: \`${specDir}/contracts/\`
|
|
305
|
-
- Platform: \`${specDir}/platform/\`
|
|
306
|
-
- Locales: \`${specDir}/locales/\`
|
|
211
|
+
- Tokens: \`${specDir}/tokens/\`
|
|
212
|
+
- Screens: \`${specDir}/screens/\`
|
|
213
|
+
- Flows: \`${specDir}/flows/\`
|
|
214
|
+
- Contracts: \`${specDir}/contracts/\`
|
|
215
|
+
- Platform: \`${specDir}/platform/\`
|
|
216
|
+
- Locales: \`${specDir}/locales/\`
|
|
307
217
|
|
|
308
|
-
**Note:** These are the default paths. Actual paths are in \`includes:\` in \`openuispec.yaml\` and may use relative paths
|
|
218
|
+
**Note:** These are the default paths. Actual paths are in \`includes:\` in \`openuispec.yaml\` and may use relative paths. Always read \`openuispec.yaml\` to find the real directories.
|
|
309
219
|
|
|
310
220
|
## If spec directories are empty (first-time setup)
|
|
311
221
|
This means the project has existing UI code but hasn't been specced yet. Your job:
|
|
312
222
|
|
|
313
|
-
1. **
|
|
314
|
-
2. **
|
|
223
|
+
1. **Read the spec first** — find and read \`spec/openuispec-v0.1.md\` from the installed package.
|
|
224
|
+
2. **Find existing screens** — scan the codebase for UI screen files.
|
|
225
|
+
3. **Create stubs** — for each screen, create \`${specDir}/screens/<name>.yaml\` with:
|
|
315
226
|
\`\`\`yaml
|
|
316
227
|
screen_name:
|
|
317
228
|
semantic: "Brief description of what this screen does"
|
|
@@ -319,14 +230,8 @@ This means the project has existing UI code but hasn't been specced yet. Your jo
|
|
|
319
230
|
layout:
|
|
320
231
|
type: scroll_vertical
|
|
321
232
|
\`\`\`
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
5. **Spec screens on demand** — when the user asks to spec a screen, read the native code, create a full spec, and change \`status: draft\` → \`ready\`.
|
|
325
|
-
|
|
326
|
-
## Screen and flow status
|
|
327
|
-
- \`stub\` — placeholder, not yet specced. Drift detection skips these.
|
|
328
|
-
- \`draft\` — actively being specced. Tracked by drift.
|
|
329
|
-
- \`ready\` — fully specified (default if omitted). Tracked by drift.
|
|
233
|
+
4. **Extract tokens** — scan for colors, fonts, spacing and create files in \`${specDir}/tokens/\`.
|
|
234
|
+
5. **Update the manifest** — fill in \`data_model\` and \`api.endpoints\` in \`${specDir}/openuispec.yaml\`.
|
|
330
235
|
|
|
331
236
|
## Making UI changes
|
|
332
237
|
1. Read the relevant spec files before modifying any UI code.
|
|
@@ -340,74 +245,6 @@ This means the project has existing UI code but hasn't been specced yet. Your jo
|
|
|
340
245
|
2. Run \`openuispec drift --snapshot --target <target>\` for each affected platform.
|
|
341
246
|
3. Run \`openuispec drift\` to verify no untracked drift remains.
|
|
342
247
|
|
|
343
|
-
## Learning OpenUISpec — where to find the docs
|
|
344
|
-
All documentation is in the installed \`openuispec\` package. Search in this order:
|
|
345
|
-
1. **Local:** \`node_modules/openuispec/\` (project dependency)
|
|
346
|
-
2. **Global:** run \`npm root -g\` to get the global prefix, then look in \`<prefix>/openuispec/\`
|
|
347
|
-
3. **Online fallback:** if not installed, fetch from:
|
|
348
|
-
- \`https://openuispec.rsteam.uz/llms-full.txt\` — complete spec + all JSON schemas
|
|
349
|
-
- \`https://openuispec.rsteam.uz/llms.txt\` — concise summary with links
|
|
350
|
-
|
|
351
|
-
Inside the package:
|
|
352
|
-
1. **Full specification:** \`spec/openuispec-v0.1.md\` — the complete spec (read this to understand the format)
|
|
353
|
-
2. **Example app:** \`examples/taskflow/\` — a complete working app with all file types
|
|
354
|
-
3. **JSON Schemas:** \`schema/\` — validation schemas that define the exact structure of every file type
|
|
355
|
-
|
|
356
|
-
## Token file structure — root wrapper key required
|
|
357
|
-
Every token file must have a single root key matching the token type. Do NOT put properties at the top level.
|
|
358
|
-
- \`tokens/color.yaml\` → root key: \`color\`
|
|
359
|
-
- \`tokens/typography.yaml\` → root key: \`typography\`
|
|
360
|
-
- \`tokens/spacing.yaml\` → root key: \`spacing\`
|
|
361
|
-
- \`tokens/elevation.yaml\` → root key: \`elevation\`
|
|
362
|
-
- \`tokens/motion.yaml\` → root key: \`motion\`
|
|
363
|
-
- \`tokens/layout.yaml\` → root key: \`layout\`
|
|
364
|
-
- \`tokens/themes.yaml\` → root key: \`themes\`
|
|
365
|
-
- \`tokens/icons.yaml\` → root key: \`icons\`
|
|
366
|
-
|
|
367
|
-
## File formats and schemas — read before creating spec files
|
|
368
|
-
Before creating or editing any spec file, read the corresponding JSON Schema. Do not guess the file format.
|
|
369
|
-
|
|
370
|
-
| File | Schema (in \`schema/\` inside the installed package) | Root key |
|
|
371
|
-
|------|--------|----------|
|
|
372
|
-
| \`openuispec.yaml\` | \`openuispec.schema.json\` | \`spec_version\` |
|
|
373
|
-
| \`screens/*.yaml\` | \`screen.schema.json\` | \`<screen_id>\` |
|
|
374
|
-
| \`flows/*.yaml\` | \`flow.schema.json\` | \`<flow_id>\` |
|
|
375
|
-
| \`platform/*.yaml\` | \`platform.schema.json\` | \`platform\` |
|
|
376
|
-
| \`locales/*.json\` | \`locale.schema.json\` | (object) |
|
|
377
|
-
| \`contracts/<name>.yaml\` | \`contract.schema.json\` | \`<contract_name>\` |
|
|
378
|
-
| \`contracts/x_*.yaml\` | \`custom-contract.schema.json\` | \`<x_name>\` |
|
|
379
|
-
| \`tokens/color.yaml\` | \`tokens/color.schema.json\` | \`color\` |
|
|
380
|
-
| \`tokens/typography.yaml\` | \`tokens/typography.schema.json\` | \`typography\` |
|
|
381
|
-
| \`tokens/spacing.yaml\` | \`tokens/spacing.schema.json\` | \`spacing\` |
|
|
382
|
-
| \`tokens/elevation.yaml\` | \`tokens/elevation.schema.json\` | \`elevation\` |
|
|
383
|
-
| \`tokens/motion.yaml\` | \`tokens/motion.schema.json\` | \`motion\` |
|
|
384
|
-
| \`tokens/layout.yaml\` | \`tokens/layout.schema.json\` | \`layout\` |
|
|
385
|
-
| \`tokens/themes.yaml\` | \`tokens/themes.schema.json\` | \`themes\` |
|
|
386
|
-
| \`tokens/icons.yaml\` | \`tokens/icons.schema.json\` | \`icons\` |
|
|
387
|
-
|
|
388
|
-
Shared type definitions (actions, data-binding, adaptive, validation, common) are in \`schema/defs/\`.
|
|
389
|
-
|
|
390
|
-
Workflow: read the schema → read an example from \`examples/taskflow/\` → create the YAML → run \`openuispec validate\`.
|
|
391
|
-
|
|
392
|
-
## Spec format reference
|
|
393
|
-
- 7 contract families: nav_container, surface, action_trigger, input_field, data_display, collection, feedback
|
|
394
|
-
- Custom contracts: prefixed with \`x_\` (e.g., \`x_media_player\`)
|
|
395
|
-
- Data binding: \`$data:\`, \`$state:\`, \`$param:\`, \`$t:\` prefixes
|
|
396
|
-
- Actions: typed objects (navigate, api_call, set_state, confirm, sequence, feedback, etc.)
|
|
397
|
-
- Adaptive layout: size classes (compact, regular, expanded) with per-section overrides
|
|
398
|
-
|
|
399
|
-
## Output directories
|
|
400
|
-
Drift tracks spec changes per target. By default state is stored in \`generated/<target>/<project>/\`.
|
|
401
|
-
To map targets to actual code directories, set \`generation.output_dir\` in \`openuispec.yaml\`:
|
|
402
|
-
\`\`\`yaml
|
|
403
|
-
generation:
|
|
404
|
-
output_dir:
|
|
405
|
-
web: "../web-ui/"
|
|
406
|
-
android: "../kmp-ui/"
|
|
407
|
-
ios: "../kmp-ui/iosApp/"
|
|
408
|
-
\`\`\`
|
|
409
|
-
Paths are relative to \`openuispec.yaml\`. The \`.openuispec-state.json\` file is stored inside each output directory.
|
|
410
|
-
|
|
411
248
|
## CLI commands
|
|
412
249
|
- \`openuispec init\` — scaffold a new spec project
|
|
413
250
|
- \`openuispec validate [group...]\` — validate spec files against schemas
|
|
@@ -4,26 +4,47 @@
|
|
|
4
4
|
# Spec files are the single source of truth for all UI across platforms.
|
|
5
5
|
# Targets: "ios", "android", "web"
|
|
6
6
|
|
|
7
|
+
## IMPORTANT — Read the specification before working with spec files
|
|
8
|
+
|
|
9
|
+
The spec format, file schemas, and generation rules are defined in the installed `openuispec` package.
|
|
10
|
+
You MUST read the reference files listed below before creating, editing, or generating from any spec file.
|
|
11
|
+
Do NOT guess the file format — skipping this step will produce invalid YAML that fails validation.
|
|
12
|
+
|
|
13
|
+
**Find the package in this order:**
|
|
14
|
+
1. `node_modules/openuispec/` (project dependency)
|
|
15
|
+
2. Run `npm root -g` → `<prefix>/openuispec/` (global install)
|
|
16
|
+
3. Online: `https://openuispec.rsteam.uz/llms-full.txt` (if not installed)
|
|
17
|
+
|
|
18
|
+
**Reference files inside the package (read in this order):**
|
|
19
|
+
1. `README.md` — schema tables, file format reference, root wrapper keys
|
|
20
|
+
2. `spec/openuispec-v0.1.md` — full specification (contracts, layout, expressions, adaptive, etc.)
|
|
21
|
+
3. `examples/taskflow/` — complete working example with all file types
|
|
22
|
+
4. `schema/` — JSON Schemas for every file type
|
|
23
|
+
|
|
24
|
+
These files are updated with each package version. Always read from the installed package,
|
|
25
|
+
not from cached or memorized content, to ensure you use the latest spec.
|
|
26
|
+
|
|
7
27
|
## What is OpenUISpec
|
|
8
28
|
OpenUISpec is a YAML-based spec format that describes an app's UI semantically — tokens, screens, flows, and platform overrides. AI reads the spec and generates native code (SwiftUI, Compose, React). AI reads native code and updates the spec. The spec is the sync layer between platforms.
|
|
9
29
|
|
|
10
30
|
## Spec location
|
|
11
31
|
- Spec root: `openuispec/`
|
|
12
32
|
- Manifest: `openuispec/openuispec.yaml` — always read this first.
|
|
13
|
-
- Tokens: `openuispec/tokens/`
|
|
14
|
-
- Screens: `openuispec/screens/`
|
|
15
|
-
- Flows: `openuispec/flows/`
|
|
16
|
-
- Contracts: `openuispec/contracts/`
|
|
17
|
-
- Platform: `openuispec/platform/`
|
|
18
|
-
- Locales: `openuispec/locales/`
|
|
33
|
+
- Tokens: `openuispec/tokens/`
|
|
34
|
+
- Screens: `openuispec/screens/`
|
|
35
|
+
- Flows: `openuispec/flows/`
|
|
36
|
+
- Contracts: `openuispec/contracts/`
|
|
37
|
+
- Platform: `openuispec/platform/`
|
|
38
|
+
- Locales: `openuispec/locales/`
|
|
19
39
|
|
|
20
|
-
**Note:** These are the default paths. Actual paths are in `includes:` in `openuispec.yaml` and may use relative paths
|
|
40
|
+
**Note:** These are the default paths. Actual paths are in `includes:` in `openuispec.yaml` and may use relative paths. Always read `openuispec.yaml` to find the real directories.
|
|
21
41
|
|
|
22
42
|
## If spec directories are empty (first-time setup)
|
|
23
43
|
This means the project has existing UI code but hasn't been specced yet. Your job:
|
|
24
44
|
|
|
25
|
-
1. **
|
|
26
|
-
2. **
|
|
45
|
+
1. **Read the spec first** — find and read `spec/openuispec-v0.1.md` from the installed package.
|
|
46
|
+
2. **Find existing screens** — scan the codebase for UI screen files.
|
|
47
|
+
3. **Create stubs** — for each screen, create `openuispec/screens/<name>.yaml` with:
|
|
27
48
|
```yaml
|
|
28
49
|
screen_name:
|
|
29
50
|
semantic: "Brief description of what this screen does"
|
|
@@ -31,14 +52,8 @@ This means the project has existing UI code but hasn't been specced yet. Your jo
|
|
|
31
52
|
layout:
|
|
32
53
|
type: scroll_vertical
|
|
33
54
|
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
5. **Spec screens on demand** — when the user asks to spec a screen, read the native code, create a full spec, and change `status: draft` → `ready`.
|
|
37
|
-
|
|
38
|
-
## Screen and flow status
|
|
39
|
-
- `stub` — placeholder, not yet specced. Drift detection skips these.
|
|
40
|
-
- `draft` — actively being specced. Tracked by drift.
|
|
41
|
-
- `ready` — fully specified (default if omitted). Tracked by drift.
|
|
55
|
+
4. **Extract tokens** — scan for colors, fonts, spacing and create files in `openuispec/tokens/`.
|
|
56
|
+
5. **Update the manifest** — fill in `data_model` and `api.endpoints` in `openuispec/openuispec.yaml`.
|
|
42
57
|
|
|
43
58
|
## Making UI changes
|
|
44
59
|
1. Read the relevant spec files before modifying any UI code.
|
|
@@ -52,73 +67,6 @@ This means the project has existing UI code but hasn't been specced yet. Your jo
|
|
|
52
67
|
2. Run `openuispec drift --snapshot --target <target>` for each affected platform.
|
|
53
68
|
3. Run `openuispec drift` to verify no untracked drift remains.
|
|
54
69
|
|
|
55
|
-
## Learning OpenUISpec — where to find the docs
|
|
56
|
-
All documentation is in the installed `openuispec` package. Search in this order:
|
|
57
|
-
1. **Local:** `node_modules/openuispec/` (project dependency)
|
|
58
|
-
2. **Global:** run `npm root -g` to get the global prefix, then look in `<prefix>/openuispec/`
|
|
59
|
-
3. **Online fallback:** if not installed, fetch from:
|
|
60
|
-
- `https://openuispec.rsteam.uz/llms-full.txt` — complete spec + all JSON schemas
|
|
61
|
-
- `https://openuispec.rsteam.uz/llms.txt` — concise summary with links
|
|
62
|
-
|
|
63
|
-
Inside the package:
|
|
64
|
-
1. **Full specification:** `spec/openuispec-v0.1.md` — the complete spec (read this to understand the format)
|
|
65
|
-
2. **Example app:** `examples/taskflow/` — a complete working app with all file types
|
|
66
|
-
3. **JSON Schemas:** `schema/` — validation schemas that define the exact structure of every file type
|
|
67
|
-
|
|
68
|
-
## Token file structure — root wrapper key required
|
|
69
|
-
Every token file must have a single root key matching the token type. Do NOT put properties at the top level.
|
|
70
|
-
- `tokens/color.yaml` → root key: `color`
|
|
71
|
-
- `tokens/typography.yaml` → root key: `typography`
|
|
72
|
-
- `tokens/spacing.yaml` → root key: `spacing`
|
|
73
|
-
- `tokens/elevation.yaml` → root key: `elevation`
|
|
74
|
-
- `tokens/motion.yaml` → root key: `motion`
|
|
75
|
-
- `tokens/layout.yaml` → root key: `layout`
|
|
76
|
-
- `tokens/themes.yaml` → root key: `themes`
|
|
77
|
-
- `tokens/icons.yaml` → root key: `icons`
|
|
78
|
-
|
|
79
|
-
## File formats and schemas — read before creating spec files
|
|
80
|
-
Before creating or editing any spec file, read the corresponding JSON Schema. Do not guess the file format.
|
|
81
|
-
|
|
82
|
-
| File | Schema (in `schema/` inside the installed package) | Root key |
|
|
83
|
-
|------|--------|----------|
|
|
84
|
-
| `openuispec.yaml` | `openuispec.schema.json` | `spec_version` |
|
|
85
|
-
| `screens/*.yaml` | `screen.schema.json` | `<screen_id>` |
|
|
86
|
-
| `flows/*.yaml` | `flow.schema.json` | `<flow_id>` |
|
|
87
|
-
| `platform/*.yaml` | `platform.schema.json` | `platform` |
|
|
88
|
-
| `locales/*.json` | `locale.schema.json` | (object) |
|
|
89
|
-
| `contracts/x_*.yaml` | `custom-contract.schema.json` | `contract` |
|
|
90
|
-
| `tokens/color.yaml` | `tokens/color.schema.json` | `color` |
|
|
91
|
-
| `tokens/typography.yaml` | `tokens/typography.schema.json` | `typography` |
|
|
92
|
-
| `tokens/spacing.yaml` | `tokens/spacing.schema.json` | `spacing` |
|
|
93
|
-
| `tokens/elevation.yaml` | `tokens/elevation.schema.json` | `elevation` |
|
|
94
|
-
| `tokens/motion.yaml` | `tokens/motion.schema.json` | `motion` |
|
|
95
|
-
| `tokens/layout.yaml` | `tokens/layout.schema.json` | `layout` |
|
|
96
|
-
| `tokens/themes.yaml` | `tokens/themes.schema.json` | `themes` |
|
|
97
|
-
| `tokens/icons.yaml` | `tokens/icons.schema.json` | `icons` |
|
|
98
|
-
|
|
99
|
-
Shared type definitions (actions, data-binding, adaptive, validation, common) are in `schema/defs/`.
|
|
100
|
-
|
|
101
|
-
Workflow: read the schema → read an example from `examples/taskflow/` → create the YAML → run `openuispec validate`.
|
|
102
|
-
|
|
103
|
-
## Spec format reference
|
|
104
|
-
- 7 contract families: nav_container, surface, action_trigger, input_field, data_display, collection, feedback
|
|
105
|
-
- Custom contracts: prefixed with `x_` (e.g., `x_media_player`)
|
|
106
|
-
- Data binding: `$data:`, `$state:`, `$param:`, `$t:` prefixes
|
|
107
|
-
- Actions: typed objects (navigate, api_call, set_state, confirm, sequence, feedback, etc.)
|
|
108
|
-
- Adaptive layout: size classes (compact, regular, expanded) with per-section overrides
|
|
109
|
-
|
|
110
|
-
## Output directories
|
|
111
|
-
Drift tracks spec changes per target. By default state is stored in `generated/<target>/<project>/`.
|
|
112
|
-
To map targets to actual code directories, set `generation.output_dir` in `openuispec.yaml`:
|
|
113
|
-
```yaml
|
|
114
|
-
generation:
|
|
115
|
-
output_dir:
|
|
116
|
-
web: "../web-ui/"
|
|
117
|
-
android: "../kmp-ui/"
|
|
118
|
-
ios: "../kmp-ui/iosApp/"
|
|
119
|
-
```
|
|
120
|
-
Paths are relative to `openuispec.yaml`. The `.openuispec-state.json` file is stored inside each output directory.
|
|
121
|
-
|
|
122
70
|
## CLI commands
|
|
123
71
|
- `openuispec init` — scaffold a new spec project
|
|
124
72
|
- `openuispec validate [group...]` — validate spec files against schemas
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://openuispec.org/schema/defs/action.schema.json",
|
|
4
4
|
"title": "OpenUISpec Action",
|
|
5
|
-
"description": "Discriminated union of the
|
|
5
|
+
"description": "Discriminated union of the 14 action types in OpenUISpec",
|
|
6
6
|
|
|
7
7
|
"type": "object",
|
|
8
8
|
"required": ["type"],
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"semantic": {
|
|
18
18
|
"type": "string"
|
|
19
19
|
},
|
|
20
|
+
"title": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"description": "Optional screen title for navigation bars and headers. When omitted, inferred from the first data_display title in the layout or the nav item label."
|
|
23
|
+
},
|
|
20
24
|
"status": {
|
|
21
25
|
"type": "string",
|
|
22
26
|
"enum": [
|
|
@@ -182,6 +186,11 @@
|
|
|
182
186
|
"input_type": {
|
|
183
187
|
"type": "string"
|
|
184
188
|
},
|
|
189
|
+
"render_hint": {
|
|
190
|
+
"type": "string",
|
|
191
|
+
"enum": ["dropdown", "segmented", "radio_group", "bottom_sheet"],
|
|
192
|
+
"description": "Optional hint for how an input_field with input_type select should render. When omitted, the platform default is used (e.g., Picker on iOS, ExposedDropdownMenuBox on Android, select on Web)."
|
|
193
|
+
},
|
|
185
194
|
"size": {
|
|
186
195
|
"type": "string"
|
|
187
196
|
},
|
package/spec/openuispec-v0.1.md
CHANGED
|
@@ -835,6 +835,12 @@ input_field:
|
|
|
835
835
|
suffix: { type: string, required: false }
|
|
836
836
|
icon: { type: icon_ref, required: false, position: [leading, trailing] }
|
|
837
837
|
clearable: { type: bool, default: false }
|
|
838
|
+
render_hint:
|
|
839
|
+
type: enum
|
|
840
|
+
values: [dropdown, segmented, radio_group, bottom_sheet]
|
|
841
|
+
required: false
|
|
842
|
+
condition: "input_type == select"
|
|
843
|
+
description: "Overrides the default platform widget for select fields. When omitted, the platform default is used."
|
|
838
844
|
|
|
839
845
|
states:
|
|
840
846
|
empty:
|
|
@@ -909,6 +915,7 @@ input_field:
|
|
|
909
915
|
text: { widget: "TextField" }
|
|
910
916
|
multiline: { widget: "TextEditor" }
|
|
911
917
|
select: { widget: "Picker" }
|
|
918
|
+
select[segmented]: { widget: "Picker(style: .segmented)" }
|
|
912
919
|
toggle: { widget: "Toggle" }
|
|
913
920
|
slider: { widget: "Slider" }
|
|
914
921
|
date: { widget: "DatePicker" }
|
|
@@ -916,6 +923,7 @@ input_field:
|
|
|
916
923
|
text: { composable: "OutlinedTextField" }
|
|
917
924
|
multiline: { composable: "OutlinedTextField(singleLine=false)" }
|
|
918
925
|
select: { composable: "ExposedDropdownMenuBox" }
|
|
926
|
+
select[segmented]: { composable: "SegmentedButton / SingleChoiceSegmentedButtonRow" }
|
|
919
927
|
toggle: { composable: "Switch" }
|
|
920
928
|
slider: { composable: "Slider" }
|
|
921
929
|
date: { composable: "DatePicker" }
|
|
@@ -923,6 +931,7 @@ input_field:
|
|
|
923
931
|
text: { element: "input", type: "text" }
|
|
924
932
|
multiline: { element: "textarea" }
|
|
925
933
|
select: { element: "select" }
|
|
934
|
+
select[segmented]: { element: "fieldset > input[type=radio]", styled: "segmented control" }
|
|
926
935
|
toggle: { element: "input", type: "checkbox", role: "switch" }
|
|
927
936
|
slider: { element: "input", type: "range" }
|
|
928
937
|
date: { element: "input", type: "date" }
|
|
@@ -1484,7 +1493,8 @@ Screens compose contracts into layouts. A screen never references platform widge
|
|
|
1484
1493
|
# Example: screens/order_detail.yaml
|
|
1485
1494
|
order_detail:
|
|
1486
1495
|
semantic: "Displays detailed information about a single order"
|
|
1487
|
-
|
|
1496
|
+
title: "$t:order_detail.title" # Optional — shown in nav bar / browser tab
|
|
1497
|
+
|
|
1488
1498
|
params:
|
|
1489
1499
|
order_id: { type: string, required: true }
|
|
1490
1500
|
|
|
@@ -1561,7 +1571,19 @@ order_detail:
|
|
|
1561
1571
|
|
|
1562
1572
|
### 5.1 Screen-level keys
|
|
1563
1573
|
|
|
1564
|
-
|
|
1574
|
+
Screen definitions support the following top-level keys alongside `semantic`, `layout`, `data`, `state`, `params`, `navigation`, and `surfaces`:
|
|
1575
|
+
|
|
1576
|
+
#### `title`
|
|
1577
|
+
|
|
1578
|
+
Optional display title for the screen, shown in navigation bars, browser tabs, and back-button labels. When omitted, generators should infer the title from the first `data_display` title in the layout, or from the corresponding `nav_container` item label.
|
|
1579
|
+
|
|
1580
|
+
```yaml
|
|
1581
|
+
settings:
|
|
1582
|
+
semantic: "Preferences screen for language and theme"
|
|
1583
|
+
title: "$t:settings.title" # "Preferences"
|
|
1584
|
+
```
|
|
1585
|
+
|
|
1586
|
+
Beyond these, screen files use several keys that modify how sections and contract instances behave. These keys are available on any section or contract instance within a screen's `sections:` array.
|
|
1565
1587
|
|
|
1566
1588
|
#### `tokens_override`
|
|
1567
1589
|
|
|
@@ -2648,8 +2670,9 @@ Format expressions transform values for display. They appear inside `{}` delimit
|
|
|
2648
2670
|
```
|
|
2649
2671
|
interpolation := '{' (piped_expr | computed_expr) '}'
|
|
2650
2672
|
piped_expr := data_path ('|' pipe)*
|
|
2651
|
-
pipe := operation ':' argument
|
|
2673
|
+
pipe := operation ':' argument ('.' option)?
|
|
2652
2674
|
operation := 'format' | 'map' | 'default'
|
|
2675
|
+
option := identifier # e.g., abbreviated, narrow
|
|
2653
2676
|
computed_expr := data_path comparator value '?' literal ':' literal
|
|
2654
2677
|
comparator := '==' | '!=' | '>' | '<' | '>=' | '<='
|
|
2655
2678
|
locale_ref := '$t:' locale_key
|
|
@@ -2705,18 +2728,18 @@ subtitle: "{item.quantity} × {item.unit_price | format:currency}"
|
|
|
2705
2728
|
|
|
2706
2729
|
**Built-in formatters:**
|
|
2707
2730
|
|
|
2708
|
-
| Formatter | Input | Output | Locale-aware |
|
|
2709
|
-
|
|
2710
|
-
| `currency` | number | "$1,234.56" | Yes |
|
|
2711
|
-
| `date` | date/datetime | "Mar 13, 2026" | Yes |
|
|
2712
|
-
| `date_relative` | date/datetime | "2 hours ago", "yesterday" | Yes |
|
|
2713
|
-
| `date_short` | date/datetime | "Mar 13" | Yes |
|
|
2714
|
-
| `time` | datetime | "3:45 PM" | Yes |
|
|
2715
|
-
| `number` | number | "1,234" | Yes |
|
|
2716
|
-
| `percentage` | number (0-1) | "45%" | No |
|
|
2717
|
-
| `status_label` | enum string | "In Progress" (title case) | No |
|
|
2718
|
-
| `pluralize` | number | "1 task" / "3 tasks" | Yes |
|
|
2719
|
-
| `file_size` | number (bytes) | "2.4 MB" | No |
|
|
2731
|
+
| Formatter | Input | Output | Locale-aware | Options |
|
|
2732
|
+
|-----------|-------|--------|-------------|---------|
|
|
2733
|
+
| `currency` | number | "$1,234.56" | Yes | — |
|
|
2734
|
+
| `date` | date/datetime | "Mar 13, 2026" | Yes | — |
|
|
2735
|
+
| `date_relative` | date/datetime | "2 hours ago", "yesterday" | Yes | `style`: full (default), abbreviated, narrow |
|
|
2736
|
+
| `date_short` | date/datetime | "Mar 13" | Yes | — |
|
|
2737
|
+
| `time` | datetime | "3:45 PM" | Yes | — |
|
|
2738
|
+
| `number` | number | "1,234" | Yes | — |
|
|
2739
|
+
| `percentage` | number (0-1) | "45%" | No | — |
|
|
2740
|
+
| `status_label` | enum string | "In Progress" (title case) | No | — |
|
|
2741
|
+
| `pluralize` | number | "1 task" / "3 tasks" | Yes | — |
|
|
2742
|
+
| `file_size` | number (bytes) | "2.4 MB" | No | — |
|
|
2720
2743
|
|
|
2721
2744
|
**Built-in mappers:**
|
|
2722
2745
|
|
|
@@ -2726,6 +2749,21 @@ subtitle: "{item.quantity} × {item.unit_price | format:currency}"
|
|
|
2726
2749
|
| `priority_to_severity` | priority enum → severity enum (e.g., "urgent" → "error") |
|
|
2727
2750
|
| `bool_to_label` | true/false → "Yes"/"No" (or custom mapping) |
|
|
2728
2751
|
|
|
2752
|
+
**Formatter options** — some built-in formatters accept an `options` parameter, passed with a dot suffix:
|
|
2753
|
+
|
|
2754
|
+
```yaml
|
|
2755
|
+
# Default style (full): "in 2 days", "3 hours ago"
|
|
2756
|
+
subtitle: "{task.due_date | format:date_relative}"
|
|
2757
|
+
|
|
2758
|
+
# Abbreviated: "in 2d", "3h ago"
|
|
2759
|
+
subtitle: "{task.due_date | format:date_relative.abbreviated}"
|
|
2760
|
+
|
|
2761
|
+
# Narrow: "2d", "3h"
|
|
2762
|
+
subtitle: "{task.due_date | format:date_relative.narrow}"
|
|
2763
|
+
```
|
|
2764
|
+
|
|
2765
|
+
When no option is provided, the default style is used. Generators must respect the option across all platforms to ensure consistent output.
|
|
2766
|
+
|
|
2729
2767
|
**Custom formatters and mappers** can be defined in the project manifest:
|
|
2730
2768
|
|
|
2731
2769
|
```yaml
|