@nodegx/mcp 0.1.0 → 0.2.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 (64) hide show
  1. package/dist/backend/cli.js +102959 -0
  2. package/dist/cloud-bundle.cjs +35311 -2409
  3. package/dist/kit-extract.cjs +732 -545
  4. package/dist/kit-extract.inputs.json +115 -113
  5. package/dist/node-catalog.json +502 -59
  6. package/dist/noodl-mcp.cjs +44889 -9360
  7. package/dist/package.json +6 -0
  8. package/dist/preset-fonts/dm-sans/OFL.txt +93 -0
  9. package/dist/preset-fonts/dm-sans/dm-sans-latin-ext-wght-normal.woff2 +0 -0
  10. package/dist/preset-fonts/dm-sans/dm-sans-latin-wght-normal.woff2 +0 -0
  11. package/dist/preset-fonts/dm-sans/manifest.json +9 -0
  12. package/dist/preset-fonts/dm-sans/styles.css +25 -0
  13. package/dist/preset-fonts/nunito/OFL.txt +93 -0
  14. package/dist/preset-fonts/nunito/manifest.json +9 -0
  15. package/dist/preset-fonts/nunito/nunito-latin-ext-wght-normal.woff2 +0 -0
  16. package/dist/preset-fonts/nunito/nunito-latin-wght-normal.woff2 +0 -0
  17. package/dist/preset-fonts/nunito/styles.css +25 -0
  18. package/dist/preset-fonts/source-sans-3/OFL.txt +93 -0
  19. package/dist/preset-fonts/source-sans-3/manifest.json +9 -0
  20. package/dist/preset-fonts/source-sans-3/source-sans-3-latin-ext-wght-normal.woff2 +0 -0
  21. package/dist/preset-fonts/source-sans-3/source-sans-3-latin-wght-normal.woff2 +0 -0
  22. package/dist/preset-fonts/source-sans-3/styles.css +25 -0
  23. package/dist/render-harness/data/node-catalog-enriched.json +93650 -0
  24. package/dist/render-harness/data/node-catalog.json +65289 -0
  25. package/dist/render-harness/data/tokens.ts +674 -0
  26. package/dist/render-harness/drive-steps.js +2784 -0
  27. package/dist/render-harness/measure-from-disk.js +2366 -0
  28. package/dist/render-harness/node_modules/ws/LICENSE +20 -0
  29. package/dist/render-harness/node_modules/ws/README.md +548 -0
  30. package/dist/render-harness/node_modules/ws/browser.js +8 -0
  31. package/dist/render-harness/node_modules/ws/index.js +22 -0
  32. package/dist/render-harness/node_modules/ws/lib/buffer-util.js +131 -0
  33. package/dist/render-harness/node_modules/ws/lib/constants.js +19 -0
  34. package/dist/render-harness/node_modules/ws/lib/event-target.js +292 -0
  35. package/dist/render-harness/node_modules/ws/lib/extension.js +203 -0
  36. package/dist/render-harness/node_modules/ws/lib/limiter.js +55 -0
  37. package/dist/render-harness/node_modules/ws/lib/permessage-deflate.js +528 -0
  38. package/dist/render-harness/node_modules/ws/lib/receiver.js +743 -0
  39. package/dist/render-harness/node_modules/ws/lib/sender.js +607 -0
  40. package/dist/render-harness/node_modules/ws/lib/stream.js +161 -0
  41. package/dist/render-harness/node_modules/ws/lib/subprotocol.js +62 -0
  42. package/dist/render-harness/node_modules/ws/lib/validation.js +152 -0
  43. package/dist/render-harness/node_modules/ws/lib/websocket-server.js +562 -0
  44. package/dist/render-harness/node_modules/ws/lib/websocket.js +1407 -0
  45. package/dist/render-harness/node_modules/ws/package.json +74 -0
  46. package/dist/render-harness/node_modules/ws/wrapper.mjs +21 -0
  47. package/dist/render-harness/render-from-disk.js +7306 -0
  48. package/dist/src/external/viewer/global.d.ts.keep +718 -0
  49. package/dist/src/external/viewer/index.html +126 -0
  50. package/dist/src/external/viewer/load_terminator.js +1 -0
  51. package/dist/src/external/viewer/ndl_assets/OpenSans-Bold.ttf +0 -0
  52. package/dist/src/external/viewer/ndl_assets/OpenSans-Regular.ttf +0 -0
  53. package/dist/src/external/viewer/ndl_assets/home-icon.svg +3 -0
  54. package/dist/src/external/viewer/ndl_assets/make-home-instructions@2x.png +0 -0
  55. package/dist/src/external/viewer/ndl_assets/noodl-logo-black.svg +3 -0
  56. package/dist/src/external/viewer/noodl-app.png +0 -0
  57. package/dist/src/external/viewer/noodl.viewer.js +2 -0
  58. package/dist/src/external/viewer/noodl.viewer.js.LICENSE.txt +20 -0
  59. package/dist/src/external/viewer/react-dom.production.min.js +267 -0
  60. package/dist/src/external/viewer/react.production.min.js +31 -0
  61. package/dist/src/external/viewer/react19/VERSION +2 -0
  62. package/dist/src/external/viewer/react19/react-dom.production.min.js +51 -0
  63. package/dist/src/external/viewer/react19/react.production.min.js +21 -0
  64. package/package.json +8 -2
@@ -0,0 +1,674 @@
1
+ /**
2
+ * The default design-token vocabulary a NodeGX project is written against.
3
+ *
4
+ * Moved here from `noodl-editor`'s `StyleTokensModel/DefaultTokens.ts` by HLS-001. It is read by
5
+ * the editor (which shows and edits it) and by `@nodegx/export` (which resolves a project's stored
6
+ * overrides against it to decide which tokens the exported app has to emit). Neither package can
7
+ * depend on the other, so the vocabulary they must agree on lives here.
8
+ *
9
+ * 🔴 **One copy, on purpose.** This repo has been bitten before by a second copy of a palette
10
+ * drifting in silence, so `StyleTokensModel/DefaultTokens.ts` in the editor is a re-export of this
11
+ * file rather than a duplicate of it, and the same goes for the two types below.
12
+ *
13
+ * STYLE-001: Tailwind CSS-inspired scales with semantic aliases. All values are CSS custom
14
+ * properties that get injected as `:root { ... }`.
15
+ */
16
+
17
+ export type TokenCategory =
18
+ | 'color-semantic'
19
+ | 'color-palette'
20
+ | 'spacing'
21
+ | 'typography-size'
22
+ | 'typography-weight'
23
+ | 'typography-leading'
24
+ | 'typography-tracking'
25
+ | 'typography-family'
26
+ | 'border-radius'
27
+ | 'border-width'
28
+ | 'shadow'
29
+ | 'gradient'
30
+ | 'animation-duration'
31
+ | 'animation-easing';
32
+
33
+ export interface StyleTokenRecord {
34
+ name: string;
35
+ value: string;
36
+ category: TokenCategory;
37
+ isCustom: boolean;
38
+ description?: string;
39
+ }
40
+
41
+ export const DEFAULT_TOKENS: StyleTokenRecord[] = [
42
+ // ─── Semantic Colors ─────────────────────────────────────────────────────────
43
+
44
+ // Primary - Main brand/action color
45
+ {
46
+ // DEF-001: was `#3b82f6` (blue-500), which put `--primary-foreground` (#ffffff) at **3.68:1**
47
+ // — below WCAG 1.4.3's 4.5:1 — on the filled button of every project created by accepting the
48
+ // defaults. `ModernPreset` ships `tokens: {}`, so this value *is* Modern's, and the failure was
49
+ // never one preset's. Moved one step to blue-600 (**5.17:1**) on Richard's ruling that
50
+ // `--primary` moves and white text stays; `--primary-hover` and `--ring` moved with it so the
51
+ // three keep meaning one colour. Graded by `tests-unit/def-001/design-token-contrast.test.ts`,
52
+ // which derives the pairs from the element configs rather than listing them.
53
+ name: '--primary',
54
+ value: '#2563eb',
55
+ category: 'color-semantic',
56
+ isCustom: false,
57
+ description: 'Main brand and action color'
58
+ },
59
+ {
60
+ name: '--primary-hover',
61
+ value: '#1d4ed8',
62
+ category: 'color-semantic',
63
+ isCustom: false,
64
+ description: 'Primary color on hover'
65
+ },
66
+ {
67
+ name: '--primary-foreground',
68
+ value: '#ffffff',
69
+ category: 'color-semantic',
70
+ isCustom: false,
71
+ description: 'Text color on primary background'
72
+ },
73
+
74
+ // Secondary
75
+ {
76
+ name: '--secondary',
77
+ value: '#64748b',
78
+ category: 'color-semantic',
79
+ isCustom: false,
80
+ description: 'Supporting action color'
81
+ },
82
+ {
83
+ name: '--secondary-hover',
84
+ value: '#475569',
85
+ category: 'color-semantic',
86
+ isCustom: false,
87
+ description: 'Secondary color on hover'
88
+ },
89
+ {
90
+ name: '--secondary-foreground',
91
+ value: '#ffffff',
92
+ category: 'color-semantic',
93
+ isCustom: false,
94
+ description: 'Text color on secondary background'
95
+ },
96
+
97
+ // Destructive
98
+ {
99
+ // DEF-001: was `#ef4444` (red-500) at **3.76:1** against white — the destructive button's own
100
+ // label failed the same floor as the primary one, and the same value doubles as the TextInput
101
+ // `error` border. red-600 is **4.83:1**, and `--destructive-hover` moved to red-700 with it.
102
+ name: '--destructive',
103
+ value: '#dc2626',
104
+ category: 'color-semantic',
105
+ isCustom: false,
106
+ description: 'Dangerous actions and errors'
107
+ },
108
+ {
109
+ name: '--destructive-hover',
110
+ value: '#b91c1c',
111
+ category: 'color-semantic',
112
+ isCustom: false,
113
+ description: 'Destructive color on hover'
114
+ },
115
+ {
116
+ name: '--destructive-foreground',
117
+ value: '#ffffff',
118
+ category: 'color-semantic',
119
+ isCustom: false,
120
+ description: 'Text color on destructive background'
121
+ },
122
+
123
+ // Muted
124
+ {
125
+ name: '--muted',
126
+ value: '#f1f5f9',
127
+ category: 'color-semantic',
128
+ isCustom: false,
129
+ description: 'Subtle backgrounds, disabled states'
130
+ },
131
+ {
132
+ name: '--muted-foreground',
133
+ value: '#64748b',
134
+ category: 'color-semantic',
135
+ isCustom: false,
136
+ description: 'Text color on muted background'
137
+ },
138
+
139
+ // Accent
140
+ {
141
+ name: '--accent',
142
+ value: '#f1f5f9',
143
+ category: 'color-semantic',
144
+ isCustom: false,
145
+ description: 'Highlights and selections'
146
+ },
147
+ {
148
+ name: '--accent-foreground',
149
+ value: '#0f172a',
150
+ category: 'color-semantic',
151
+ isCustom: false,
152
+ description: 'Text color on accent background'
153
+ },
154
+
155
+ // Surface colors
156
+ {
157
+ name: '--background',
158
+ value: '#ffffff',
159
+ category: 'color-semantic',
160
+ isCustom: false,
161
+ description: 'Page/app background'
162
+ },
163
+ {
164
+ name: '--foreground',
165
+ value: '#0f172a',
166
+ category: 'color-semantic',
167
+ isCustom: false,
168
+ description: 'Default text color'
169
+ },
170
+ {
171
+ name: '--surface',
172
+ value: '#f8fafc',
173
+ category: 'color-semantic',
174
+ isCustom: false,
175
+ description: 'Card and panel surfaces'
176
+ },
177
+ {
178
+ name: '--surface-raised',
179
+ value: '#ffffff',
180
+ category: 'color-semantic',
181
+ isCustom: false,
182
+ description: 'Elevated surface (dialogs, dropdowns)'
183
+ },
184
+
185
+ // Border colors
186
+ {
187
+ name: '--border',
188
+ value: '#e2e8f0',
189
+ category: 'color-semantic',
190
+ isCustom: false,
191
+ description: 'Default border color'
192
+ },
193
+ {
194
+ name: '--border-subtle',
195
+ value: '#f1f5f9',
196
+ category: 'color-semantic',
197
+ isCustom: false,
198
+ description: 'Subtle/light border'
199
+ },
200
+ {
201
+ name: '--border-strong',
202
+ value: '#cbd5e1',
203
+ category: 'color-semantic',
204
+ isCustom: false,
205
+ description: 'Strong/dark border'
206
+ },
207
+ {
208
+ // The design doctrine prescribes this at 3:1 so a control reads as a
209
+ // control; without it `border-color` falls back to `currentColor`, which is
210
+ // the defect the sentence exists to prevent. Seeded from the editor's own
211
+ // `--theme-color-border-control`. Measured 3.46:1 against `--surface`
212
+ // (#f8fafc) and 3.62:1 against `--background` (#ffffff).
213
+ name: '--border-control',
214
+ value: '#7c8894',
215
+ category: 'color-semantic',
216
+ isCustom: false,
217
+ description: 'Border for controls — meets 3:1 against surface, unlike --border'
218
+ },
219
+
220
+ // Focus ring
221
+ // DEF-001: tracks `--primary`. A focus ring answers to 1.4.11's 3:1, which #3b82f6 already met —
222
+ // it moved because a ring that is not the brand colour is a different bug.
223
+ { name: '--ring', value: '#2563eb', category: 'color-semantic', isCustom: false, description: 'Focus ring color' },
224
+ {
225
+ name: '--ring-offset',
226
+ value: '#ffffff',
227
+ category: 'color-semantic',
228
+ isCustom: false,
229
+ description: 'Focus ring offset color'
230
+ },
231
+ // P88 GAM-026, ruled by Richard: the ring is one look for every app, but its THICKNESS is a
232
+ // project's own. 3px is what session 23 drove and screenshotted on Rocket School — at 2px,
233
+ // beside a template's own 3px border, the ring read as a double border rather than a ring.
234
+ // ⚠️ The viewer's rule is `var(--ring-width, 3px)`, so a project that never heard of this token
235
+ // draws exactly what it drew before.
236
+ { name: '--ring-width', value: '3px', category: 'border-width', isCustom: false, description: 'Focus ring thickness' },
237
+
238
+ // ─── Palette Colors (Gray) ────────────────────────────────────────────────────
239
+
240
+ { name: '--gray-50', value: '#f8fafc', category: 'color-palette', isCustom: false },
241
+ { name: '--gray-100', value: '#f1f5f9', category: 'color-palette', isCustom: false },
242
+ { name: '--gray-200', value: '#e2e8f0', category: 'color-palette', isCustom: false },
243
+ { name: '--gray-300', value: '#cbd5e1', category: 'color-palette', isCustom: false },
244
+ { name: '--gray-400', value: '#94a3b8', category: 'color-palette', isCustom: false },
245
+ { name: '--gray-500', value: '#64748b', category: 'color-palette', isCustom: false },
246
+ { name: '--gray-600', value: '#475569', category: 'color-palette', isCustom: false },
247
+ { name: '--gray-700', value: '#334155', category: 'color-palette', isCustom: false },
248
+ { name: '--gray-800', value: '#1e293b', category: 'color-palette', isCustom: false },
249
+ { name: '--gray-900', value: '#0f172a', category: 'color-palette', isCustom: false },
250
+ { name: '--gray-950', value: '#020617', category: 'color-palette', isCustom: false },
251
+
252
+ // Blue
253
+ { name: '--blue-50', value: '#eff6ff', category: 'color-palette', isCustom: false },
254
+ { name: '--blue-100', value: '#dbeafe', category: 'color-palette', isCustom: false },
255
+ { name: '--blue-200', value: '#bfdbfe', category: 'color-palette', isCustom: false },
256
+ { name: '--blue-300', value: '#93c5fd', category: 'color-palette', isCustom: false },
257
+ { name: '--blue-400', value: '#60a5fa', category: 'color-palette', isCustom: false },
258
+ { name: '--blue-500', value: '#3b82f6', category: 'color-palette', isCustom: false },
259
+ { name: '--blue-600', value: '#2563eb', category: 'color-palette', isCustom: false },
260
+ { name: '--blue-700', value: '#1d4ed8', category: 'color-palette', isCustom: false },
261
+ { name: '--blue-800', value: '#1e40af', category: 'color-palette', isCustom: false },
262
+ { name: '--blue-900', value: '#1e3a8a', category: 'color-palette', isCustom: false },
263
+
264
+ // Red
265
+ { name: '--red-50', value: '#fef2f2', category: 'color-palette', isCustom: false },
266
+ { name: '--red-100', value: '#fee2e2', category: 'color-palette', isCustom: false },
267
+ { name: '--red-200', value: '#fecaca', category: 'color-palette', isCustom: false },
268
+ { name: '--red-300', value: '#fca5a5', category: 'color-palette', isCustom: false },
269
+ { name: '--red-400', value: '#f87171', category: 'color-palette', isCustom: false },
270
+ { name: '--red-500', value: '#ef4444', category: 'color-palette', isCustom: false },
271
+ { name: '--red-600', value: '#dc2626', category: 'color-palette', isCustom: false },
272
+ { name: '--red-700', value: '#b91c1c', category: 'color-palette', isCustom: false },
273
+ { name: '--red-800', value: '#991b1b', category: 'color-palette', isCustom: false },
274
+ { name: '--red-900', value: '#7f1d1d', category: 'color-palette', isCustom: false },
275
+
276
+ // Green
277
+ { name: '--green-50', value: '#f0fdf4', category: 'color-palette', isCustom: false },
278
+ { name: '--green-100', value: '#dcfce7', category: 'color-palette', isCustom: false },
279
+ { name: '--green-200', value: '#bbf7d0', category: 'color-palette', isCustom: false },
280
+ { name: '--green-300', value: '#86efac', category: 'color-palette', isCustom: false },
281
+ { name: '--green-400', value: '#4ade80', category: 'color-palette', isCustom: false },
282
+ { name: '--green-500', value: '#22c55e', category: 'color-palette', isCustom: false },
283
+ { name: '--green-600', value: '#16a34a', category: 'color-palette', isCustom: false },
284
+ { name: '--green-700', value: '#15803d', category: 'color-palette', isCustom: false },
285
+ { name: '--green-800', value: '#166534', category: 'color-palette', isCustom: false },
286
+ { name: '--green-900', value: '#14532d', category: 'color-palette', isCustom: false },
287
+
288
+ // Yellow/Amber
289
+ { name: '--amber-50', value: '#fffbeb', category: 'color-palette', isCustom: false },
290
+ { name: '--amber-100', value: '#fef3c7', category: 'color-palette', isCustom: false },
291
+ { name: '--amber-200', value: '#fde68a', category: 'color-palette', isCustom: false },
292
+ { name: '--amber-300', value: '#fcd34d', category: 'color-palette', isCustom: false },
293
+ { name: '--amber-400', value: '#fbbf24', category: 'color-palette', isCustom: false },
294
+ { name: '--amber-500', value: '#f59e0b', category: 'color-palette', isCustom: false },
295
+ { name: '--amber-600', value: '#d97706', category: 'color-palette', isCustom: false },
296
+ { name: '--amber-700', value: '#b45309', category: 'color-palette', isCustom: false },
297
+ { name: '--amber-800', value: '#92400e', category: 'color-palette', isCustom: false },
298
+ { name: '--amber-900', value: '#78350f', category: 'color-palette', isCustom: false },
299
+
300
+ // Purple
301
+ { name: '--purple-50', value: '#faf5ff', category: 'color-palette', isCustom: false },
302
+ { name: '--purple-100', value: '#f3e8ff', category: 'color-palette', isCustom: false },
303
+ { name: '--purple-200', value: '#e9d5ff', category: 'color-palette', isCustom: false },
304
+ { name: '--purple-300', value: '#d8b4fe', category: 'color-palette', isCustom: false },
305
+ { name: '--purple-400', value: '#c084fc', category: 'color-palette', isCustom: false },
306
+ { name: '--purple-500', value: '#a855f7', category: 'color-palette', isCustom: false },
307
+ { name: '--purple-600', value: '#9333ea', category: 'color-palette', isCustom: false },
308
+ { name: '--purple-700', value: '#7e22ce', category: 'color-palette', isCustom: false },
309
+ { name: '--purple-800', value: '#6b21a8', category: 'color-palette', isCustom: false },
310
+ { name: '--purple-900', value: '#581c87', category: 'color-palette', isCustom: false },
311
+
312
+ // ─── Spacing ──────────────────────────────────────────────────────────────────
313
+
314
+ { name: '--space-0', value: '0px', category: 'spacing', isCustom: false },
315
+ { name: '--space-px', value: '1px', category: 'spacing', isCustom: false },
316
+ { name: '--space-0-5', value: '2px', category: 'spacing', isCustom: false },
317
+ { name: '--space-1', value: '4px', category: 'spacing', isCustom: false },
318
+ { name: '--space-1-5', value: '6px', category: 'spacing', isCustom: false },
319
+ { name: '--space-2', value: '8px', category: 'spacing', isCustom: false },
320
+ { name: '--space-2-5', value: '10px', category: 'spacing', isCustom: false },
321
+ { name: '--space-3', value: '12px', category: 'spacing', isCustom: false },
322
+ { name: '--space-3-5', value: '14px', category: 'spacing', isCustom: false },
323
+ { name: '--space-4', value: '16px', category: 'spacing', isCustom: false },
324
+ { name: '--space-5', value: '20px', category: 'spacing', isCustom: false },
325
+ { name: '--space-6', value: '24px', category: 'spacing', isCustom: false },
326
+ { name: '--space-7', value: '28px', category: 'spacing', isCustom: false },
327
+ { name: '--space-8', value: '32px', category: 'spacing', isCustom: false },
328
+ { name: '--space-9', value: '36px', category: 'spacing', isCustom: false },
329
+ { name: '--space-10', value: '40px', category: 'spacing', isCustom: false },
330
+ { name: '--space-11', value: '44px', category: 'spacing', isCustom: false },
331
+ { name: '--space-12', value: '48px', category: 'spacing', isCustom: false },
332
+ { name: '--space-14', value: '56px', category: 'spacing', isCustom: false },
333
+ { name: '--space-16', value: '64px', category: 'spacing', isCustom: false },
334
+ { name: '--space-20', value: '80px', category: 'spacing', isCustom: false },
335
+ { name: '--space-24', value: '96px', category: 'spacing', isCustom: false },
336
+ { name: '--space-28', value: '112px', category: 'spacing', isCustom: false },
337
+ { name: '--space-32', value: '128px', category: 'spacing', isCustom: false },
338
+
339
+ // Semantic spacing aliases
340
+ {
341
+ name: '--space-xs',
342
+ value: 'var(--space-1)',
343
+ category: 'spacing',
344
+ isCustom: false,
345
+ description: 'Extra small (4px)'
346
+ },
347
+ { name: '--space-sm', value: 'var(--space-2)', category: 'spacing', isCustom: false, description: 'Small (8px)' },
348
+ { name: '--space-md', value: 'var(--space-4)', category: 'spacing', isCustom: false, description: 'Medium (16px)' },
349
+ { name: '--space-lg', value: 'var(--space-6)', category: 'spacing', isCustom: false, description: 'Large (24px)' },
350
+ {
351
+ name: '--space-xl',
352
+ value: 'var(--space-8)',
353
+ category: 'spacing',
354
+ isCustom: false,
355
+ description: 'Extra large (32px)'
356
+ },
357
+ {
358
+ name: '--space-2xl',
359
+ value: 'var(--space-12)',
360
+ category: 'spacing',
361
+ isCustom: false,
362
+ description: '2x large (48px)'
363
+ },
364
+ {
365
+ name: '--space-3xl',
366
+ value: 'var(--space-16)',
367
+ category: 'spacing',
368
+ isCustom: false,
369
+ description: '3x large (64px)'
370
+ },
371
+
372
+ // ─── Typography: Font Sizes ───────────────────────────────────────────────────
373
+
374
+ { name: '--text-xs', value: '12px', category: 'typography-size', isCustom: false },
375
+ { name: '--text-sm', value: '14px', category: 'typography-size', isCustom: false },
376
+ { name: '--text-base', value: '16px', category: 'typography-size', isCustom: false },
377
+ { name: '--text-lg', value: '18px', category: 'typography-size', isCustom: false },
378
+ { name: '--text-xl', value: '20px', category: 'typography-size', isCustom: false },
379
+ { name: '--text-2xl', value: '24px', category: 'typography-size', isCustom: false },
380
+ { name: '--text-3xl', value: '30px', category: 'typography-size', isCustom: false },
381
+ { name: '--text-4xl', value: '36px', category: 'typography-size', isCustom: false },
382
+ { name: '--text-5xl', value: '48px', category: 'typography-size', isCustom: false },
383
+ { name: '--text-6xl', value: '60px', category: 'typography-size', isCustom: false },
384
+
385
+ // ─── Typography: Display Sizes (VIB-002 / register V13) ──────────────────────
386
+ //
387
+ // The design doctrine used to say, correctly for the tokens that existed:
388
+ // *"Type does not scale. `fontSize` has no responsive form, so a `--text-6xl`
389
+ // display headline is 60px on a phone too. Pick the display size that still
390
+ // works at 390px — usually `--text-4xl` or `--text-5xl`."* That advice put a
391
+ // hard 48px ceiling on every headline in the product, and the phase-81 baseline
392
+ // photographed the consequence: the members-area hero is 48px, *exactly* the
393
+ // ceiling, and reads as a paragraph that got bigger rather than as a hero.
394
+ //
395
+ // The fix is not a runtime feature. A token's value is emitted verbatim into
396
+ // `:root` (see `TokenResolver.generateCss`), so a token whose value is a
397
+ // `clamp()` scales with the viewport with no engine change at all — the port
398
+ // sets `font-size: var(--display-lg)` and the browser does the rest. What was
399
+ // missing was a *token that scales*, not a responsive `fontSize` port.
400
+ //
401
+ // Each is `clamp(floor, preferred, ceiling)` where the floor is what still
402
+ // works at 390px and the ceiling is what the width Richard drives at deserves:
403
+ //
404
+ // --display-sm 30px at 390 → 48px at 1900
405
+ // --display-md 36px at 390 → 72px at 1900
406
+ // --display-lg 44px at 390 → 96px at 1900
407
+ //
408
+ // ⚠️ Pair these with `--leading-none` and `--tracking-tighter`. At 96px the
409
+ // normal line height and tracking are what make large type look like enlarged
410
+ // body copy instead of set display type.
411
+ {
412
+ name: '--display-sm',
413
+ value: 'clamp(1.875rem, 1.2rem + 2.4vw, 3rem)',
414
+ category: 'typography-size',
415
+ isCustom: false,
416
+ description: 'Fluid display size: 30px on a phone, 48px on a wide desktop'
417
+ },
418
+ {
419
+ name: '--display-md',
420
+ value: 'clamp(2.25rem, 1.2rem + 4.2vw, 4.5rem)',
421
+ category: 'typography-size',
422
+ isCustom: false,
423
+ description: 'Fluid display size: 36px on a phone, 72px on a wide desktop'
424
+ },
425
+ {
426
+ name: '--display-lg',
427
+ value: 'clamp(2.75rem, 1.4rem + 5.6vw, 6rem)',
428
+ category: 'typography-size',
429
+ isCustom: false,
430
+ description: 'Fluid display size: 44px on a phone, 96px on a wide desktop — the page\'s one hero headline'
431
+ },
432
+
433
+ // ─── Typography: Font Weights ─────────────────────────────────────────────────
434
+
435
+ { name: '--font-thin', value: '100', category: 'typography-weight', isCustom: false },
436
+ { name: '--font-extralight', value: '200', category: 'typography-weight', isCustom: false },
437
+ { name: '--font-light', value: '300', category: 'typography-weight', isCustom: false },
438
+ { name: '--font-normal', value: '400', category: 'typography-weight', isCustom: false },
439
+ { name: '--font-medium', value: '500', category: 'typography-weight', isCustom: false },
440
+ { name: '--font-semibold', value: '600', category: 'typography-weight', isCustom: false },
441
+ { name: '--font-bold', value: '700', category: 'typography-weight', isCustom: false },
442
+ { name: '--font-extrabold', value: '800', category: 'typography-weight', isCustom: false },
443
+ { name: '--font-black', value: '900', category: 'typography-weight', isCustom: false },
444
+
445
+ // ─── Typography: Line Heights ─────────────────────────────────────────────────
446
+
447
+ { name: '--leading-none', value: '1', category: 'typography-leading', isCustom: false },
448
+ { name: '--leading-tight', value: '1.25', category: 'typography-leading', isCustom: false },
449
+ { name: '--leading-snug', value: '1.375', category: 'typography-leading', isCustom: false },
450
+ { name: '--leading-normal', value: '1.5', category: 'typography-leading', isCustom: false },
451
+ { name: '--leading-relaxed', value: '1.625', category: 'typography-leading', isCustom: false },
452
+ { name: '--leading-loose', value: '2', category: 'typography-leading', isCustom: false },
453
+
454
+ // ─── Typography: Letter Spacing ───────────────────────────────────────────────
455
+
456
+ { name: '--tracking-tighter', value: '-0.05em', category: 'typography-tracking', isCustom: false },
457
+ { name: '--tracking-tight', value: '-0.025em', category: 'typography-tracking', isCustom: false },
458
+ { name: '--tracking-normal', value: '0em', category: 'typography-tracking', isCustom: false },
459
+ { name: '--tracking-wide', value: '0.025em', category: 'typography-tracking', isCustom: false },
460
+ { name: '--tracking-wider', value: '0.05em', category: 'typography-tracking', isCustom: false },
461
+ { name: '--tracking-widest', value: '0.1em', category: 'typography-tracking', isCustom: false },
462
+
463
+ // ─── Typography: Font Families ────────────────────────────────────────────────
464
+
465
+ {
466
+ /*
467
+ * POL-006 — Inter first, the platform stack behind it.
468
+ *
469
+ * Inter is bundled into every new project as `noodl_modules/inter/`, whose stylesheet the
470
+ * module scanner injects into the preview and into a deploy alike, so this resolves to a real
471
+ * face offline and with no request to fonts.googleapis.com. Naming it here rather than in the
472
+ * three ElementConfigs keeps one place to change a project's font — which is the whole point
473
+ * of the token.
474
+ *
475
+ * The rest of the stack is not decoration. A project that predates POL-006 has no Inter
476
+ * module, and a browser that cannot find the family simply moves on to the next name; nothing
477
+ * has to know whether the module is there.
478
+ */
479
+ name: '--font-sans',
480
+ value: "Inter, ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'",
481
+ category: 'typography-family',
482
+ isCustom: false,
483
+ description: 'Inter, falling back to the system sans-serif stack'
484
+ },
485
+ {
486
+ name: '--font-serif',
487
+ value: "ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif",
488
+ category: 'typography-family',
489
+ isCustom: false,
490
+ description: 'System serif stack'
491
+ },
492
+ {
493
+ name: '--font-mono',
494
+ value: "ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace",
495
+ category: 'typography-family',
496
+ isCustom: false,
497
+ description: 'System monospace stack'
498
+ },
499
+
500
+ // ─── Border Radius ────────────────────────────────────────────────────────────
501
+
502
+ { name: '--radius-none', value: '0px', category: 'border-radius', isCustom: false },
503
+ { name: '--radius-sm', value: '4px', category: 'border-radius', isCustom: false },
504
+ { name: '--radius-md', value: '8px', category: 'border-radius', isCustom: false },
505
+ { name: '--radius-lg', value: '12px', category: 'border-radius', isCustom: false },
506
+ { name: '--radius-xl', value: '16px', category: 'border-radius', isCustom: false },
507
+ { name: '--radius-2xl', value: '24px', category: 'border-radius', isCustom: false },
508
+ { name: '--radius-3xl', value: '32px', category: 'border-radius', isCustom: false },
509
+ { name: '--radius-full', value: '9999px', category: 'border-radius', isCustom: false, description: 'Pill/circle' },
510
+
511
+ // ─── Border Width ─────────────────────────────────────────────────────────────
512
+
513
+ { name: '--border-0', value: '0px', category: 'border-width', isCustom: false },
514
+ { name: '--border-1', value: '1px', category: 'border-width', isCustom: false },
515
+ { name: '--border-2', value: '2px', category: 'border-width', isCustom: false },
516
+ { name: '--border-4', value: '4px', category: 'border-width', isCustom: false },
517
+ { name: '--border-8', value: '8px', category: 'border-width', isCustom: false },
518
+
519
+ // ─── Shadows ──────────────────────────────────────────────────────────────────
520
+
521
+ { name: '--shadow-none', value: 'none', category: 'shadow', isCustom: false },
522
+ { name: '--shadow-sm', value: '0 1px 2px 0 rgb(0 0 0 / 0.05)', category: 'shadow', isCustom: false },
523
+ {
524
+ name: '--shadow-md',
525
+ value: '0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1)',
526
+ category: 'shadow',
527
+ isCustom: false
528
+ },
529
+ {
530
+ name: '--shadow-lg',
531
+ value: '0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1)',
532
+ category: 'shadow',
533
+ isCustom: false
534
+ },
535
+ {
536
+ name: '--shadow-xl',
537
+ value: '0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1)',
538
+ category: 'shadow',
539
+ isCustom: false
540
+ },
541
+ { name: '--shadow-2xl', value: '0 25px 50px -12px rgb(0 0 0 / 0.25)', category: 'shadow', isCustom: false },
542
+ { name: '--shadow-inner', value: 'inset 0 2px 4px 0 rgb(0 0 0 / 0.05)', category: 'shadow', isCustom: false },
543
+
544
+ // ─── Translucent surfaces (VIB-002 / register V5) ────────────────────────────
545
+ //
546
+ // The two tokens that make a panel readable ON a gradient or a photograph
547
+ // rather than beside one. They are the only way to author translucency without
548
+ // tripping `raw-color-literal`: `opacity` on a Group fades its children too, so
549
+ // a see-through panel has to come from an alpha *fill*, and an alpha fill
550
+ // written inline is a raw colour literal the gate warns on — which is how the
551
+ // capability ended up unreachable through the sanctioned door.
552
+ //
553
+ // ⚠️ Both are white-based, for a panel sitting on a DARK ground (a
554
+ // `--gradient-deep`/`--gradient-spotlight` hero, or a photograph under
555
+ // `--gradient-scrim`). Glass over a light ground is a different token and a
556
+ // different decision; it is not shipped rather than shipped wrong.
557
+ {
558
+ name: '--surface-glass',
559
+ value: 'rgb(255 255 255 / 0.12)',
560
+ category: 'color-semantic',
561
+ isCustom: false,
562
+ description: 'Translucent panel fill for use over a dark gradient or image ground'
563
+ },
564
+ {
565
+ name: '--border-glass',
566
+ value: 'rgb(255 255 255 / 0.28)',
567
+ category: 'color-semantic',
568
+ isCustom: false,
569
+ description: 'The hairline that gives a translucent panel an edge'
570
+ },
571
+
572
+ // ─── Gradients (VIB-002 / register V5) ───────────────────────────────────────
573
+ //
574
+ // Measured at the door before these were written: `get_node_type('Group')`
575
+ // answered `notFound` to `backgroundImage`, and no token category anywhere
576
+ // named a gradient. Every marketing surface in both shipped templates was
577
+ // therefore one flat colour end to end, which is the phase-81 rubric's first
578
+ // disqualifying tell.
579
+ //
580
+ // 🔴 **Every gradient below is written in terms of OTHER tokens, never in raw
581
+ // hex.** That is what makes them re-theme: `MinimalPreset` moves `--primary` to
582
+ // near-black and overrides no gradient, and `--gradient-brand` becomes a
583
+ // near-black wash without the preset knowing gradients exist. A literal
584
+ // `#2563eb` here would have been a second copy of the brand colour, drifting
585
+ // silently the first time somebody re-themed. `--gradient-scrim` is the one
586
+ // exception and is deliberate: a legibility scrim is black-to-transparent in
587
+ // every theme, because its job is contrast against a photograph, not identity.
588
+ // 🔴 The third stop is not decoration. Photographed at 1900 wide with only
589
+ // `--primary` → `--primary-hover`, `--gradient-brand` read as a FLAT blue
590
+ // block: the two tokens are one step apart (#2563eb / #1d4ed8) and a 135° ramp
591
+ // across a 1900×700 band spreads that difference until the eye cannot see it.
592
+ // A gradient token that reads flat on a full-width band is the tell it exists
593
+ // to avoid. `--foreground` at 115% keeps the ink off the canvas while
594
+ // darkening the far corner enough to see.
595
+ //
596
+ // ⚠️ **Never put a comment between a token's `name:` and its `value:`.**
597
+ // `scripts/devtools/render-from-disk.js` reconstructs `:root` by regex
598
+ // (`name:\s*'…',\s*value:\s*'…'`) when no editor is listening, and `\s*` does
599
+ // not cross a `//`. A token commented that way is silently absent from every
600
+ // rendered page the phase-81 Judge photographs, while being perfectly present
601
+ // in the product — a harness that lies in the direction of "your new capability
602
+ // does nothing". This comment sits above the object for that reason.
603
+ {
604
+ name: '--gradient-brand',
605
+ value: 'linear-gradient(135deg, var(--primary) 0%, var(--primary-hover) 45%, var(--foreground) 115%)',
606
+ category: 'gradient',
607
+ isCustom: false,
608
+ description: 'The brand ground for a hero or CTA band. Put --primary-foreground text on it'
609
+ },
610
+ {
611
+ name: '--gradient-deep',
612
+ value: 'linear-gradient(160deg, var(--foreground) 0%, var(--primary-hover) 100%)',
613
+ category: 'gradient',
614
+ isCustom: false,
615
+ description: 'A dark editorial ground — ink into brand. Light text only'
616
+ },
617
+ {
618
+ name: '--gradient-spotlight',
619
+ value: 'radial-gradient(90% 120% at 20% 0%, var(--primary) 0%, var(--foreground) 70%)',
620
+ category: 'gradient',
621
+ isCustom: false,
622
+ description: 'An off-centre glow on a dark ground, for a hero that should not read as a flat panel'
623
+ },
624
+ {
625
+ name: '--gradient-surface',
626
+ value: 'linear-gradient(180deg, var(--surface) 0%, var(--background) 100%)',
627
+ category: 'gradient',
628
+ isCustom: false,
629
+ description: 'A quiet wash for a mid-page band, so a section can change ground without shouting'
630
+ },
631
+ {
632
+ name: '--gradient-scrim',
633
+ value: 'linear-gradient(180deg, rgb(0 0 0 / 0.15) 0%, rgb(0 0 0 / 0.78) 100%)',
634
+ category: 'gradient',
635
+ isCustom: false,
636
+ description: 'Darkens the bottom of a photograph so text laid over it stays readable'
637
+ },
638
+
639
+ // ─── Animation: Durations ─────────────────────────────────────────────────────
640
+
641
+ { name: '--duration-75', value: '75ms', category: 'animation-duration', isCustom: false },
642
+ { name: '--duration-100', value: '100ms', category: 'animation-duration', isCustom: false },
643
+ { name: '--duration-150', value: '150ms', category: 'animation-duration', isCustom: false },
644
+ { name: '--duration-200', value: '200ms', category: 'animation-duration', isCustom: false },
645
+ { name: '--duration-300', value: '300ms', category: 'animation-duration', isCustom: false },
646
+ { name: '--duration-500', value: '500ms', category: 'animation-duration', isCustom: false },
647
+ { name: '--duration-700', value: '700ms', category: 'animation-duration', isCustom: false },
648
+ { name: '--duration-1000', value: '1000ms', category: 'animation-duration', isCustom: false },
649
+
650
+ // ─── Animation: Easing ───────────────────────────────────────────────────────
651
+
652
+ { name: '--ease-linear', value: 'linear', category: 'animation-easing', isCustom: false },
653
+ { name: '--ease-in', value: 'cubic-bezier(0.4, 0, 1, 1)', category: 'animation-easing', isCustom: false },
654
+ { name: '--ease-out', value: 'cubic-bezier(0, 0, 0.2, 1)', category: 'animation-easing', isCustom: false },
655
+ { name: '--ease-in-out', value: 'cubic-bezier(0.4, 0, 0.2, 1)', category: 'animation-easing', isCustom: false },
656
+ {
657
+ name: '--ease-bounce',
658
+ value: 'cubic-bezier(0.68, -0.55, 0.265, 1.55)',
659
+ category: 'animation-easing',
660
+ isCustom: false,
661
+ description: 'Bouncy animation curve'
662
+ }
663
+ ];
664
+
665
+ /**
666
+ * Build a Map from the default tokens array for fast lookup by name.
667
+ */
668
+ export function buildDefaultTokenMap(): Map<string, StyleTokenRecord> {
669
+ const map = new Map<string, StyleTokenRecord>();
670
+ for (const token of DEFAULT_TOKENS) {
671
+ map.set(token.name, token);
672
+ }
673
+ return map;
674
+ }