aub-workspace 0.3.0

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.
Files changed (152) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +23 -0
  3. package/bin/aub-workspace.mjs +246 -0
  4. package/package.json +32 -0
  5. package/vendor/aub/apps/editor/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
  6. package/vendor/aub/apps/editor/dist/assets/angular-importer.lib-dB_jK4mR.js +32 -0
  7. package/vendor/aub/apps/editor/dist/assets/canvas-tools-CuYC7cA2.js +364 -0
  8. package/vendor/aub/apps/editor/dist/assets/design-bridge.lib-DJvaK6AX.js +1 -0
  9. package/vendor/aub/apps/editor/dist/assets/export-agent-prompt.lib-BsP0KNqo.js +2 -0
  10. package/vendor/aub/apps/editor/dist/assets/export-md.lib-DdmdeWgO.js +3 -0
  11. package/vendor/aub/apps/editor/dist/assets/handoff-package.lib-DDYpcEma.js +20 -0
  12. package/vendor/aub/apps/editor/dist/assets/implementation-report.lib-CmsSB_8s.js +1 -0
  13. package/vendor/aub/apps/editor/dist/assets/index-BCH-ek3h.js +2 -0
  14. package/vendor/aub/apps/editor/dist/assets/index-lAnc928Q.css +1 -0
  15. package/vendor/aub/apps/editor/dist/assets/index-vt1nM1M4.js +507 -0
  16. package/vendor/aub/apps/editor/dist/assets/jszip.min-CRfXyL92.js +12 -0
  17. package/vendor/aub/apps/editor/dist/assets/react-vendor-ByX9Pqse.js +40 -0
  18. package/vendor/aub/apps/editor/dist/brand/android-chrome-192x192.png +0 -0
  19. package/vendor/aub/apps/editor/dist/brand/android-chrome-512x512.png +0 -0
  20. package/vendor/aub/apps/editor/dist/brand/app-icon-1024.png +0 -0
  21. package/vendor/aub/apps/editor/dist/brand/app-icon-192.png +0 -0
  22. package/vendor/aub/apps/editor/dist/brand/app-icon-512.png +0 -0
  23. package/vendor/aub/apps/editor/dist/brand/apple-touch-icon.png +0 -0
  24. package/vendor/aub/apps/editor/dist/brand/aub-logo-mark.svg +28 -0
  25. package/vendor/aub/apps/editor/dist/brand/favicon-16x16.png +0 -0
  26. package/vendor/aub/apps/editor/dist/brand/favicon-32x32.png +0 -0
  27. package/vendor/aub/apps/editor/dist/brand/favicon-48x48.png +0 -0
  28. package/vendor/aub/apps/editor/dist/brand/favicon.ico +0 -0
  29. package/vendor/aub/apps/editor/dist/brand/favicon.svg +9 -0
  30. package/vendor/aub/apps/editor/dist/brand/maskable-icon-512.png +0 -0
  31. package/vendor/aub/apps/editor/dist/brand/mstile-150x150.png +0 -0
  32. package/vendor/aub/apps/editor/dist/brand/safari-pinned-tab.svg +8 -0
  33. package/vendor/aub/apps/editor/dist/browserconfig.xml +9 -0
  34. package/vendor/aub/apps/editor/dist/index.html +22 -0
  35. package/vendor/aub/apps/editor/dist/manifest.webmanifest +28 -0
  36. package/vendor/aub/apps/editor/dist/template-previews/admin-table.png +0 -0
  37. package/vendor/aub/apps/editor/dist/template-previews/booking.png +0 -0
  38. package/vendor/aub/apps/editor/dist/template-previews/calendar.png +0 -0
  39. package/vendor/aub/apps/editor/dist/template-previews/catalog.png +0 -0
  40. package/vendor/aub/apps/editor/dist/template-previews/chat.png +0 -0
  41. package/vendor/aub/apps/editor/dist/template-previews/checkout.png +0 -0
  42. package/vendor/aub/apps/editor/dist/template-previews/crm.png +0 -0
  43. package/vendor/aub/apps/editor/dist/template-previews/dashboard.png +0 -0
  44. package/vendor/aub/apps/editor/dist/template-previews/feed.png +0 -0
  45. package/vendor/aub/apps/editor/dist/template-previews/files.png +0 -0
  46. package/vendor/aub/apps/editor/dist/template-previews/kanban.png +0 -0
  47. package/vendor/aub/apps/editor/dist/template-previews/landing.png +0 -0
  48. package/vendor/aub/apps/editor/dist/template-previews/mail.png +0 -0
  49. package/vendor/aub/apps/editor/dist/template-previews/onboarding.png +0 -0
  50. package/vendor/aub/apps/editor/dist/template-previews/pricing.png +0 -0
  51. package/vendor/aub/apps/editor/dist/template-previews/product-detail.png +0 -0
  52. package/vendor/aub/apps/editor/dist/template-previews/settings.png +0 -0
  53. package/vendor/aub/apps/editor/dist/template-previews/wiki.png +0 -0
  54. package/vendor/aub/apps/mcp-server/dist/aub.js +15 -0
  55. package/vendor/aub/apps/mcp-server/dist/context.js +1 -0
  56. package/vendor/aub/apps/mcp-server/dist/http.js +123 -0
  57. package/vendor/aub/apps/mcp-server/dist/index.js +23 -0
  58. package/vendor/aub/apps/mcp-server/dist/repo.js +17 -0
  59. package/vendor/aub/apps/mcp-server/dist/schema.js +42 -0
  60. package/vendor/aub/apps/mcp-server/dist/server.js +80 -0
  61. package/vendor/aub/apps/mcp-server/dist/tools/approve-component-candidate.js +27 -0
  62. package/vendor/aub/apps/mcp-server/dist/tools/diff-blueprints.js +27 -0
  63. package/vendor/aub/apps/mcp-server/dist/tools/export-handoff.js +87 -0
  64. package/vendor/aub/apps/mcp-server/dist/tools/export-prompt.js +35 -0
  65. package/vendor/aub/apps/mcp-server/dist/tools/export-template-authoring-prompt.js +13 -0
  66. package/vendor/aub/apps/mcp-server/dist/tools/generate-template-from-source.js +25 -0
  67. package/vendor/aub/apps/mcp-server/dist/tools/get-aub-session.js +13 -0
  68. package/vendor/aub/apps/mcp-server/dist/tools/get-blueprint.js +28 -0
  69. package/vendor/aub/apps/mcp-server/dist/tools/get-project.js +45 -0
  70. package/vendor/aub/apps/mcp-server/dist/tools/get-workspace-status.js +10 -0
  71. package/vendor/aub/apps/mcp-server/dist/tools/import-design-bridge.js +62 -0
  72. package/vendor/aub/apps/mcp-server/dist/tools/list-blueprints.js +11 -0
  73. package/vendor/aub/apps/mcp-server/dist/tools/list-projects.js +11 -0
  74. package/vendor/aub/apps/mcp-server/dist/tools/lock-blueprint.js +33 -0
  75. package/vendor/aub/apps/mcp-server/dist/tools/migrate-blueprint.js +38 -0
  76. package/vendor/aub/apps/mcp-server/dist/tools/resolve-component.js +51 -0
  77. package/vendor/aub/apps/mcp-server/dist/tools/scaffold-blueprint.js +53 -0
  78. package/vendor/aub/apps/mcp-server/dist/tools/scan-project-ui.js +18 -0
  79. package/vendor/aub/apps/mcp-server/dist/tools/submit-report.js +48 -0
  80. package/vendor/aub/apps/mcp-server/dist/tools/update-aub-session.js +14 -0
  81. package/vendor/aub/apps/mcp-server/dist/tools/validate-blueprint.js +67 -0
  82. package/vendor/aub/apps/mcp-server/dist/tools/validate-project.js +74 -0
  83. package/vendor/aub/apps/mcp-server/dist/tools/write-blueprint.js +72 -0
  84. package/vendor/aub/apps/mcp-server/dist/workspace.js +138 -0
  85. package/vendor/aub/docs/agent-handoff.md +85 -0
  86. package/vendor/aub/docs/agent-handoff.zh-Hant.md +85 -0
  87. package/vendor/aub/docs/template-authoring-agent.md +86 -0
  88. package/vendor/aub/schema/aub-ci.schema.json +34 -0
  89. package/vendor/aub/schema/aub.registry.schema.json +118 -0
  90. package/vendor/aub/schema/design-bridge.schema.json +44 -0
  91. package/vendor/aub/schema/implementation-report.schema.json +93 -0
  92. package/vendor/aub/schema/project-types.ts +72 -0
  93. package/vendor/aub/schema/registry/components.json +118 -0
  94. package/vendor/aub/schema/types.js +13 -0
  95. package/vendor/aub/schema/types.ts +348 -0
  96. package/vendor/aub/schema/ui-blueprint-lock.schema.json +61 -0
  97. package/vendor/aub/schema/ui-blueprint.schema.json +1339 -0
  98. package/vendor/aub/schema/ui-project.schema.json +139 -0
  99. package/vendor/aub/scripts/agent-implementation-benchmark.lib.mjs +125 -0
  100. package/vendor/aub/scripts/angular-importer.lib.mjs +982 -0
  101. package/vendor/aub/scripts/check-editor-bundle-budget.mjs +36 -0
  102. package/vendor/aub/scripts/ci-verify.lib.mjs +256 -0
  103. package/vendor/aub/scripts/ci-verify.mjs +45 -0
  104. package/vendor/aub/scripts/create-authoring-kit.mjs +84 -0
  105. package/vendor/aub/scripts/create-implementation-report.mjs +24 -0
  106. package/vendor/aub/scripts/design-bridge.lib.d.mts +32 -0
  107. package/vendor/aub/scripts/design-bridge.lib.mjs +69 -0
  108. package/vendor/aub/scripts/diff-blueprint.lib.d.mts +18 -0
  109. package/vendor/aub/scripts/diff-blueprint.lib.mjs +148 -0
  110. package/vendor/aub/scripts/diff-blueprint.mjs +25 -0
  111. package/vendor/aub/scripts/export-agent-prompt.lib.d.mts +10 -0
  112. package/vendor/aub/scripts/export-agent-prompt.lib.mjs +160 -0
  113. package/vendor/aub/scripts/export-agent-prompt.mjs +79 -0
  114. package/vendor/aub/scripts/export-md.lib.d.mts +3 -0
  115. package/vendor/aub/scripts/export-md.lib.mjs +302 -0
  116. package/vendor/aub/scripts/export-md.mjs +43 -0
  117. package/vendor/aub/scripts/generate-registry-artifacts.lib.mjs +118 -0
  118. package/vendor/aub/scripts/generate-registry-artifacts.mjs +65 -0
  119. package/vendor/aub/scripts/generate-site-locales.mjs +545 -0
  120. package/vendor/aub/scripts/handoff-package.lib.d.mts +20 -0
  121. package/vendor/aub/scripts/handoff-package.lib.mjs +111 -0
  122. package/vendor/aub/scripts/implementation-report.lib.d.mts +21 -0
  123. package/vendor/aub/scripts/implementation-report.lib.mjs +97 -0
  124. package/vendor/aub/scripts/import-angular-component.mjs +72 -0
  125. package/vendor/aub/scripts/import-design-bridge.mjs +59 -0
  126. package/vendor/aub/scripts/lock-blueprint.lib.d.mts +23 -0
  127. package/vendor/aub/scripts/lock-blueprint.lib.mjs +58 -0
  128. package/vendor/aub/scripts/lock-blueprint.mjs +36 -0
  129. package/vendor/aub/scripts/migrate-blueprint-cli.mjs +28 -0
  130. package/vendor/aub/scripts/migrate-blueprint.d.mts +5 -0
  131. package/vendor/aub/scripts/migrate-blueprint.mjs +95 -0
  132. package/vendor/aub/scripts/package-workspace-cli.mjs +34 -0
  133. package/vendor/aub/scripts/project.lib.d.mts +44 -0
  134. package/vendor/aub/scripts/project.lib.mjs +175 -0
  135. package/vendor/aub/scripts/project.mjs +332 -0
  136. package/vendor/aub/scripts/registry.lib.d.mts +52 -0
  137. package/vendor/aub/scripts/registry.lib.mjs +222 -0
  138. package/vendor/aub/scripts/run-agent-implementation.mjs +423 -0
  139. package/vendor/aub/scripts/run-agent-readability.mjs +145 -0
  140. package/vendor/aub/scripts/run-ollama-prompt.mjs +30 -0
  141. package/vendor/aub/scripts/scaffold-blueprint.lib.d.mts +38 -0
  142. package/vendor/aub/scripts/scaffold-blueprint.lib.mjs +316 -0
  143. package/vendor/aub/scripts/scaffold-blueprint.mjs +86 -0
  144. package/vendor/aub/scripts/score-agent-implementation.mjs +27 -0
  145. package/vendor/aub/scripts/score-agent-readability.mjs +54 -0
  146. package/vendor/aub/scripts/sync-brand-assets.mjs +33 -0
  147. package/vendor/aub/scripts/validate-blueprint.lib.d.mts +14 -0
  148. package/vendor/aub/scripts/validate-blueprint.lib.mjs +136 -0
  149. package/vendor/aub/scripts/validate.mjs +128 -0
  150. package/vendor/aub/scripts/verify-implementation-report.mjs +36 -0
  151. package/vendor/aub/scripts/workspace-loop.lib.d.mts +17 -0
  152. package/vendor/aub/scripts/workspace-loop.lib.mjs +674 -0
@@ -0,0 +1,302 @@
1
+ // Pure-function Markdown exporter for UI Blueprint.
2
+ // Browser-safe: no Node imports, no shebang, no file I/O.
3
+ // The CLI wrapper (export-md.mjs) imports this and handles file operations.
4
+
5
+ /**
6
+ * Convert a Blueprint to Markdown suitable as agent prompt context.
7
+ * Sections:
8
+ * 1. Title + meta
9
+ * 2. Screen summary
10
+ * 3. Component hierarchy (tree)
11
+ * 4. Layout contract
12
+ * 5. Interaction rules
13
+ * 6. Responsive rules
14
+ * 7. Non-violable conditions
15
+ * 8. Acceptance checklist
16
+ * 9. Agent task
17
+ */
18
+ export function exportMarkdown(blueprint) {
19
+ const lines = [];
20
+
21
+ // 1. Title + meta
22
+ lines.push(`# ${blueprint.screen.name} — Implementation Brief`);
23
+ lines.push('');
24
+ lines.push(`> **Version:** ${blueprint.version}`);
25
+ lines.push(`> **Source:** this brief was generated by \`scripts/export-md.mjs\` from a UI Blueprint.`);
26
+ lines.push(`> **Target framework:** any (this brief is framework-agnostic)`);
27
+ lines.push('');
28
+ lines.push('This document is the Agent context export of a UI Blueprint. Read it top-to-bottom before implementing. The acceptance checklist at the bottom is the contract — every item must pass before this implementation is considered done.');
29
+ lines.push('');
30
+ lines.push('---');
31
+ lines.push('');
32
+
33
+ // 2. Screen summary
34
+ lines.push('## 1. Screen Summary');
35
+ lines.push('');
36
+ lines.push('| Field | Value |');
37
+ lines.push('|---|---|');
38
+ lines.push(`| **id** | \`${blueprint.screen.id}\` |`);
39
+ lines.push(`| **name** | ${blueprint.screen.name} |`);
40
+ lines.push(`| **type** | \`${blueprint.screen.type}\` |`);
41
+ lines.push(`| **platform** | \`${blueprint.screen.platform}\` |`);
42
+ lines.push(`| **primary_user_goal** | ${blueprint.screen.primary_user_goal} |`);
43
+ if (blueprint.screen.notes) lines.push(`| **notes** | ${blueprint.screen.notes} |`);
44
+ lines.push('');
45
+
46
+ // 3. Component hierarchy
47
+ lines.push('## 2. Component Hierarchy');
48
+ lines.push('');
49
+ lines.push('```');
50
+ lines.push(renderTree(blueprint));
51
+ lines.push('```');
52
+ lines.push('');
53
+ lines.push('Every node in the tree is a **registered semantic component type** (not a generic rectangle). When you implement, you MUST use components that map to the stated type — not visual stand-ins.');
54
+ lines.push('');
55
+ lines.push('---');
56
+ lines.push('');
57
+
58
+ // 4. Layout contract
59
+ lines.push('## 3. Layout Contract');
60
+ lines.push('');
61
+ const layoutNodes = blueprint.nodes.filter((n) => n.layout);
62
+ if (layoutNodes.length > 0) {
63
+ for (const n of layoutNodes) {
64
+ const desc = describeLayout(n);
65
+ if (desc) lines.push(`- **${n.name}** (\`${n.id}\`): ${desc}`);
66
+ }
67
+ } else {
68
+ lines.push('_No layout rules declared._');
69
+ }
70
+ lines.push('');
71
+ const placementNodes = blueprint.nodes.filter((node) => node.placements && Object.keys(node.placements).length > 0);
72
+ if (placementNodes.length > 0) {
73
+ lines.push('### Exact Viewport Geometry');
74
+ lines.push('');
75
+ lines.push('For children of a `freeform` container, these pixel values are authoritative at the declared viewport size. Preserve relative composition when interpolating between declared viewports.');
76
+ lines.push('');
77
+ lines.push('| Node | Viewport | x | y | width | height | z-index |');
78
+ lines.push('|---|---:|---:|---:|---:|---:|---:|');
79
+ for (const node of placementNodes) {
80
+ for (const viewport of blueprint.viewports) {
81
+ const placement = resolvePlacement(node, viewport.id);
82
+ if (!placement) continue;
83
+ lines.push(`| \`${node.id}\` | \`${viewport.id}\` | ${placement.x} | ${placement.y} | ${placement.width} | ${placement.height} | ${placement.z_index ?? 1} |`);
84
+ }
85
+ }
86
+ lines.push('');
87
+ }
88
+ lines.push('Use each container according to its declared mode: `auto` means flex/grid flow is the contract; `freeform` means child `placements` are the contract. Do not replace one mode with the other unless an acceptance item explicitly permits it.');
89
+ lines.push('');
90
+ lines.push('### Design System Tokens');
91
+ lines.push('');
92
+ renderDesignSystem(lines, blueprint.design_system);
93
+ lines.push('');
94
+ lines.push('---');
95
+ lines.push('');
96
+
97
+ // 5. Interaction rules
98
+ lines.push('## 4. Interaction Rules');
99
+ lines.push('');
100
+ if (blueprint.interactions.length > 0) {
101
+ lines.push('| Source | Trigger | Action | Target | Result |');
102
+ lines.push('|---|---|---|---|---|');
103
+ for (const i of blueprint.interactions) {
104
+ lines.push(`| \`${i.source_node_id}\` | \`${i.trigger}\` | \`${i.action}\` | ${i.target ? `\`${i.target}\`` : '—'} | ${i.result_state} |`);
105
+ }
106
+ } else {
107
+ lines.push('_No interactions declared._');
108
+ }
109
+ lines.push('');
110
+ lines.push('Every interaction declares its **trigger**, **action verb-noun**, and **observable result state**. Do not invent interactions not listed here.');
111
+ lines.push('');
112
+ lines.push('---');
113
+ lines.push('');
114
+
115
+ // 6. Responsive rules
116
+ lines.push('## 5. Responsive Rules');
117
+ lines.push('');
118
+ if (blueprint.responsive.length > 0) {
119
+ lines.push('| Viewport | Target | Rule | Effect |');
120
+ lines.push('|---|---|---|---|');
121
+ for (const r of blueprint.responsive) {
122
+ const changeText = Object.keys(r.changes || {}).length > 0
123
+ ? Object.entries(r.changes).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(', ')
124
+ : '_no change_';
125
+ lines.push(`| \`${r.viewport}\` | \`${r.target_node_id}\` | \`${r.rule}\` | ${changeText} |`);
126
+ }
127
+ } else {
128
+ lines.push('_No responsive rules declared._');
129
+ }
130
+ lines.push('');
131
+ lines.push('Every responsive decision is declared explicitly. The agent MUST NOT collapse the sidebar silently, hide the metric row, or change table to horizontal scroll without a declared rule.');
132
+ lines.push('');
133
+ lines.push('---');
134
+ lines.push('');
135
+
136
+ // 7. Non-violable conditions (from acceptance priority=blocker)
137
+ lines.push('## 6. Non-Violable Conditions');
138
+ lines.push('');
139
+ const blockers = blueprint.acceptance.filter((a) => a.priority === 'blocker');
140
+ if (blockers.length > 0) {
141
+ blockers.forEach((b, i) => {
142
+ lines.push(`${i + 1}. **${b.statement}** _(target: \`${b.target}\`)_`);
143
+ });
144
+ } else {
145
+ lines.push('_No blocker-level acceptance items declared._');
146
+ }
147
+ lines.push('');
148
+ lines.push('---');
149
+ lines.push('');
150
+
151
+ // 8. Acceptance checklist
152
+ lines.push('## 7. Acceptance Checklist');
153
+ lines.push('');
154
+ lines.push('This checklist is the contract. Every item must be verifiable. Mark each `[x]` only after manual or automated verification.');
155
+ lines.push('');
156
+ if (blueprint.acceptance.length > 0) {
157
+ const byType = groupBy(blueprint.acceptance, (a) => a.type);
158
+ let sectionNum = 0;
159
+ const typeOrder = ['layout', 'interaction', 'responsive', 'a11y', 'content', 'performance'];
160
+ for (const t of typeOrder) {
161
+ if (!byType[t] || byType[t].length === 0) continue;
162
+ sectionNum++;
163
+ const sectionNames = { layout: 'Layout', interaction: 'Interaction', responsive: 'Responsive', a11y: 'Accessibility', content: 'Content', performance: 'Performance' };
164
+ lines.push(`### ${sectionNames[t] ?? t} (${byType[t].length})`);
165
+ lines.push('');
166
+ for (const a of byType[t]) {
167
+ lines.push(`- [ ] **${a.id}** (${a.priority}) — ${a.statement} _[verify: \`${a.verification_method}\`]_`);
168
+ }
169
+ lines.push('');
170
+ }
171
+ lines.push(`**Total: ${blueprint.acceptance.length} verifiable items** (≥5 required by spec).`);
172
+ } else {
173
+ lines.push('_No acceptance items declared._');
174
+ }
175
+ lines.push('');
176
+ lines.push('---');
177
+ lines.push('');
178
+
179
+ // 9. Agent task
180
+ lines.push('## 8. Agent Task');
181
+ lines.push('');
182
+ lines.push(`You are implementing the ${blueprint.screen.name} described above. Your deliverable is:`);
183
+ lines.push('');
184
+ lines.push('1. A new file or route matching the component hierarchy in §2.');
185
+ lines.push('2. Implementation that respects the layout contract in §3.');
186
+ lines.push('3. Wiring of all interactions in §4.');
187
+ lines.push('4. Responsive behavior per §5.');
188
+ lines.push('5. A passing run of the acceptance checklist in §7 — every item verifiable, not vibes.');
189
+ lines.push('');
190
+ lines.push('### Implementation order (suggested, not strict)');
191
+ lines.push('');
192
+ lines.push('1. App shell + main containers (skeleton, no data)');
193
+ lines.push('2. Children nodes with hardcoded content');
194
+ lines.push('3. Wire primary interactions');
195
+ lines.push('4. Wire secondary interactions and edge cases');
196
+ lines.push('5. Add responsive rules');
197
+ lines.push('6. Add empty/loading/error states');
198
+ lines.push('7. Run accessibility audit, fix any issues');
199
+ lines.push('8. Run acceptance checklist, mark all items');
200
+ lines.push('');
201
+ lines.push('### Stop conditions');
202
+ lines.push('');
203
+ lines.push('- **Blocker found:** stop and report. Do not "fix forward" by relaxing the spec.');
204
+ lines.push('- **Acceptance item impossible as specified:** stop and report. Do not silently weaken the criterion.');
205
+ lines.push('- **Component type does not exist in your framework:** stop and report. Do not substitute a generic `<div>`.');
206
+ lines.push('');
207
+ lines.push('### Output format (when done)');
208
+ lines.push('');
209
+ lines.push('Report per acceptance item:');
210
+ lines.push('');
211
+ lines.push('```');
212
+ lines.push('[ ] acc_id — pass | fail | needs-review');
213
+ lines.push(' evidence: <screenshot path | file:line | dom query | interaction log>');
214
+ lines.push(' notes: <if any>');
215
+ lines.push('```');
216
+ lines.push('');
217
+ lines.push('If any item is `fail` or `needs-review`, the implementation is not done.');
218
+ lines.push('');
219
+
220
+ return lines.join('\n');
221
+ }
222
+
223
+ function renderTree(blueprint) {
224
+ const byId = new Map(blueprint.nodes.map((n) => [n.id, n]));
225
+ const root = blueprint.nodes.find((n) => n.parent_id === null) || blueprint.nodes[0];
226
+ if (!root) return '(empty)';
227
+ const lines = [];
228
+ walk(root, 0);
229
+ return lines.join('\n');
230
+
231
+ function walk(node, depth) {
232
+ const indent = ' '.repeat(depth);
233
+ const label = `${node.id} (${node.type})`;
234
+ const childCount = node.children?.length ?? 0;
235
+ lines.push(`${indent}${label}${childCount > 0 ? '' : ' [leaf]'}`);
236
+ if (Array.isArray(node.children)) {
237
+ for (const childId of node.children) {
238
+ const child = byId.get(childId);
239
+ if (child) walk(child, depth + 1);
240
+ }
241
+ }
242
+ }
243
+ }
244
+
245
+ function describeLayout(node) {
246
+ const l = node.layout;
247
+ const parts = [];
248
+ if (l.mode) parts.push(`mode: ${l.mode}`);
249
+ if (l.display) parts.push(`display: ${l.display}`);
250
+ if (l.direction) parts.push(`direction: ${l.direction}`);
251
+ if (l.grid?.columns) parts.push(`grid: ${l.grid.columns} cols${l.grid.template ? ` (${l.grid.template})` : ''}`);
252
+ if (l.gap) {
253
+ if (l.gap.x != null && l.gap.y != null) parts.push(`gap: ${l.gap.x}/${l.gap.y}px`);
254
+ else if (l.gap.x != null) parts.push(`gap-x: ${l.gap.x}px`);
255
+ else if (l.gap.y != null) parts.push(`gap-y: ${l.gap.y}px`);
256
+ }
257
+ if (l.padding) {
258
+ const p = l.padding;
259
+ if (p.top != null || p.bottom != null || p.left != null || p.right != null) {
260
+ parts.push(`padding: ${p.top ?? '-'}/${p.right ?? '-'}/${p.bottom ?? '-'}/${p.left ?? '-'}`);
261
+ }
262
+ }
263
+ if (l.width) parts.push(`width: ${l.width.value}${l.width.unit}`);
264
+ if (l.height) parts.push(`height: ${l.height.value}${l.height.unit}`);
265
+ return parts.length > 0 ? parts.join(', ') : null;
266
+ }
267
+
268
+ function resolvePlacement(node, viewportId) {
269
+ const placements = node.placements;
270
+ if (!placements) return null;
271
+ return placements[viewportId]
272
+ ?? placements.desktop
273
+ ?? placements.tablet
274
+ ?? placements.mobile
275
+ ?? placements.wide
276
+ ?? null;
277
+ }
278
+
279
+ function renderDesignSystem(lines, designSystem) {
280
+ if (!designSystem) {
281
+ lines.push('_No design system tokens declared._');
282
+ return;
283
+ }
284
+ lines.push(`Token set: **${designSystem.name}**`);
285
+ lines.push('');
286
+ lines.push('| Group | Token | Value |');
287
+ lines.push('|---|---|---|');
288
+ for (const group of ['colors', 'typography', 'spacing', 'radii', 'shadows']) {
289
+ for (const [name, value] of Object.entries(designSystem[group] ?? {})) {
290
+ lines.push(`| ${group} | \`${name}\` | \`${String(value).replaceAll('|', '\\|')}\` |`);
291
+ }
292
+ }
293
+ }
294
+
295
+ function groupBy(arr, keyFn) {
296
+ const out = {};
297
+ for (const x of arr) {
298
+ const k = keyFn(x);
299
+ (out[k] ??= []).push(x);
300
+ }
301
+ return out;
302
+ }
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ // CLI wrapper around the browser-safe Markdown exporter.
3
+ // Usage: node scripts/export-md.mjs <input.ui.json|input.ui.yaml> [output.ui.md]
4
+
5
+ import { readFile, writeFile } from 'node:fs/promises';
6
+ import { resolve, extname } from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import yaml from 'js-yaml';
9
+ import { exportMarkdown } from './export-md.lib.mjs';
10
+
11
+ export { exportMarkdown };
12
+
13
+ async function main() {
14
+ const args = process.argv.slice(2);
15
+ if (args.length < 1) {
16
+ console.error('Usage: node scripts/export-md.mjs <input.ui.json|input.ui.yaml> [output.ui.md]');
17
+ console.error(' node scripts/export-md.mjs - (read JSON from stdin)');
18
+ process.exit(2);
19
+ }
20
+
21
+ const inputText = args[0] === '-'
22
+ ? await readFile(0, 'utf8')
23
+ : await readFile(resolve(args[0]), 'utf8');
24
+ const ext = args[0] === '-' ? '.json' : extname(args[0]).toLowerCase();
25
+ const blueprint = ext === '.yaml' || ext === '.yml'
26
+ ? yaml.load(inputText)
27
+ : JSON.parse(inputText);
28
+ const markdown = exportMarkdown(blueprint);
29
+
30
+ if (args[1] && args[1] !== '-') {
31
+ await writeFile(resolve(args[1]), markdown, 'utf8');
32
+ console.error(`✓ wrote ${args[1]} (${markdown.length} bytes)`);
33
+ } else {
34
+ process.stdout.write(markdown);
35
+ }
36
+ }
37
+
38
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
39
+ main().catch((error) => {
40
+ console.error(error);
41
+ process.exit(1);
42
+ });
43
+ }
@@ -0,0 +1,118 @@
1
+ // Pure helpers that derive the schema enums and TypeScript unions from the
2
+ // component registry. The registry (schema/registry/components.json) is the
3
+ // single source of truth; this module computes everything else from it.
4
+
5
+ import { readFile } from 'node:fs/promises';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { dirname, resolve } from 'node:path';
8
+
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const ROOT = resolve(__dirname, '..');
11
+
12
+ export const REGISTRY_PATH = resolve(ROOT, 'schema', 'registry', 'components.json');
13
+ export const SCHEMA_PATH = resolve(ROOT, 'schema', 'ui-blueprint.schema.json');
14
+ export const TYPES_PATH = resolve(ROOT, 'schema', 'types.ts');
15
+
16
+ export const TYPES_BEGIN = '// <generated:component-types>';
17
+ export const TYPES_END = '// </generated:component-types>';
18
+
19
+ export async function loadCoreRegistry() {
20
+ return JSON.parse(await readFile(REGISTRY_PATH, 'utf8'));
21
+ }
22
+
23
+ /**
24
+ * Flatten the registry into ordered type lists, preserving category and
25
+ * in-category order so output is deterministic.
26
+ */
27
+ export function computeTypeLists(registry) {
28
+ const all = [];
29
+ const containers = [];
30
+ const leaves = [];
31
+ for (const category of registry.categories ?? []) {
32
+ for (const type of category.types ?? []) {
33
+ all.push(type.name);
34
+ if (type.isContainer) containers.push(type.name);
35
+ else leaves.push(type.name);
36
+ }
37
+ }
38
+ return { all, containers, leaves };
39
+ }
40
+
41
+ /** Group type names by registry category for readable, stable rendering. */
42
+ function typesByCategory(registry) {
43
+ return (registry.categories ?? []).map((category) => ({
44
+ id: category.id,
45
+ names: (category.types ?? []).map((t) => t.name),
46
+ }));
47
+ }
48
+
49
+ function renderUnionLines(groups, indent = ' ') {
50
+ return groups
51
+ .filter((g) => g.names.length > 0)
52
+ .map((g) => `${indent}| ${g.names.map((n) => `'${n}'`).join(' | ')}`)
53
+ .join('\n');
54
+ }
55
+
56
+ /** Produce the generated TypeScript block (between the sentinel markers). */
57
+ export function renderTypesSection(registry) {
58
+ const groups = typesByCategory(registry);
59
+ const lists = computeTypeLists(registry);
60
+ const containerGroups = (registry.categories ?? []).map((category) => ({
61
+ id: category.id,
62
+ names: (category.types ?? []).filter((t) => t.isContainer).map((t) => t.name),
63
+ }));
64
+
65
+ const componentUnion = renderUnionLines(groups);
66
+ const containerUnion = renderUnionLines(containerGroups, ' ');
67
+
68
+ return [
69
+ TYPES_BEGIN,
70
+ '/** Semantic component type — agents read this to know WHAT a node is. */',
71
+ 'export type ComponentType =',
72
+ `${componentUnion};`,
73
+ '',
74
+ 'export type ContainerComponentType = Extract<',
75
+ ' ComponentType,',
76
+ `${containerUnion}`,
77
+ '>;',
78
+ '',
79
+ 'export type LeafComponentType = Exclude<ComponentType, ContainerComponentType>;',
80
+ TYPES_END,
81
+ ].join('\n');
82
+ }
83
+
84
+ /**
85
+ * Return a new schema object with the generated core-type enums populated from
86
+ * the registry. Does not mutate the input.
87
+ */
88
+ export function applyEnumsToSchema(schema, lists) {
89
+ const next = structuredClone(schema);
90
+ const defs = next.$defs ?? {};
91
+ if (!defs.coreComponentType || !defs.coreContainerComponentType || !defs.coreLeafComponentType) {
92
+ throw new Error(
93
+ 'schema is missing coreComponentType / coreContainerComponentType / coreLeafComponentType $defs'
94
+ );
95
+ }
96
+ defs.coreComponentType.enum = [...lists.all];
97
+ defs.coreContainerComponentType.enum = [...lists.containers];
98
+ defs.coreLeafComponentType.enum = [...lists.leaves];
99
+ return next;
100
+ }
101
+
102
+ export function serializeSchema(schema) {
103
+ return `${JSON.stringify(schema, null, 2)}\n`;
104
+ }
105
+
106
+ /** Replace the generated block of an existing types.ts source string. */
107
+ export function replaceTypesSection(source, generatedBlock) {
108
+ const start = source.indexOf(TYPES_BEGIN);
109
+ const end = source.indexOf(TYPES_END);
110
+ if (start === -1 || end === -1 || end < start) {
111
+ throw new Error(
112
+ `types.ts is missing the generated markers ${TYPES_BEGIN} / ${TYPES_END}`
113
+ );
114
+ }
115
+ const before = source.slice(0, start);
116
+ const after = source.slice(end + TYPES_END.length);
117
+ return `${before}${generatedBlock}${after}`;
118
+ }
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ // Regenerate the schema core-type enums and the types.ts component-type block
3
+ // from schema/registry/components.json (the single source of truth).
4
+ //
5
+ // Usage:
6
+ // node scripts/generate-registry-artifacts.mjs # write artifacts
7
+ // node scripts/generate-registry-artifacts.mjs --check # fail if stale
8
+
9
+ import { readFile, writeFile } from 'node:fs/promises';
10
+ import {
11
+ SCHEMA_PATH,
12
+ TYPES_PATH,
13
+ loadCoreRegistry,
14
+ computeTypeLists,
15
+ renderTypesSection,
16
+ replaceTypesSection,
17
+ applyEnumsToSchema,
18
+ serializeSchema,
19
+ } from './generate-registry-artifacts.lib.mjs';
20
+
21
+ async function build() {
22
+ const registry = await loadCoreRegistry();
23
+ const lists = computeTypeLists(registry);
24
+
25
+ const schema = JSON.parse(await readFile(SCHEMA_PATH, 'utf8'));
26
+ const nextSchema = serializeSchema(applyEnumsToSchema(schema, lists));
27
+
28
+ const typesSource = await readFile(TYPES_PATH, 'utf8');
29
+ const nextTypes = replaceTypesSection(typesSource, renderTypesSection(registry));
30
+
31
+ return { nextSchema, nextTypes };
32
+ }
33
+
34
+ async function main() {
35
+ const check = process.argv.includes('--check');
36
+ const { nextSchema, nextTypes } = await build();
37
+
38
+ if (check) {
39
+ const [currentSchema, currentTypes] = await Promise.all([
40
+ readFile(SCHEMA_PATH, 'utf8'),
41
+ readFile(TYPES_PATH, 'utf8'),
42
+ ]);
43
+ const stale = [];
44
+ if (currentSchema !== nextSchema) stale.push('schema/ui-blueprint.schema.json');
45
+ if (currentTypes !== nextTypes) stale.push('schema/types.ts');
46
+ if (stale.length > 0) {
47
+ console.error('✗ generated artifacts are stale. Run `pnpm gen`. Out of date:');
48
+ for (const f of stale) console.error(` - ${f}`);
49
+ process.exit(1);
50
+ }
51
+ console.log('✓ generated artifacts are up to date');
52
+ return;
53
+ }
54
+
55
+ await Promise.all([
56
+ writeFile(SCHEMA_PATH, nextSchema, 'utf8'),
57
+ writeFile(TYPES_PATH, nextTypes, 'utf8'),
58
+ ]);
59
+ console.log('✓ wrote schema enums and types.ts component types from registry');
60
+ }
61
+
62
+ main().catch((err) => {
63
+ console.error(err.message ?? err);
64
+ process.exit(2);
65
+ });