@mildastudio/milda 0.1.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.
package/dist/index.js ADDED
@@ -0,0 +1,3094 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ LexError: () => LexError,
24
+ TOKEN_VALUE_KIND: () => TOKEN_VALUE_KIND,
25
+ allTokensOfType: () => allTokensOfType,
26
+ archetypesByName: () => archetypesByName,
27
+ atomsByName: () => atomsByName,
28
+ collectActiveStates: () => collectActiveStates,
29
+ conformance: () => conformance,
30
+ findToken: () => findToken,
31
+ lex: () => lex,
32
+ parseMildaDocument: () => parseMildaDocument,
33
+ preludeV0: () => preludeV0,
34
+ resolveFacets: () => resolveFacets,
35
+ resolveLayout: () => resolveLayout,
36
+ resolveToken: () => resolveToken,
37
+ validate: () => validate
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+
41
+ // types/tokens.ts
42
+ var TOKEN_VALUE_KIND = {
43
+ color: "color",
44
+ spacing: "dimension",
45
+ radius: "dimension",
46
+ size: "dimension",
47
+ shadow: "shadow",
48
+ typography: "typography",
49
+ fontFamily: "fontFamily",
50
+ duration: "duration",
51
+ opacity: "opacity",
52
+ blur: "blur",
53
+ tonal: "tonal",
54
+ border: "border",
55
+ elevation: "facet",
56
+ ring: "facet",
57
+ backdrop: "facet",
58
+ none: "facet"
59
+ };
60
+
61
+ // prelude/preludeV0.ts
62
+ var atoms = [
63
+ {
64
+ kind: "atom",
65
+ name: "activation",
66
+ doc: "invoke an action + emit event/payload on a trigger",
67
+ params: [{ name: "payload" }],
68
+ states: [{ id: "ready", initial: true }],
69
+ events: [{ name: "trigger" }],
70
+ transitions: [
71
+ {
72
+ from: "ready",
73
+ on: "trigger",
74
+ when: "enabled",
75
+ to: "ready",
76
+ emit: { event: "invoke", arg: "payload" }
77
+ }
78
+ ],
79
+ emits: [{ name: "invoke" }],
80
+ note: "transducer (single state); foundational atom under almost every interactive archetype"
81
+ },
82
+ {
83
+ kind: "atom",
84
+ name: "scheduler",
85
+ doc: "timed transition: auto-dismiss, auto-advance, debounce, delay",
86
+ modes: ["timeout", "debounce", "interval"],
87
+ params: [{ name: "t" }, { name: "mode" }],
88
+ states: [{ id: "idle", initial: true }, { id: "pending" }],
89
+ events: [{ name: "arm" }, { name: "cancel" }],
90
+ transitions: [
91
+ { from: "idle", on: "arm", to: "pending" },
92
+ { from: "pending", after: "t", to: "idle", emit: { event: "fire" } },
93
+ { from: "pending", on: "arm", to: "pending", note: "restart (debounce)" },
94
+ { from: "pending", on: "cancel", to: "idle" }
95
+ ],
96
+ emits: [{ name: "fire" }],
97
+ note: "load-bearing: all time-based interactions compose from it"
98
+ },
99
+ {
100
+ kind: "atom",
101
+ name: "toggle",
102
+ doc: "flip a boolean (BooleanInput core; tristate \u2192 +mixed)",
103
+ params: [{ name: "v", bind: true }],
104
+ states: [{ id: "off", initial: true }, { id: "on" }],
105
+ events: [{ name: "toggle" }, { name: "set", args: ["b"] }],
106
+ transitions: [
107
+ { from: "off", on: "toggle", to: "on", emit: { event: "change", arg: true } },
108
+ { from: "on", on: "toggle", to: "off", emit: { event: "change", arg: false } },
109
+ { from: "any", on: "set", to: { expr: "b?on:off" }, note: "controlled sync, no emit" }
110
+ ],
111
+ exposes: [{ state: "checked", when: "state==on" }],
112
+ emits: [{ name: "change" }]
113
+ },
114
+ {
115
+ kind: "atom",
116
+ name: "selection",
117
+ doc: "choose member(s) of a collection",
118
+ modes: ["single", "multiple", "range", "none"],
119
+ params: [{ name: "C" }, { name: "selected", bind: true }],
120
+ context: [{ name: "sel", type: "C|none", init: "none" }],
121
+ states: [{ id: "empty", initial: true }, { id: "chosen" }],
122
+ events: [{ name: "select", args: ["x"] }, { name: "clear" }, { name: "set", args: ["x"] }],
123
+ transitions: [
124
+ {
125
+ from: "any",
126
+ on: "select",
127
+ when: "x\u2208C",
128
+ to: "chosen",
129
+ ctx: [{ target: "sel", expr: "x" }],
130
+ emit: { event: "change", arg: "x" }
131
+ },
132
+ {
133
+ from: "chosen",
134
+ on: "clear",
135
+ to: "empty",
136
+ ctx: [{ target: "sel", expr: "none" }],
137
+ emit: { event: "change", arg: "none" }
138
+ },
139
+ {
140
+ from: "any",
141
+ on: "set",
142
+ to: { expr: "x?chosen:empty" },
143
+ ctx: [{ target: "sel", expr: "x" }],
144
+ note: "controlled sync, no emit"
145
+ }
146
+ ],
147
+ exposes: [{ state: "selected", arg: "x", when: "state==chosen \u2227 sel==x" }],
148
+ emits: [{ name: "change" }],
149
+ note: "multiple=set; range=anchor+extent (date range, shift-click, cell range)"
150
+ },
151
+ {
152
+ kind: "atom",
153
+ name: "browse",
154
+ doc: "move a cursor over a collection",
155
+ modes: ["roving", "spatial", "tree", "scroll-detent"],
156
+ requires: [{ anyOf: ["input.keyboard", "input.remote"], when: "mode\u2208{roving,tree}" }],
157
+ params: [{ name: "C" }, { name: "mode" }],
158
+ context: [{ name: "active", type: "C|none", init: "none" }],
159
+ events: [
160
+ { name: "move", args: ["dir"] },
161
+ { name: "to", args: ["x"] }
162
+ ],
163
+ transitions: [
164
+ {
165
+ from: "any",
166
+ on: "move",
167
+ to: "any",
168
+ ctx: [{ target: "active", expr: "neighbor(active,dir,mode,wrap)" }],
169
+ emit: { event: "activeChanged", arg: "active" }
170
+ },
171
+ {
172
+ from: "any",
173
+ on: "to",
174
+ to: "any",
175
+ ctx: [{ target: "active", expr: "x" }],
176
+ emit: { event: "activeChanged", arg: "x" }
177
+ }
178
+ ],
179
+ exposes: [{ state: "active", arg: "x", when: "active==x" }],
180
+ emits: [{ name: "activeChanged" }],
181
+ note: "neighbor(): roving=linear \xB7 spatial=2D \xB7 tree=in/out/sib \xB7 scroll-detent=nearest"
182
+ },
183
+ {
184
+ kind: "atom",
185
+ name: "presentation",
186
+ doc: "manage a surface",
187
+ modes: ["inline", "popover", "menu", "sheet", "dialog", "push", "wheel"],
188
+ params: [{ name: "surface" }],
189
+ states: [{ id: "closed", initial: true }, { id: "open" }],
190
+ events: [{ name: "open" }, { name: "close" }, { name: "toggle" }],
191
+ transitions: [
192
+ { from: "closed", on: "toggle|open", to: "open", emit: { event: "opened" } },
193
+ { from: "open", on: "toggle|close", to: "closed", emit: { event: "closed" } }
194
+ ],
195
+ exposes: [{ state: "open", when: "state==open" }],
196
+ emits: [{ name: "opened" }, { name: "closed" }],
197
+ note: "surface is realization-only"
198
+ },
199
+ {
200
+ kind: "atom",
201
+ name: "dismiss",
202
+ doc: "close on a configured trigger",
203
+ params: [{ name: "when" }],
204
+ states: [{ id: "armed", initial: true }],
205
+ events: [{ name: "trigger" }],
206
+ transitions: [
207
+ { from: "armed", on: "trigger", to: "armed", emit: { event: "dismissRequested" } }
208
+ ],
209
+ emits: [{ name: "dismissRequested" }],
210
+ note: "trigger \u2208 configured [outside, escape, commit, swipe-down, timeout]"
211
+ },
212
+ {
213
+ kind: "atom",
214
+ name: "focus-scope",
215
+ doc: "trap / contain / restore focus within a region (modal a11y)",
216
+ requires: [{ anyOf: ["input.keyboard", "input.remote"] }],
217
+ states: [{ id: "inactive", initial: true }, { id: "active" }],
218
+ events: [{ name: "activate" }, { name: "deactivate" }],
219
+ transitions: [
220
+ {
221
+ from: "inactive",
222
+ on: "activate",
223
+ to: "active",
224
+ note: "save prior focus; move focus into region; trap Tab"
225
+ },
226
+ {
227
+ from: "active",
228
+ on: "deactivate",
229
+ to: "inactive",
230
+ note: "release trap; restore prior focus"
231
+ }
232
+ ],
233
+ note: "no visual states (exposes none) \u2014 a behaviour guarantee, not a paint surface; realized as a focus trap on web, OS-owned on native sheets"
234
+ },
235
+ {
236
+ kind: "atom",
237
+ name: "value-entry",
238
+ doc: "free text/value entry; commit on change|blur|enter",
239
+ params: [{ name: "value", bind: true }, { name: "commit" }],
240
+ context: [
241
+ { name: "value", type: "V" },
242
+ { name: "draft", type: "V", init: "value" }
243
+ ],
244
+ states: [{ id: "idle", initial: true }, { id: "editing" }],
245
+ events: [
246
+ { name: "focus" },
247
+ { name: "input", args: ["t"] },
248
+ { name: "enter" },
249
+ { name: "blur" },
250
+ { name: "set", args: ["v"] }
251
+ ],
252
+ transitions: [
253
+ { from: "idle", on: "focus", to: "editing" },
254
+ {
255
+ from: "editing",
256
+ on: "input",
257
+ to: "editing",
258
+ ctx: [{ target: "draft", expr: "t" }],
259
+ when: "change\u2208commit",
260
+ emit: { event: "change", arg: "t" },
261
+ note: "value:=t only when change\u2208commit"
262
+ },
263
+ {
264
+ from: "editing",
265
+ on: "enter",
266
+ when: "enter\u2208commit",
267
+ to: "editing",
268
+ ctx: [{ target: "value", expr: "draft" }],
269
+ emit: { event: "change", arg: "draft" }
270
+ },
271
+ {
272
+ from: "editing",
273
+ on: "blur",
274
+ to: "idle",
275
+ note: "when blur\u2208commit: value:=draft, emit change(draft); else draft:=value"
276
+ },
277
+ {
278
+ from: "any",
279
+ on: "set",
280
+ to: "any",
281
+ ctx: [
282
+ { target: "value", expr: "v" },
283
+ { target: "draft", expr: "v" }
284
+ ],
285
+ note: "controlled, no emit"
286
+ }
287
+ ],
288
+ exposes: [
289
+ { state: "focused", when: "state==editing" },
290
+ { state: "empty", when: "value empty" },
291
+ { state: "dirty", when: "draft\u2260value" }
292
+ ],
293
+ emits: [{ name: "change" }]
294
+ },
295
+ {
296
+ kind: "atom",
297
+ name: "range",
298
+ doc: "value(s) along a min/max track (single/dual thumb)",
299
+ params: [
300
+ { name: "value", bind: true },
301
+ { name: "min" },
302
+ { name: "max" },
303
+ { name: "step" },
304
+ { name: "thumbs" }
305
+ ],
306
+ context: [
307
+ { name: "value", type: "V|{lo,hi}" },
308
+ { name: "active", type: "thumb|none", init: "none" }
309
+ ],
310
+ states: [{ id: "idle", initial: true }, { id: "dragging" }],
311
+ events: [
312
+ { name: "grab", args: ["t"] },
313
+ { name: "to", args: ["v"] },
314
+ { name: "release" },
315
+ { name: "by", args: ["t", "d"] },
316
+ { name: "set", args: ["v"] }
317
+ ],
318
+ transitions: [
319
+ { from: "idle", on: "grab", to: "dragging", ctx: [{ target: "active", expr: "t" }] },
320
+ {
321
+ from: "dragging",
322
+ on: "to",
323
+ to: "dragging",
324
+ ctx: [{ target: "value[active]", expr: "clamp+snap(v); lo\u2264hi" }],
325
+ emit: { event: "change", arg: "value" }
326
+ },
327
+ { from: "dragging", on: "release", to: "idle", emit: { event: "commit", arg: "value" } },
328
+ {
329
+ from: "idle",
330
+ on: "by",
331
+ to: "idle",
332
+ ctx: [{ target: "value[t]", expr: "clamp(value[t]+d)" }],
333
+ emit: { event: "change", arg: "value" },
334
+ note: "keyboard"
335
+ },
336
+ { from: "any", on: "set", to: "any", ctx: [{ target: "value", expr: "v" }] }
337
+ ],
338
+ exposes: [{ state: "active", arg: "t", when: "state==dragging \u2227 active==t" }],
339
+ emits: [{ name: "change" }, { name: "commit" }],
340
+ note: "live (change) vs committed (commit)"
341
+ },
342
+ {
343
+ kind: "atom",
344
+ name: "stepping",
345
+ doc: "increment/decrement a value",
346
+ params: [{ name: "value", bind: true }, { name: "min" }, { name: "max" }, { name: "step" }],
347
+ context: [{ name: "value", type: "number" }],
348
+ events: [{ name: "inc" }, { name: "dec" }, { name: "set", args: ["v"] }],
349
+ transitions: [
350
+ {
351
+ from: "any",
352
+ on: "inc",
353
+ when: "value+step\u2264max",
354
+ to: "any",
355
+ ctx: [{ target: "value", expr: "value+step" }],
356
+ emit: { event: "change", arg: "value" }
357
+ },
358
+ {
359
+ from: "any",
360
+ on: "dec",
361
+ when: "value-step\u2265min",
362
+ to: "any",
363
+ ctx: [{ target: "value", expr: "value-step" }],
364
+ emit: { event: "change", arg: "value" }
365
+ },
366
+ { from: "any", on: "set", to: "any", ctx: [{ target: "value", expr: "clamp(v)" }] }
367
+ ],
368
+ exposes: [
369
+ { state: "atMin", when: "value\u2264min" },
370
+ { state: "atMax", when: "value\u2265max" }
371
+ ],
372
+ emits: [{ name: "change" }]
373
+ },
374
+ {
375
+ kind: "atom",
376
+ name: "validation",
377
+ doc: "valid/invalid against rules (PURE \u2014 does not listen to input)",
378
+ params: [{ name: "value" }, { name: "rules" }],
379
+ context: [{ name: "errors", type: "list", init: "[]" }],
380
+ states: [{ id: "pristine", initial: true }, { id: "valid" }, { id: "invalid" }],
381
+ events: [{ name: "check" }, { name: "reset" }],
382
+ transitions: [
383
+ {
384
+ from: "any",
385
+ on: "check",
386
+ to: { expr: "eval(rules)?valid:invalid" },
387
+ ctx: [{ target: "errors", expr: "eval(rules).errors" }]
388
+ },
389
+ { from: "any", on: "reset", to: "pristine", ctx: [{ target: "errors", expr: "[]" }] }
390
+ ],
391
+ exposes: [
392
+ { state: "invalid", when: "state==invalid" },
393
+ { state: "valid", when: "state==valid" },
394
+ { state: "pristine", when: "state==pristine" }
395
+ ],
396
+ emits: [{ name: "validity" }],
397
+ note: "the when:change|blur|submit policy = a wire from value-entry/submission into check"
398
+ },
399
+ {
400
+ kind: "atom",
401
+ name: "submission",
402
+ doc: "form-level orchestration: collective validation, submit, focus-first-error",
403
+ params: [{ name: "fields" }],
404
+ context: [{ name: "fields", type: "set<validation>" }],
405
+ states: [{ id: "editing", initial: true }, { id: "submitting" }, { id: "submitted" }],
406
+ events: [{ name: "submit" }, { name: "success" }, { name: "failure", args: ["e"] }],
407
+ transitions: [
408
+ {
409
+ from: "editing",
410
+ on: "submit",
411
+ to: "editing",
412
+ do: "broadcast fields.check",
413
+ note: "when allValid \u2192 submitting emit submitStart(values); else \u2192 editing do firstInvalid.focus"
414
+ },
415
+ { from: "submitting", on: "success", to: "submitted", emit: { event: "submitSuccess" } },
416
+ {
417
+ from: "submitting",
418
+ on: "failure",
419
+ to: "editing",
420
+ emit: { event: "submitError", arg: "e" }
421
+ }
422
+ ],
423
+ exposes: [
424
+ { state: "loading", when: "state==submitting" },
425
+ { state: "dirty", when: "any(fields,f\u2192f.dirty)" }
426
+ ],
427
+ emits: [{ name: "submitStart" }, { name: "submitSuccess" }, { name: "submitError" }],
428
+ note: "fields: DYNAMIC set of validation machines; FAN-OUT broadcast + FAN-IN aggregate/select"
429
+ },
430
+ {
431
+ kind: "atom",
432
+ name: "gesture",
433
+ doc: "swipe | long-press | pinch | pan | drag | drop",
434
+ modes: ["swipe", "long-press", "pinch", "pan", "drag", "drop"],
435
+ requires: [{ anyOf: ["input.touch"], when: "per gesture" }],
436
+ params: [{ name: "kind" }, { name: "thresholds" }],
437
+ context: [{ name: "start" }, { name: "current" }, { name: "t0" }],
438
+ states: [{ id: "idle", initial: true }, { id: "tracking" }, { id: "recognized" }],
439
+ events: [{ name: "down", args: ["p"] }, { name: "move", args: ["p"] }, { name: "up" }],
440
+ transitions: [
441
+ { from: "idle", on: "down", to: "tracking", ctx: [{ target: "start", expr: "p" }] },
442
+ {
443
+ from: "tracking",
444
+ on: "move",
445
+ to: "tracking",
446
+ ctx: [{ target: "current", expr: "p" }],
447
+ when: "exceeds(\u03B8)",
448
+ emit: { event: "begin" },
449
+ note: "recognized when threshold exceeded"
450
+ },
451
+ {
452
+ from: "tracking",
453
+ after: "t",
454
+ to: "recognized",
455
+ emit: { event: "begin" },
456
+ note: "long-press"
457
+ },
458
+ { from: "tracking", on: "up", to: "idle", note: "canceled, threshold not met" },
459
+ { from: "recognized", on: "move", to: "recognized", emit: { event: "update", arg: "delta" } },
460
+ { from: "recognized", on: "up", to: "idle", emit: { event: "end" } }
461
+ ],
462
+ exposes: [{ state: "dragging", when: "state==recognized" }],
463
+ emits: [{ name: "begin" }, { name: "update" }, { name: "end" }],
464
+ note: "load-bearing: all drag-based interactions compose from it"
465
+ },
466
+ {
467
+ kind: "atom",
468
+ name: "async-state",
469
+ doc: "idle | loading | success | error lifecycle",
470
+ states: [{ id: "idle", initial: true }, { id: "loading" }, { id: "success" }, { id: "error" }],
471
+ events: [{ name: "start" }, { name: "resolve", args: ["v"] }, { name: "reject", args: ["e"] }],
472
+ transitions: [
473
+ { from: "any", on: "start", to: "loading", do: "run" },
474
+ { from: "loading", on: "resolve", to: "success", emit: { event: "success", arg: "v" } },
475
+ { from: "loading", on: "reject", to: "error", emit: { event: "error", arg: "e" } }
476
+ ],
477
+ exposes: [
478
+ { state: "loading", when: "state==loading" },
479
+ { state: "success", when: "state==success" },
480
+ { state: "error", when: "state==error" }
481
+ ],
482
+ emits: [{ name: "success" }, { name: "error" }]
483
+ },
484
+ {
485
+ kind: "atom",
486
+ name: "navigation",
487
+ doc: "switch between destinations (carries a history stack)",
488
+ params: [{ name: "dests" }],
489
+ context: [{ name: "stack", type: "list", init: "[root]" }],
490
+ events: [{ name: "go", args: ["d"] }, { name: "back" }],
491
+ transitions: [
492
+ {
493
+ from: "any",
494
+ on: "go",
495
+ to: "any",
496
+ ctx: [{ target: "stack", expr: "stack.push(d)" }],
497
+ emit: { event: "navigate", arg: "current" }
498
+ },
499
+ {
500
+ from: "any",
501
+ on: "back",
502
+ when: "|stack|>1",
503
+ to: "any",
504
+ ctx: [{ target: "stack", expr: "stack.pop()" }],
505
+ emit: { event: "navigate", arg: "current" }
506
+ }
507
+ ],
508
+ exposes: [{ state: "current", arg: "d", when: "current==d" }],
509
+ emits: [{ name: "navigate" }],
510
+ note: "history stack shared with undo/redo, wizard-back; richer than selection.single + activation"
511
+ },
512
+ {
513
+ kind: "atom",
514
+ name: "scroll",
515
+ doc: "observe scroll position: infinite-load, scroll-spy, reveal-on-scroll, parallax",
516
+ params: [{ name: "region" }],
517
+ context: [{ name: "pos", init: "0" }],
518
+ events: [{ name: "scrolled", args: ["p"] }],
519
+ transitions: [
520
+ {
521
+ from: "any",
522
+ on: "scrolled",
523
+ to: "any",
524
+ ctx: [{ target: "pos", expr: "p" }],
525
+ emit: { event: "atEnd", arg: "p" },
526
+ note: "emit atEnd when p\u2265max, nearEnd when p\u2265max-\u03B8"
527
+ }
528
+ ],
529
+ exposes: [
530
+ { state: "atStart", when: "pos\u22640" },
531
+ { state: "atEnd", when: "pos\u2265max" },
532
+ { state: "scrolled", when: "pos>0" }
533
+ ],
534
+ emits: [{ name: "atEnd" }, { name: "nearEnd" }],
535
+ note: "atEnd \u2192 wire \u2192 async-state.start = infinite-load"
536
+ },
537
+ {
538
+ kind: "atom",
539
+ name: "progress",
540
+ doc: "determinate / indeterminate completion",
541
+ modes: ["determinate", "indeterminate"],
542
+ params: [{ name: "value" }, { name: "max" }],
543
+ events: [{ name: "set", args: ["v"] }],
544
+ transitions: [
545
+ {
546
+ from: "any",
547
+ on: "set",
548
+ to: "any",
549
+ ctx: [{ target: "value", expr: "v" }],
550
+ emit: { event: "change", arg: "value" }
551
+ }
552
+ ],
553
+ exposes: [{ state: "complete", when: "value\u2265max" }],
554
+ emits: [{ name: "change" }]
555
+ },
556
+ {
557
+ kind: "atom",
558
+ name: "liveness",
559
+ doc: "announce dynamic changes (polite/assertive)",
560
+ modes: ["polite", "assertive"],
561
+ requires: [{ anyOf: ["a11y.screenreader"] }],
562
+ events: [{ name: "announce", args: ["msg"] }],
563
+ transitions: [
564
+ { from: "any", on: "announce", to: "any", emit: { event: "announced", arg: "msg" } }
565
+ ],
566
+ emits: [{ name: "announced" }]
567
+ }
568
+ ];
569
+ var composites = [
570
+ {
571
+ kind: "composite",
572
+ name: "disclosure",
573
+ builtFrom: ["toggle", "presentation"],
574
+ uses: [
575
+ { atom: "toggle", as: "T" },
576
+ { atom: "presentation", as: "P", params: { surface: "inline" } }
577
+ ],
578
+ composition: [{ kind: "wire", from: "T.change", to: "P.toggle" }],
579
+ note: "\u2248 toggle(expanded) + presentation(inline); exposes expanded"
580
+ },
581
+ {
582
+ kind: "composite",
583
+ name: "typeahead",
584
+ builtFrom: ["scheduler", "browse"],
585
+ uses: [
586
+ { atom: "scheduler", as: "S", params: { mode: "debounce" } },
587
+ { atom: "browse", as: "B" }
588
+ ],
589
+ note: "jump to a member by typed prefix; requires input.keyboard"
590
+ },
591
+ {
592
+ kind: "composite",
593
+ name: "dnd",
594
+ builtFrom: ["gesture", "ordering"],
595
+ note: "drag-and-drop move/reorder across containers; ordering is a non-behavior (collection mutation)"
596
+ },
597
+ {
598
+ kind: "composite",
599
+ name: "resize",
600
+ builtFrom: ["gesture"],
601
+ uses: [{ atom: "gesture", as: "G", params: { kind: "drag" } }],
602
+ note: "drag-resize a region/panel; requires input.pointer"
603
+ },
604
+ {
605
+ kind: "composite",
606
+ name: "auto-dismiss",
607
+ builtFrom: ["scheduler"],
608
+ uses: [{ atom: "scheduler", as: "S", params: { mode: "timeout" } }],
609
+ composition: [{ kind: "wire", from: "S.fire", to: "dismissRequested" }],
610
+ note: "self-close after a timeout (toast)"
611
+ },
612
+ {
613
+ kind: "composite",
614
+ name: "stepping-nav",
615
+ builtFrom: ["navigation"],
616
+ note: "\u2248 navigation(ordered) + constraint; ordered progress through steps; exposes active, complete"
617
+ },
618
+ {
619
+ kind: "composite",
620
+ name: "pressability",
621
+ builtFrom: ["gesture"],
622
+ note: "press/hover/active feedback (ripple, highlight); contributes pressed/hovered feedback states ONLY \u2014 does not fire the action (that is activation)"
623
+ }
624
+ ];
625
+ var archetypes = [
626
+ {
627
+ kind: "archetype",
628
+ name: "Button",
629
+ group: "Actions",
630
+ contract: "label/icon, fires action",
631
+ produces: { level: "phrase", interactive: true },
632
+ anatomy: [
633
+ { name: "icon", role: "icon", optional: true },
634
+ { name: "label", role: "text" }
635
+ ],
636
+ composes: ["activation", "pressability", "async-state?"],
637
+ realizationNote: "Material FAB/elevated/tonal; iOS .borderedProminent"
638
+ },
639
+ {
640
+ kind: "archetype",
641
+ name: "ToggleButton",
642
+ group: "Actions",
643
+ contract: "on/off action",
644
+ produces: { level: "phrase", interactive: true },
645
+ anatomy: [
646
+ { name: "indicator", role: "icon", optional: true },
647
+ { name: "label", role: "text" }
648
+ ],
649
+ composes: ["toggle", "pressability"]
650
+ },
651
+ {
652
+ kind: "archetype",
653
+ name: "MenuButton",
654
+ group: "Actions",
655
+ contract: "opens a menu of actions",
656
+ produces: { level: "region", interactive: false },
657
+ anatomy: [
658
+ {
659
+ name: "trigger",
660
+ role: "control",
661
+ children: [
662
+ { name: "label", role: "text" },
663
+ { name: "indicator", role: "icon", optional: true }
664
+ ]
665
+ },
666
+ {
667
+ name: "surface",
668
+ role: "container",
669
+ children: [{ name: "item", role: "item", children: [{ name: "label", role: "text" }] }]
670
+ }
671
+ ],
672
+ composes: ["presentation", "browse", "typeahead?", "dismiss"]
673
+ },
674
+ {
675
+ kind: "archetype",
676
+ name: "Link",
677
+ group: "Actions",
678
+ contract: "navigates",
679
+ produces: { level: "phrase", interactive: true },
680
+ anatomy: [
681
+ { name: "label", role: "text" },
682
+ { name: "icon", role: "icon", optional: true }
683
+ ],
684
+ composes: ["activation", "pressability"]
685
+ },
686
+ {
687
+ kind: "archetype",
688
+ name: "Form",
689
+ group: "Inputs",
690
+ contract: "a set of fields, one submit",
691
+ produces: { level: "region", interactive: false },
692
+ anatomy: [
693
+ { name: "content", role: "container" },
694
+ { name: "submit", role: "control", children: [{ name: "label", role: "text" }] },
695
+ { name: "error", role: "text", optional: true }
696
+ ],
697
+ composes: ["submission"]
698
+ },
699
+ {
700
+ kind: "archetype",
701
+ name: "Field",
702
+ group: "Inputs",
703
+ contract: "label + control + error/help wrapper",
704
+ produces: { level: "region", interactive: false },
705
+ anatomy: [
706
+ { name: "label", role: "text" },
707
+ { name: "control", role: "container" },
708
+ { name: "help", role: "text", optional: true },
709
+ { name: "error", role: "text", optional: true }
710
+ ],
711
+ composes: ["validation"]
712
+ },
713
+ {
714
+ kind: "archetype",
715
+ name: "TextField",
716
+ group: "Inputs",
717
+ contract: "string value",
718
+ produces: { level: "region", interactive: false },
719
+ anatomy: [
720
+ { name: "icon", role: "icon", optional: true },
721
+ { name: "control", role: "input" }
722
+ ],
723
+ composes: ["value-entry", "validation?"]
724
+ },
725
+ {
726
+ kind: "archetype",
727
+ name: "TextArea",
728
+ group: "Inputs",
729
+ contract: "multiline string value",
730
+ produces: { level: "region", interactive: false },
731
+ anatomy: [{ name: "control", role: "input" }],
732
+ composes: ["value-entry", "validation?"]
733
+ },
734
+ {
735
+ kind: "archetype",
736
+ name: "SearchField",
737
+ group: "Inputs",
738
+ contract: "query string + clear",
739
+ produces: { level: "region", interactive: false },
740
+ anatomy: [
741
+ { name: "icon", role: "icon", optional: true },
742
+ { name: "control", role: "input" },
743
+ { name: "clear", role: "control", optional: true }
744
+ ],
745
+ composes: ["value-entry", "filter?"]
746
+ },
747
+ {
748
+ kind: "archetype",
749
+ name: "BooleanInput",
750
+ group: "Inputs",
751
+ contract: "one bool",
752
+ produces: { level: "region", interactive: false },
753
+ anatomy: [
754
+ { name: "control", role: "input" },
755
+ { name: "indicator", role: "icon" },
756
+ { name: "label", role: "text", optional: true }
757
+ ],
758
+ composes: ["toggle"],
759
+ realizationNote: "iOS: checkbox \u2192 switch (no indeterminate)"
760
+ },
761
+ {
762
+ kind: "archetype",
763
+ name: "ChoiceGroup",
764
+ group: "Inputs",
765
+ contract: "one-of-N",
766
+ produces: { level: "region", interactive: false },
767
+ anatomy: [
768
+ {
769
+ name: "option",
770
+ role: "item",
771
+ children: [
772
+ { name: "indicator", role: "icon" },
773
+ { name: "label", role: "text" }
774
+ ]
775
+ }
776
+ ],
777
+ composes: ["selection.single", "browse"],
778
+ realizationNote: "radio stack | segmented (node.group); iOS: radio \u2192 wheel/segmented"
779
+ },
780
+ {
781
+ kind: "archetype",
782
+ name: "SingleSelect",
783
+ group: "Inputs",
784
+ contract: "one of a known set",
785
+ produces: { level: "region", interactive: false },
786
+ anatomy: [
787
+ {
788
+ name: "trigger",
789
+ role: "control",
790
+ children: [
791
+ { name: "value", role: "text" },
792
+ { name: "indicator", role: "icon" }
793
+ ]
794
+ },
795
+ {
796
+ name: "surface",
797
+ role: "container",
798
+ children: [
799
+ {
800
+ name: "option",
801
+ role: "item",
802
+ children: [
803
+ { name: "check", role: "icon", optional: true },
804
+ { name: "label", role: "text" }
805
+ ]
806
+ }
807
+ ]
808
+ }
809
+ ],
810
+ composes: ["selection.single", "presentation", "browse", "dismiss", "typeahead?"],
811
+ uses: [
812
+ { atom: "activation", as: "A" },
813
+ { atom: "presentation", as: "P", params: { surface: "popover" } },
814
+ { atom: "selection", as: "S", params: { mode: "single", over: "options" } },
815
+ { atom: "browse", as: "B", params: { over: "options", mode: "roving" } },
816
+ { atom: "dismiss", as: "D", params: { when: "[outside,escape]" } }
817
+ ],
818
+ composition: [
819
+ { kind: "wire", from: "A.invoke", to: "P.toggle" },
820
+ { kind: "wire", from: "P.opened", to: "B.to", arg: "S.sel ?? first" },
821
+ { kind: "gate", machine: "D", activeWhen: "P.open" },
822
+ { kind: "wire", from: "D.dismissRequested", to: "P.close" },
823
+ { kind: "wire", from: "option-activate", to: "S.select", arg: "B.active" },
824
+ { kind: "wire", from: "S.change", to: "P.close", when: "closeOnSelect" }
825
+ ],
826
+ exposes: [
827
+ { state: "open", when: "P.open" },
828
+ { state: "selected", when: "S.selected" },
829
+ { state: "active", when: "B.active" }
830
+ ],
831
+ emits: [{ name: "change", from: "S.change" }],
832
+ realize: [
833
+ {
834
+ target: "web",
835
+ surface: "popover",
836
+ browse: "roving",
837
+ commit: "explicit",
838
+ note: "place bottom-start; flip; matchWidth; commit on click/enter"
839
+ },
840
+ {
841
+ target: "ios",
842
+ when: "style=wheel",
843
+ surface: "inline-wheel",
844
+ browse: "scroll-detent",
845
+ commit: "continuous",
846
+ drop: ["activation", "dismiss"],
847
+ wire: [{ from: "B.active-changed", to: "S.select", arg: "B.active" }]
848
+ }
849
+ ],
850
+ realizationNote: "web popover-listbox \xB7 Android dropdown/dialog \xB7 iOS menu/sheet/push/wheel"
851
+ },
852
+ {
853
+ kind: "archetype",
854
+ name: "MultiSelect",
855
+ group: "Inputs",
856
+ contract: "subset of a set",
857
+ produces: { level: "region", interactive: false },
858
+ anatomy: [
859
+ {
860
+ name: "trigger",
861
+ role: "control",
862
+ children: [
863
+ { name: "value", role: "text" },
864
+ { name: "indicator", role: "icon" }
865
+ ]
866
+ },
867
+ {
868
+ name: "surface",
869
+ role: "container",
870
+ children: [
871
+ {
872
+ name: "option",
873
+ role: "item",
874
+ children: [
875
+ { name: "check", role: "icon" },
876
+ { name: "label", role: "text" }
877
+ ]
878
+ }
879
+ ]
880
+ }
881
+ ],
882
+ composes: ["selection.multiple", "presentation", "browse", "dismiss"]
883
+ },
884
+ {
885
+ kind: "archetype",
886
+ name: "Combobox",
887
+ group: "Inputs",
888
+ contract: "filtered single-select w/ entry",
889
+ produces: { level: "region", interactive: false },
890
+ anatomy: [
891
+ { name: "control", role: "input" },
892
+ {
893
+ name: "surface",
894
+ role: "container",
895
+ children: [{ name: "option", role: "item", children: [{ name: "label", role: "text" }] }]
896
+ }
897
+ ],
898
+ composes: ["value-entry", "filter", "selection.single", "browse", "presentation", "dismiss"]
899
+ },
900
+ {
901
+ kind: "archetype",
902
+ name: "Slider",
903
+ group: "Inputs",
904
+ contract: "value(s) on a range",
905
+ produces: { level: "region", interactive: false },
906
+ anatomy: [
907
+ {
908
+ name: "track",
909
+ role: "container",
910
+ children: [
911
+ { name: "range", role: "container", optional: true },
912
+ { name: "thumb", role: "control" }
913
+ ]
914
+ },
915
+ { name: "value", role: "text", optional: true }
916
+ ],
917
+ composes: ["range", "gesture(pan)"]
918
+ },
919
+ {
920
+ kind: "archetype",
921
+ name: "Stepper",
922
+ group: "Inputs",
923
+ contract: "number \xB1",
924
+ produces: { level: "region", interactive: false },
925
+ anatomy: [
926
+ { name: "decrement", role: "control", children: [{ name: "icon", role: "icon" }] },
927
+ { name: "control", role: "input" },
928
+ { name: "increment", role: "control", children: [{ name: "icon", role: "icon" }] }
929
+ ],
930
+ composes: ["stepping", "value-entry"]
931
+ },
932
+ {
933
+ kind: "archetype",
934
+ name: "DatePicker",
935
+ group: "Inputs",
936
+ contract: "date value",
937
+ produces: { level: "region", interactive: false },
938
+ anatomy: [
939
+ {
940
+ name: "trigger",
941
+ role: "control",
942
+ children: [
943
+ { name: "value", role: "text" },
944
+ { name: "icon", role: "icon" }
945
+ ]
946
+ },
947
+ {
948
+ name: "surface",
949
+ role: "container",
950
+ children: [
951
+ {
952
+ name: "header",
953
+ role: "container",
954
+ children: [
955
+ { name: "prev", role: "control" },
956
+ { name: "title", role: "text" },
957
+ { name: "next", role: "control" }
958
+ ]
959
+ },
960
+ {
961
+ name: "grid",
962
+ role: "container",
963
+ children: [{ name: "day", role: "item", children: [{ name: "label", role: "text" }] }]
964
+ }
965
+ ]
966
+ }
967
+ ],
968
+ composes: ["selection.single", "presentation", "browse", "dismiss", "masking"],
969
+ realizationNote: "web calendar-grid \xB7 iOS wheel (momentum+detent+haptic) \xB7 Android dialog"
970
+ },
971
+ {
972
+ kind: "archetype",
973
+ name: "TimePicker",
974
+ group: "Inputs",
975
+ contract: "time value",
976
+ produces: { level: "region", interactive: false },
977
+ anatomy: [
978
+ {
979
+ name: "trigger",
980
+ role: "control",
981
+ children: [
982
+ { name: "value", role: "text" },
983
+ { name: "icon", role: "icon" }
984
+ ]
985
+ },
986
+ {
987
+ name: "surface",
988
+ role: "container",
989
+ children: [
990
+ {
991
+ name: "column",
992
+ role: "item",
993
+ children: [
994
+ { name: "option", role: "item", children: [{ name: "label", role: "text" }] }
995
+ ]
996
+ }
997
+ ]
998
+ }
999
+ ],
1000
+ composes: ["selection.single", "presentation", "browse", "dismiss", "masking"],
1001
+ realizationNote: "iOS wheel \xB7 Android dialog"
1002
+ },
1003
+ {
1004
+ kind: "archetype",
1005
+ name: "ColorPicker",
1006
+ group: "Inputs",
1007
+ contract: "color value",
1008
+ produces: { level: "region", interactive: false },
1009
+ anatomy: [
1010
+ {
1011
+ name: "trigger",
1012
+ role: "control",
1013
+ children: [
1014
+ { name: "swatch", role: "icon" },
1015
+ { name: "value", role: "text" }
1016
+ ]
1017
+ },
1018
+ {
1019
+ name: "surface",
1020
+ role: "container",
1021
+ children: [
1022
+ { name: "area", role: "container" },
1023
+ { name: "hue", role: "container" },
1024
+ { name: "input", role: "input" }
1025
+ ]
1026
+ }
1027
+ ],
1028
+ composes: ["range", "value-entry", "presentation", "dismiss"]
1029
+ },
1030
+ {
1031
+ kind: "archetype",
1032
+ name: "FileUpload",
1033
+ group: "Inputs",
1034
+ contract: "file(s)",
1035
+ produces: { level: "region", interactive: false },
1036
+ anatomy: [
1037
+ {
1038
+ name: "dropzone",
1039
+ role: "container",
1040
+ children: [
1041
+ { name: "icon", role: "icon" },
1042
+ { name: "label", role: "text" }
1043
+ ]
1044
+ },
1045
+ { name: "input", role: "input" },
1046
+ {
1047
+ name: "list",
1048
+ role: "container",
1049
+ optional: true,
1050
+ children: [
1051
+ {
1052
+ name: "item",
1053
+ role: "item",
1054
+ children: [
1055
+ { name: "name", role: "text" },
1056
+ { name: "remove", role: "control" }
1057
+ ]
1058
+ }
1059
+ ]
1060
+ }
1061
+ ],
1062
+ composes: ["activation", "gesture(drop)?", "async-state"]
1063
+ },
1064
+ {
1065
+ kind: "archetype",
1066
+ name: "TagsInput",
1067
+ group: "Inputs",
1068
+ contract: "list of tokens",
1069
+ produces: { level: "region", interactive: false },
1070
+ anatomy: [
1071
+ {
1072
+ name: "tag",
1073
+ role: "item",
1074
+ children: [
1075
+ { name: "label", role: "text" },
1076
+ { name: "remove", role: "control" }
1077
+ ]
1078
+ },
1079
+ { name: "control", role: "input" }
1080
+ ],
1081
+ composes: ["value-entry", "selection.multiple"]
1082
+ },
1083
+ {
1084
+ kind: "archetype",
1085
+ name: "Rating",
1086
+ group: "Inputs",
1087
+ contract: "discrete score",
1088
+ produces: { level: "region", interactive: false },
1089
+ anatomy: [{ name: "item", role: "item", children: [{ name: "icon", role: "icon" }] }],
1090
+ composes: ["selection.single", "pressability"]
1091
+ },
1092
+ {
1093
+ kind: "archetype",
1094
+ name: "Disclosure",
1095
+ group: "Disclosure",
1096
+ contract: "expand region (a.k.a. Collapsible)",
1097
+ produces: { level: "region", interactive: false },
1098
+ anatomy: [
1099
+ {
1100
+ name: "trigger",
1101
+ role: "control",
1102
+ children: [
1103
+ { name: "label", role: "text" },
1104
+ { name: "indicator", role: "icon" }
1105
+ ]
1106
+ },
1107
+ { name: "panel", role: "container" }
1108
+ ],
1109
+ composes: ["disclosure"]
1110
+ },
1111
+ {
1112
+ kind: "archetype",
1113
+ name: "Accordion",
1114
+ group: "Disclosure",
1115
+ contract: "group of disclosures (single/multi)",
1116
+ produces: { level: "region", interactive: false },
1117
+ anatomy: [
1118
+ {
1119
+ name: "item",
1120
+ role: "item",
1121
+ children: [
1122
+ {
1123
+ name: "header",
1124
+ role: "control",
1125
+ children: [
1126
+ { name: "label", role: "text" },
1127
+ { name: "indicator", role: "icon" }
1128
+ ]
1129
+ },
1130
+ { name: "panel", role: "container" }
1131
+ ]
1132
+ }
1133
+ ],
1134
+ composes: ["disclosure", "selection"]
1135
+ },
1136
+ {
1137
+ kind: "archetype",
1138
+ name: "Tabs",
1139
+ group: "Disclosure",
1140
+ contract: "switch panels",
1141
+ produces: { level: "region", interactive: false },
1142
+ anatomy: [
1143
+ {
1144
+ name: "list",
1145
+ role: "container",
1146
+ children: [{ name: "tab", role: "control", children: [{ name: "label", role: "text" }] }]
1147
+ },
1148
+ { name: "panel", role: "container" }
1149
+ ],
1150
+ composes: ["navigation", "browse"],
1151
+ realizationNote: "mobile: swipe-between; web: no swipe"
1152
+ },
1153
+ {
1154
+ kind: "archetype",
1155
+ name: "Dialog",
1156
+ group: "Disclosure",
1157
+ contract: "blocking layer (a.k.a. Modal)",
1158
+ produces: { level: "region", interactive: false },
1159
+ anatomy: [
1160
+ { name: "trigger", role: "control", children: [{ name: "label", role: "text" }] },
1161
+ { name: "backdrop", role: "container", optional: true },
1162
+ {
1163
+ name: "surface",
1164
+ role: "container",
1165
+ children: [
1166
+ { name: "title", role: "text" },
1167
+ { name: "content", role: "container" },
1168
+ { name: "close", role: "control", optional: true }
1169
+ ]
1170
+ }
1171
+ ],
1172
+ composes: ["presentation(dialog)", "focus-scope", "dismiss", "backdrop"],
1173
+ realizationNote: "iOS sheet w/ detents"
1174
+ },
1175
+ {
1176
+ kind: "archetype",
1177
+ name: "Drawer",
1178
+ group: "Disclosure",
1179
+ contract: "edge-anchored panel (a.k.a. Sidebar)",
1180
+ produces: { level: "region", interactive: false },
1181
+ anatomy: [
1182
+ { name: "trigger", role: "control" },
1183
+ { name: "backdrop", role: "container", optional: true },
1184
+ {
1185
+ name: "surface",
1186
+ role: "container",
1187
+ children: [
1188
+ { name: "content", role: "container" },
1189
+ { name: "close", role: "control", optional: true }
1190
+ ]
1191
+ }
1192
+ ],
1193
+ composes: ["presentation(sheet)", "focus-scope", "dismiss", "gesture(swipe)?"]
1194
+ },
1195
+ {
1196
+ kind: "archetype",
1197
+ name: "Popover",
1198
+ group: "Disclosure",
1199
+ contract: "anchored transient layer",
1200
+ produces: { level: "region", interactive: false },
1201
+ anatomy: [
1202
+ { name: "trigger", role: "control" },
1203
+ { name: "surface", role: "container", children: [{ name: "content", role: "container" }] }
1204
+ ],
1205
+ composes: ["presentation(popover)", "anchoring", "dismiss"]
1206
+ },
1207
+ {
1208
+ kind: "archetype",
1209
+ name: "Tooltip",
1210
+ group: "Disclosure",
1211
+ contract: "hover/focus hint",
1212
+ produces: { level: "region", interactive: false },
1213
+ anatomy: [
1214
+ { name: "trigger", role: "control" },
1215
+ { name: "surface", role: "container", children: [{ name: "content", role: "text" }] }
1216
+ ],
1217
+ composes: ["presentation(popover)", "anchoring"],
1218
+ realizationNote: "requires input.pointer \u2192 tap/long-press fallback"
1219
+ },
1220
+ {
1221
+ kind: "archetype",
1222
+ name: "ContextMenu",
1223
+ group: "Disclosure",
1224
+ contract: "secondary-action menu",
1225
+ produces: { level: "region", interactive: false },
1226
+ anatomy: [
1227
+ {
1228
+ name: "surface",
1229
+ role: "container",
1230
+ children: [
1231
+ {
1232
+ name: "item",
1233
+ role: "item",
1234
+ children: [
1235
+ { name: "icon", role: "icon", optional: true },
1236
+ { name: "label", role: "text" }
1237
+ ]
1238
+ }
1239
+ ]
1240
+ }
1241
+ ],
1242
+ composes: ["presentation(menu)", "browse", "dismiss"],
1243
+ realizationNote: "right-click / long-press"
1244
+ },
1245
+ {
1246
+ kind: "archetype",
1247
+ name: "Menu",
1248
+ group: "Navigation",
1249
+ contract: "hierarchical actions (a.k.a. MenuBar)",
1250
+ produces: { level: "region", interactive: false },
1251
+ anatomy: [
1252
+ {
1253
+ name: "surface",
1254
+ role: "container",
1255
+ children: [
1256
+ {
1257
+ name: "item",
1258
+ role: "item",
1259
+ children: [
1260
+ { name: "icon", role: "icon", optional: true },
1261
+ { name: "label", role: "text" }
1262
+ ]
1263
+ }
1264
+ ]
1265
+ }
1266
+ ],
1267
+ composes: ["presentation", "browse", "typeahead?", "dismiss"]
1268
+ },
1269
+ {
1270
+ kind: "archetype",
1271
+ name: "NavigationBar",
1272
+ group: "Navigation",
1273
+ contract: "top-level destinations",
1274
+ produces: { level: "region", interactive: false },
1275
+ anatomy: [
1276
+ {
1277
+ name: "item",
1278
+ role: "item",
1279
+ children: [
1280
+ { name: "icon", role: "icon", optional: true },
1281
+ { name: "label", role: "text" }
1282
+ ]
1283
+ }
1284
+ ],
1285
+ composes: ["navigation"],
1286
+ realizationNote: "web top-nav \xB7 iOS tab-bar \xB7 Android bottom-nav/rail"
1287
+ },
1288
+ {
1289
+ kind: "archetype",
1290
+ name: "Breadcrumbs",
1291
+ group: "Navigation",
1292
+ contract: "path trail",
1293
+ produces: { level: "region", interactive: false },
1294
+ anatomy: [
1295
+ { name: "item", role: "item", children: [{ name: "label", role: "text" }] },
1296
+ { name: "separator", role: "icon", optional: true }
1297
+ ],
1298
+ composes: ["navigation"],
1299
+ realizationNote: "iOS: \u2192 nav title + back"
1300
+ },
1301
+ {
1302
+ kind: "archetype",
1303
+ name: "Pagination",
1304
+ group: "Navigation",
1305
+ contract: "page index",
1306
+ produces: { level: "region", interactive: false },
1307
+ anatomy: [
1308
+ { name: "previous", role: "control" },
1309
+ { name: "item", role: "item", children: [{ name: "label", role: "text" }] },
1310
+ { name: "next", role: "control" }
1311
+ ],
1312
+ composes: ["navigation", "stepping"]
1313
+ },
1314
+ {
1315
+ kind: "archetype",
1316
+ name: "Tree",
1317
+ group: "Navigation",
1318
+ contract: "hierarchical collection",
1319
+ produces: { level: "region", interactive: false },
1320
+ anatomy: [
1321
+ {
1322
+ name: "item",
1323
+ role: "item",
1324
+ children: [
1325
+ { name: "indicator", role: "icon" },
1326
+ { name: "label", role: "text" }
1327
+ ]
1328
+ }
1329
+ ],
1330
+ composes: ["disclosure", "browse", "selection"]
1331
+ },
1332
+ {
1333
+ kind: "archetype",
1334
+ name: "Wizard",
1335
+ group: "Navigation",
1336
+ contract: "ordered flow (a.k.a. Stepper-nav)",
1337
+ produces: { level: "region", interactive: false },
1338
+ anatomy: [
1339
+ {
1340
+ name: "step",
1341
+ role: "item",
1342
+ children: [
1343
+ { name: "indicator", role: "icon" },
1344
+ { name: "label", role: "text" }
1345
+ ]
1346
+ },
1347
+ { name: "content", role: "container" },
1348
+ { name: "back", role: "control" },
1349
+ { name: "next", role: "control" }
1350
+ ],
1351
+ composes: ["stepping-nav"]
1352
+ },
1353
+ {
1354
+ kind: "archetype",
1355
+ name: "List",
1356
+ group: "Collections",
1357
+ contract: "ordered items, optional select (a.k.a. ListBox)",
1358
+ produces: { level: "region", interactive: false },
1359
+ anatomy: [{ name: "item", role: "item", children: [{ name: "content", role: "container" }] }],
1360
+ composes: ["browse", "selection?", "virtualization?"],
1361
+ realizationNote: "iOS list: swipe-to-delete (gesture)"
1362
+ },
1363
+ {
1364
+ kind: "archetype",
1365
+ name: "Table",
1366
+ group: "Collections",
1367
+ contract: "rows\xD7cols, sort/select (a.k.a. DataGrid)",
1368
+ produces: { level: "region", interactive: false },
1369
+ anatomy: [
1370
+ {
1371
+ name: "header",
1372
+ role: "container",
1373
+ children: [{ name: "column", role: "item", children: [{ name: "label", role: "text" }] }]
1374
+ },
1375
+ {
1376
+ name: "body",
1377
+ role: "container",
1378
+ children: [{ name: "row", role: "item", children: [{ name: "cell", role: "item" }] }]
1379
+ }
1380
+ ],
1381
+ composes: ["selection?", "ordering", "browse", "resize?", "virtualization?"],
1382
+ realizationNote: "aggregation/grouping is data, not a behavior"
1383
+ },
1384
+ {
1385
+ kind: "archetype",
1386
+ name: "Carousel",
1387
+ group: "Collections",
1388
+ contract: "paged horizontal items",
1389
+ produces: { level: "region", interactive: false },
1390
+ anatomy: [
1391
+ { name: "viewport", role: "container", children: [{ name: "slide", role: "item" }] },
1392
+ { name: "previous", role: "control", optional: true },
1393
+ { name: "next", role: "control", optional: true }
1394
+ ],
1395
+ composes: ["browse", "momentum-scroll", "gesture(swipe)"]
1396
+ },
1397
+ {
1398
+ kind: "archetype",
1399
+ name: "Grid",
1400
+ group: "Collections",
1401
+ contract: "2D arrangement of items",
1402
+ produces: { level: "region", interactive: false },
1403
+ anatomy: [{ name: "item", role: "item" }],
1404
+ composes: ["browse", "selection?"]
1405
+ },
1406
+ {
1407
+ kind: "archetype",
1408
+ name: "Toast",
1409
+ group: "Feedback",
1410
+ contract: "transient message (a.k.a. Snackbar)",
1411
+ produces: { level: "region", interactive: false },
1412
+ anatomy: [
1413
+ { name: "icon", role: "icon", optional: true },
1414
+ { name: "content", role: "text" },
1415
+ { name: "action", role: "control", optional: true },
1416
+ { name: "close", role: "control", optional: true }
1417
+ ],
1418
+ composes: ["presentation", "auto-dismiss", "liveness"],
1419
+ realizationNote: "iOS: top banner; Android: snackbar bottom"
1420
+ },
1421
+ {
1422
+ kind: "archetype",
1423
+ name: "Alert",
1424
+ group: "Feedback",
1425
+ contract: "inline persistent message (a.k.a. Banner)",
1426
+ produces: { level: "region", interactive: false },
1427
+ anatomy: [
1428
+ { name: "icon", role: "icon", optional: true },
1429
+ { name: "title", role: "text", optional: true },
1430
+ { name: "content", role: "text" },
1431
+ { name: "close", role: "control", optional: true }
1432
+ ],
1433
+ composes: ["liveness"]
1434
+ },
1435
+ {
1436
+ kind: "archetype",
1437
+ name: "ProgressBar",
1438
+ group: "Feedback",
1439
+ contract: "completion feedback (a.k.a. Spinner)",
1440
+ produces: { level: "region", interactive: false },
1441
+ anatomy: [
1442
+ { name: "track", role: "container", children: [{ name: "indicator", role: "container" }] },
1443
+ { name: "label", role: "text", optional: true }
1444
+ ],
1445
+ composes: ["progress", "liveness"]
1446
+ },
1447
+ {
1448
+ kind: "archetype",
1449
+ name: "Skeleton",
1450
+ group: "Feedback",
1451
+ contract: "loading placeholder",
1452
+ produces: { level: "region", interactive: false },
1453
+ composes: ["async-state"]
1454
+ },
1455
+ {
1456
+ kind: "archetype",
1457
+ name: "Badge",
1458
+ group: "Feedback",
1459
+ contract: "numeric/status decoration",
1460
+ produces: { level: "phrase", interactive: false },
1461
+ anatomy: [{ name: "label", role: "text" }],
1462
+ composes: []
1463
+ },
1464
+ {
1465
+ kind: "archetype",
1466
+ name: "Meter",
1467
+ group: "Feedback",
1468
+ contract: "numeric gauge",
1469
+ produces: { level: "region", interactive: false },
1470
+ anatomy: [
1471
+ { name: "track", role: "container", children: [{ name: "indicator", role: "container" }] },
1472
+ { name: "label", role: "text", optional: true }
1473
+ ],
1474
+ composes: []
1475
+ },
1476
+ {
1477
+ kind: "archetype",
1478
+ name: "EmptyState",
1479
+ group: "Feedback",
1480
+ contract: "no-content placeholder",
1481
+ produces: { level: "region", interactive: false },
1482
+ anatomy: [
1483
+ { name: "icon", role: "icon", optional: true },
1484
+ { name: "title", role: "text" },
1485
+ { name: "description", role: "text", optional: true },
1486
+ { name: "action", role: "control", optional: true }
1487
+ ],
1488
+ composes: []
1489
+ },
1490
+ {
1491
+ kind: "archetype",
1492
+ name: "Avatar",
1493
+ group: "Media",
1494
+ contract: "identity image/initials",
1495
+ produces: { level: "phrase", interactive: false },
1496
+ anatomy: [
1497
+ { name: "image", role: "content" },
1498
+ { name: "fallback", role: "text", optional: true },
1499
+ { name: "badge", role: "icon", optional: true }
1500
+ ],
1501
+ composes: []
1502
+ },
1503
+ {
1504
+ kind: "archetype",
1505
+ name: "Image",
1506
+ group: "Media",
1507
+ contract: "bitmap w/ alt + fit",
1508
+ produces: { level: "phrase", interactive: false },
1509
+ composes: []
1510
+ },
1511
+ {
1512
+ kind: "archetype",
1513
+ name: "Icon",
1514
+ group: "Media",
1515
+ contract: "vector glyph from a set",
1516
+ produces: { level: "phrase", interactive: false },
1517
+ composes: []
1518
+ },
1519
+ {
1520
+ kind: "archetype",
1521
+ name: "AspectRatio",
1522
+ group: "Media",
1523
+ contract: "ratio-locked box",
1524
+ produces: { level: "region", interactive: false },
1525
+ anatomy: [{ name: "content", role: "container" }],
1526
+ composes: [],
1527
+ realizationNote: "layout facet, not a behavioral archetype"
1528
+ }
1529
+ ];
1530
+ var facets = [
1531
+ { name: "fill", covers: "surface/background" },
1532
+ { name: "ink", covers: "text + icon foreground" },
1533
+ {
1534
+ name: "border",
1535
+ covers: "edge: color + width (hairline|thin|thick) + style",
1536
+ divergence: "iOS often overlay-stroke"
1537
+ },
1538
+ { name: "corner", covers: "corner radius (token role)" },
1539
+ { name: "inset", covers: "internal padding (block/inline)" },
1540
+ { name: "gap", covers: "spacing between children" },
1541
+ {
1542
+ name: "text",
1543
+ covers: "typography role (body|caption|title\u2026) \u2014 bundles size/weight/line/tracking",
1544
+ divergence: "iOS Dynamic Type = OS-resolved role"
1545
+ },
1546
+ {
1547
+ name: "textClamp",
1548
+ covers: "truncate to N lines + ellipsis",
1549
+ divergence: "web -webkit-line-clamp \xB7 .lineLimit \xB7 maxLines+overflow"
1550
+ },
1551
+ { name: "textWrap", covers: "wrap | nowrap | break" },
1552
+ {
1553
+ name: "elevation",
1554
+ covers: "depth",
1555
+ divergence: "web box-shadow \xB7 Android tonal-elevation \xB7 iOS none/sheet"
1556
+ },
1557
+ {
1558
+ name: "overlay",
1559
+ covers: "state layer (Material hover/press tint)",
1560
+ divergence: "Material-specific; web \u2248 bg shift"
1561
+ },
1562
+ { name: "opacity", covers: "transparency" },
1563
+ {
1564
+ name: "ring",
1565
+ covers: "focus indicator",
1566
+ divergence: "desktop ring \xB7 mobile none \xB7 tv strong highlight"
1567
+ },
1568
+ { name: "backdrop", covers: "scrim / blur behind layer", divergence: "iOS material/vibrancy" }
1569
+ ];
1570
+ var states = [
1571
+ { name: "pressed", from: "pressability" },
1572
+ { name: "hovered", from: "pressability" },
1573
+ { name: "focused", from: "value-entry" },
1574
+ { name: "active", from: "browse" },
1575
+ { name: "selected", from: "selection" },
1576
+ { name: "checked", from: "toggle" },
1577
+ { name: "expanded", from: "disclosure" },
1578
+ { name: "open", from: "presentation" },
1579
+ { name: "disabled", from: "ambient (global gate)" },
1580
+ { name: "readonly", from: "ambient" },
1581
+ { name: "loading", from: "async-state" },
1582
+ { name: "error", from: "async-state" },
1583
+ { name: "success", from: "async-state" },
1584
+ { name: "invalid", from: "validation" },
1585
+ { name: "valid", from: "validation" },
1586
+ { name: "indeterminate", from: "toggle (tristate)" },
1587
+ { name: "dragging", from: "gesture" },
1588
+ { name: "empty", from: "value-entry" },
1589
+ { name: "complete", from: "progress / stepping-nav" },
1590
+ { name: "first", from: "collection-position" },
1591
+ { name: "last", from: "collection-position" },
1592
+ { name: "odd", from: "collection-position" },
1593
+ { name: "even", from: "collection-position" }
1594
+ ];
1595
+ var capabilities = [
1596
+ { name: "input.keyboard" },
1597
+ { name: "input.pointer", note: "hover" },
1598
+ { name: "input.touch" },
1599
+ { name: "input.remote", note: "tv focus engine" },
1600
+ {
1601
+ name: "input.voice",
1602
+ status: "aspirational",
1603
+ note: "aspirational \u2014 declared but consumed by no atom yet (no voice/turn-taking machine in v0); see NOTES-modality-coverage"
1604
+ },
1605
+ { name: "motion.haptic" },
1606
+ { name: "runtime.dynamic-color", note: "Material You" },
1607
+ { name: "display.density" },
1608
+ { name: "a11y.screenreader" },
1609
+ { name: "platform.web" },
1610
+ { name: "platform.ios" },
1611
+ { name: "platform.android" },
1612
+ { name: "platform.tvos" }
1613
+ ];
1614
+ var decorations = [
1615
+ {
1616
+ name: "motion",
1617
+ note: "decorates existing transitions (spring/flip/fade+slide); adds no states"
1618
+ },
1619
+ {
1620
+ name: "masking",
1621
+ note: "format/constrain entry (date/phone/currency) \u2014 decorates value-entry draft"
1622
+ },
1623
+ {
1624
+ name: "anchoring",
1625
+ note: "position a surface relative to a reference (placement/flip/offset/match-size) \u2014 decorates presentation(popover|menu)"
1626
+ },
1627
+ {
1628
+ name: "backdrop",
1629
+ note: "scrim / blur behind a layer \u2014 decorates a modal presentation; painted via the `backdrop` facet on the backdrop anatomy part"
1630
+ },
1631
+ {
1632
+ name: "momentum-scroll",
1633
+ note: "inertial scroll with snap / detent \u2014 decorates scroll/browse on touch surfaces"
1634
+ }
1635
+ ];
1636
+ var nonBehaviors = [
1637
+ { name: "filter", note: "derived view over a collection (data, not interaction)" },
1638
+ { name: "virtualization", note: "render windowing" },
1639
+ { name: "ordering", note: "collection mutation (reorder/sort)" },
1640
+ { name: "aggregation", note: "data transformation (grouping/totals) \u2014 DataGrid math" }
1641
+ ];
1642
+ var preludeV0 = {
1643
+ milda: "0.0.1",
1644
+ kind: "prelude",
1645
+ prelude: "0.0.1",
1646
+ meta: {
1647
+ id: "prelude.milda",
1648
+ name: "Milda Prelude",
1649
+ version: "0.0.2",
1650
+ description: "Built-in standard library: atoms, archetypes, facets, states, capabilities."
1651
+ },
1652
+ atoms,
1653
+ composites,
1654
+ archetypes,
1655
+ facets,
1656
+ states,
1657
+ capabilities,
1658
+ decorations,
1659
+ nonBehaviors
1660
+ };
1661
+ var atomsByName = Object.fromEntries(atoms.map((a) => [a.name, a]));
1662
+ var archetypesByName = Object.fromEntries(
1663
+ archetypes.map((a) => [a.name, a])
1664
+ );
1665
+
1666
+ // engine/resolveToken.ts
1667
+ function resolveToken(tokenId, contextIds, layers, _depth = 0) {
1668
+ if (_depth > 20) return null;
1669
+ const ids = typeof contextIds === "string" ? [contextIds] : contextIds;
1670
+ const token = findToken(tokenId, layers)?.token;
1671
+ if (!token) return null;
1672
+ const v = token.value;
1673
+ if (v.kind === "raw") return { kind: "value", type: token.type, value: v.value };
1674
+ if (v.kind === "escape") return { kind: "escape", raw: v.raw, note: v.note };
1675
+ const ref = ids.reduce((found, id) => found ?? v.refs[id], void 0) ?? v.refs["default"] ?? null;
1676
+ if (!ref) return null;
1677
+ return resolveToken(ref, contextIds, layers, _depth + 1);
1678
+ }
1679
+ function findToken(tokenId, layers) {
1680
+ for (const layer of layers) {
1681
+ const token = layer.tokens.find((t) => t.id === tokenId);
1682
+ if (token) return { token, layerId: layer.id };
1683
+ }
1684
+ return null;
1685
+ }
1686
+ function allTokensOfType(type, layers) {
1687
+ const sorted = [...layers].sort((a, b) => a.order - b.order);
1688
+ const result = [];
1689
+ for (const layer of sorted) {
1690
+ for (const token of layer.tokens) {
1691
+ if (token.type === type) result.push({ token, layerId: layer.id, layerName: layer.name });
1692
+ }
1693
+ }
1694
+ return result;
1695
+ }
1696
+
1697
+ // engine/resolveFacets.ts
1698
+ function resolveFacets(input) {
1699
+ const { part, activeStates } = input;
1700
+ let result = { ...part.facets ?? {} };
1701
+ const states2 = part.states ?? {};
1702
+ const order = input.stateOrder ?? Object.keys(states2);
1703
+ for (const s of order) {
1704
+ const override = activeStates.has(s) ? states2[s]?.facets : void 0;
1705
+ if (override) result = { ...result, ...override };
1706
+ }
1707
+ return result;
1708
+ }
1709
+ function resolveLayout(input) {
1710
+ const { part, activeStates } = input;
1711
+ let result = part.layout ? { ...part.layout } : void 0;
1712
+ const states2 = part.states ?? {};
1713
+ const order = input.stateOrder ?? Object.keys(states2);
1714
+ for (const s of order) {
1715
+ const override = activeStates.has(s) ? states2[s]?.layout : void 0;
1716
+ if (override) result = { ...result ?? {}, ...override };
1717
+ }
1718
+ return result;
1719
+ }
1720
+ function collectActiveStates(flags) {
1721
+ const active = /* @__PURE__ */ new Set();
1722
+ for (const [state, enabled] of Object.entries(flags)) {
1723
+ if (enabled) active.add(state);
1724
+ }
1725
+ return active;
1726
+ }
1727
+
1728
+ // engine/validate.ts
1729
+ function facetTokenRefs(facets2) {
1730
+ if (!facets2) return [];
1731
+ const ids = [];
1732
+ for (const v of Object.values(facets2)) {
1733
+ if (!v) continue;
1734
+ if ("token" in v) ids.push(v.token);
1735
+ else if ("inset" in v) {
1736
+ for (const edge of Object.values(v.inset)) if (typeof edge === "string") ids.push(edge);
1737
+ }
1738
+ }
1739
+ return ids;
1740
+ }
1741
+ function layoutTokenRefs(layout) {
1742
+ if (!layout) return [];
1743
+ const ids = [];
1744
+ if (layout.gap) ids.push(layout.gap);
1745
+ if (layout.inset) {
1746
+ for (const edge of Object.values(layout.inset)) if (typeof edge === "string") ids.push(edge);
1747
+ }
1748
+ for (const axis of [layout.size?.width, layout.size?.height]) {
1749
+ if (axis && typeof axis === "object") {
1750
+ const intent = "intent" in axis ? axis.intent : axis;
1751
+ if (intent && typeof intent === "object" && "fixed" in intent) ids.push(intent.fixed);
1752
+ if ("min" in axis && axis.min) ids.push(axis.min);
1753
+ if ("max" in axis && axis.max) ids.push(axis.max);
1754
+ }
1755
+ }
1756
+ return ids;
1757
+ }
1758
+ function validate(doc, prelude) {
1759
+ const issues = [];
1760
+ const archetypeNames = new Set(prelude.archetypes.map((a) => a.name));
1761
+ const facetNames = new Set(prelude.facets.map((f) => f.name));
1762
+ const stateNames = new Set(prelude.states.map((s) => s.name));
1763
+ const groupIds = new Set(doc.foundations.contextGroups.map((g) => g.id));
1764
+ const layers = doc.foundations.layers;
1765
+ for (const layer of layers) {
1766
+ for (const tok of layer.tokens) {
1767
+ if (tok.value.kind === "alias") {
1768
+ const where = `token ${tok.name} (${tok.id})`;
1769
+ if (!groupIds.has(tok.value.by)) {
1770
+ issues.push({
1771
+ severity: "error",
1772
+ code: "alias.unknownGroup",
1773
+ message: `alias by unknown context group "${tok.value.by}"`,
1774
+ where
1775
+ });
1776
+ }
1777
+ for (const refId of Object.values(tok.value.refs)) {
1778
+ if (!findToken(refId, layers)) {
1779
+ issues.push({
1780
+ severity: "error",
1781
+ code: "alias.danglingRef",
1782
+ message: `alias ref points at missing token "${refId}"`,
1783
+ where
1784
+ });
1785
+ }
1786
+ }
1787
+ }
1788
+ }
1789
+ }
1790
+ for (const comp of doc.components) {
1791
+ const where = `component ${comp.name} (${comp.id})`;
1792
+ if (!archetypeNames.has(comp.archetype)) {
1793
+ issues.push({
1794
+ severity: "error",
1795
+ code: "component.unknownArchetype",
1796
+ message: `unknown archetype "${comp.archetype}"`,
1797
+ where
1798
+ });
1799
+ }
1800
+ const propIds = new Set(comp.contract.props.map((p) => p.id));
1801
+ for (const [param, binding] of Object.entries(comp.contract.params)) {
1802
+ if ("prop" in binding && !propIds.has(binding.prop)) {
1803
+ issues.push({
1804
+ severity: "error",
1805
+ code: "param.danglingProp",
1806
+ message: `param "${param}" binds missing prop "${binding.prop}"`,
1807
+ where
1808
+ });
1809
+ }
1810
+ }
1811
+ for (const [partName, part] of Object.entries(comp.anatomy)) {
1812
+ checkPart(comp, partName, part, { facetNames, stateNames, layers, issues });
1813
+ }
1814
+ }
1815
+ return issues;
1816
+ }
1817
+ function checkPart(comp, partName, part, ctx) {
1818
+ const where = `${comp.name} \u203A part ${partName}`;
1819
+ const { facetNames, stateNames, layers, issues } = ctx;
1820
+ const checkFacets = (facets2, scope) => {
1821
+ if (!facets2) return;
1822
+ for (const name of Object.keys(facets2)) {
1823
+ if (!facetNames.has(name)) {
1824
+ issues.push({
1825
+ severity: "error",
1826
+ code: "facet.unknown",
1827
+ message: `unknown facet "${name}"`,
1828
+ where: scope
1829
+ });
1830
+ }
1831
+ }
1832
+ for (const id of facetTokenRefs(facets2)) {
1833
+ if (!findToken(id, layers)) {
1834
+ issues.push({
1835
+ severity: "error",
1836
+ code: "facet.danglingToken",
1837
+ message: `facet references missing token "${id}"`,
1838
+ where: scope
1839
+ });
1840
+ }
1841
+ }
1842
+ };
1843
+ const checkLayoutRefs = (layout, scope) => {
1844
+ for (const id of layoutTokenRefs(layout)) {
1845
+ if (!findToken(id, layers)) {
1846
+ issues.push({
1847
+ severity: "error",
1848
+ code: "layout.danglingToken",
1849
+ message: `layout references missing token "${id}"`,
1850
+ where: scope
1851
+ });
1852
+ }
1853
+ }
1854
+ };
1855
+ checkFacets(part.facets, where);
1856
+ checkLayoutRefs(part.layout, where);
1857
+ for (const [stateName, override] of Object.entries(part.states ?? {})) {
1858
+ const scope = `${where} [${stateName}]`;
1859
+ if (!stateNames.has(stateName)) {
1860
+ issues.push({
1861
+ severity: "error",
1862
+ code: "state.unknown",
1863
+ message: `unknown state "${stateName}"`,
1864
+ where: scope
1865
+ });
1866
+ }
1867
+ checkFacets(override?.facets, scope);
1868
+ checkLayoutRefs(override?.layout, scope);
1869
+ }
1870
+ }
1871
+
1872
+ // engine/conformance.ts
1873
+ function walkFacets(facets2, where, opts, holes) {
1874
+ if (!facets2) return;
1875
+ for (const [name, value] of Object.entries(facets2)) {
1876
+ if (!value) continue;
1877
+ if ("raw" in value) {
1878
+ holes.push({
1879
+ kind: "facet",
1880
+ name,
1881
+ where,
1882
+ reason: "raw escape-hatch value is a platform-specific leaf"
1883
+ });
1884
+ } else if ("token" in value) {
1885
+ const resolved = resolveToken(value.token, opts.contextIds, opts.layers);
1886
+ if (resolved === null) {
1887
+ holes.push({
1888
+ kind: "facet",
1889
+ name,
1890
+ where,
1891
+ reason: `token "${value.token}" does not resolve for target`
1892
+ });
1893
+ } else if (resolved.kind === "escape") {
1894
+ holes.push({
1895
+ kind: "facet",
1896
+ name,
1897
+ where,
1898
+ reason: `token "${value.token}" resolves to an escape-hatch leaf for this target`
1899
+ });
1900
+ }
1901
+ }
1902
+ }
1903
+ }
1904
+ function walkPart(comp, partName, part, opts, holes) {
1905
+ const where = `${comp.name} \u203A ${partName}`;
1906
+ walkFacets(part.facets, where, opts, holes);
1907
+ for (const [stateName, override] of Object.entries(part.states ?? {})) {
1908
+ walkFacets(override?.facets, `${where} [${stateName}]`, opts, holes);
1909
+ }
1910
+ }
1911
+ function conformance(comp, opts) {
1912
+ const holes = [];
1913
+ for (const [partName, part] of Object.entries(comp.anatomy)) {
1914
+ walkPart(comp, partName, part, opts, holes);
1915
+ }
1916
+ const substitutions = [];
1917
+ const realize = comp.realize.find((r) => r.target === opts.target);
1918
+ if (realize) {
1919
+ for (const dropped of realize.drop ?? [])
1920
+ substitutions.push({ behavior: dropped, via: "dropped" });
1921
+ for (const wire of realize.wire ?? [])
1922
+ substitutions.push({ behavior: `${wire.from} \u2192 ${wire.to}`, via: "realize wire" });
1923
+ }
1924
+ return { target: opts.target, complete: holes.length === 0, holes, substitutions };
1925
+ }
1926
+
1927
+ // dsl/lexer.ts
1928
+ var LexError = class extends Error {
1929
+ constructor(message, line, col) {
1930
+ super(`${message} (line ${line}:${col})`);
1931
+ this.line = line;
1932
+ this.col = col;
1933
+ this.name = "LexError";
1934
+ }
1935
+ };
1936
+ var isWordChar = (c) => /[A-Za-z0-9_./%-]/.test(c);
1937
+ var PUNCT = {
1938
+ "{": "lbrace",
1939
+ "}": "rbrace",
1940
+ "(": "lparen",
1941
+ ")": "rparen",
1942
+ "<": "lt",
1943
+ ">": "gt",
1944
+ ":": "colon",
1945
+ "=": "equals",
1946
+ ",": "comma",
1947
+ ";": "semi",
1948
+ "?": "question"
1949
+ };
1950
+ function lex(src) {
1951
+ const tokens = [];
1952
+ let i = 0;
1953
+ let line = 1;
1954
+ let col = 1;
1955
+ const n = src.length;
1956
+ const advance = (count = 1) => {
1957
+ for (let k = 0; k < count; k++) {
1958
+ if (src[i] === "\n") {
1959
+ line++;
1960
+ col = 1;
1961
+ } else {
1962
+ col++;
1963
+ }
1964
+ i++;
1965
+ }
1966
+ };
1967
+ const push = (type, value, startPos, startLine, startCol) => {
1968
+ tokens.push({ type, value, pos: startPos, line: startLine, col: startCol });
1969
+ };
1970
+ while (i < n) {
1971
+ const c = src[i];
1972
+ const startPos = i;
1973
+ const startLine = line;
1974
+ const startCol = col;
1975
+ if (c === "\n") {
1976
+ advance();
1977
+ if (tokens.length && tokens[tokens.length - 1].type !== "newline") {
1978
+ push("newline", "\n", startPos, startLine, startCol);
1979
+ }
1980
+ continue;
1981
+ }
1982
+ if (c === " " || c === " " || c === "\r") {
1983
+ advance();
1984
+ continue;
1985
+ }
1986
+ if (c === "#") {
1987
+ while (i < n && src[i] !== "\n") advance();
1988
+ continue;
1989
+ }
1990
+ if (c === '"') {
1991
+ advance();
1992
+ let value = "";
1993
+ while (i < n && src[i] !== '"') {
1994
+ if (src[i] === "\\" && i + 1 < n) {
1995
+ const next = src[i + 1];
1996
+ value += next === "n" ? "\n" : next === "t" ? " " : next;
1997
+ advance(2);
1998
+ continue;
1999
+ }
2000
+ if (src[i] === "\n") throw new LexError("Unterminated string", startLine, startCol);
2001
+ value += src[i];
2002
+ advance();
2003
+ }
2004
+ if (i >= n) throw new LexError("Unterminated string", startLine, startCol);
2005
+ advance();
2006
+ push("string", value, startPos, startLine, startCol);
2007
+ continue;
2008
+ }
2009
+ if (c === "-" && src[i + 1] === ">") {
2010
+ advance(2);
2011
+ push("arrow", "->", startPos, startLine, startCol);
2012
+ continue;
2013
+ }
2014
+ if (c === "\u2192") {
2015
+ advance();
2016
+ push("arrow", "->", startPos, startLine, startCol);
2017
+ continue;
2018
+ }
2019
+ if (PUNCT[c]) {
2020
+ advance();
2021
+ push(PUNCT[c], c, startPos, startLine, startCol);
2022
+ continue;
2023
+ }
2024
+ if (isWordChar(c)) {
2025
+ let value = "";
2026
+ while (i < n && isWordChar(src[i]) && !(src[i] === "-" && src[i + 1] === ">")) {
2027
+ value += src[i];
2028
+ advance();
2029
+ }
2030
+ push("word", value, startPos, startLine, startCol);
2031
+ continue;
2032
+ }
2033
+ advance();
2034
+ }
2035
+ push("eof", "", i, line, col);
2036
+ return tokens;
2037
+ }
2038
+
2039
+ // dsl/parse.ts
2040
+ var seg = (s) => s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "x";
2041
+ var cgId = (name) => `cg.${seg(name)}`;
2042
+ var ctxId = (groupName, ctxName) => `ctx.${seg(groupName)}.${seg(ctxName)}`;
2043
+ var tokId = (path) => `tok.${path.replace(/\//g, ".")}`;
2044
+ var stId = (name) => `st.${seg(name)}`;
2045
+ var cmpId = (name) => `cmp.${seg(name)}`;
2046
+ var FACET_MAP = {
2047
+ fill: "fill",
2048
+ ink: "ink",
2049
+ border: "border",
2050
+ corner: "corner",
2051
+ inset: "inset",
2052
+ gap: "gap",
2053
+ text: "text",
2054
+ "text.clamp": "textClamp",
2055
+ "text.wrap": "textWrap",
2056
+ elevation: "elevation",
2057
+ overlay: "overlay",
2058
+ opacity: "opacity",
2059
+ ring: "ring",
2060
+ backdrop: "backdrop",
2061
+ weight: null,
2062
+ font: null,
2063
+ slant: null
2064
+ };
2065
+ var TYPE_MAP = {
2066
+ color: "color",
2067
+ spacing: "spacing",
2068
+ radius: "radius",
2069
+ size: "size",
2070
+ dimension: "size",
2071
+ shadow: "shadow",
2072
+ typography: "typography",
2073
+ "font-family": "fontFamily",
2074
+ fontFamily: "fontFamily",
2075
+ duration: "duration",
2076
+ opacity: "opacity",
2077
+ blur: "blur",
2078
+ tonal: "tonal",
2079
+ border: "border",
2080
+ elevation: "elevation",
2081
+ ring: "ring",
2082
+ backdrop: "backdrop",
2083
+ none: "none"
2084
+ };
2085
+ var NUMBER_RE = /^-?\d+(\.\d+)?$/;
2086
+ var isRawValue = (v) => /^#/.test(v) || /[(%]/.test(v) || /\d(px|rem|em|vh|vw|vmin|vmax|ch|ex|fr|deg|ms|s)$/.test(v);
2087
+ function hexToColor(hex) {
2088
+ const m = /^#([0-9a-fA-F]{3,8})$/.exec(hex.trim());
2089
+ if (!m) return null;
2090
+ let h = m[1];
2091
+ if (h.length === 3) h = h.split("").map((c) => c + c).join("");
2092
+ if (h.length !== 6 && h.length !== 8) return null;
2093
+ const byte = (k) => parseInt(h.slice(k * 2, k * 2 + 2), 16) / 255;
2094
+ const value = { space: "srgb", channels: [byte(0), byte(1), byte(2)] };
2095
+ if (h.length === 8) value.alpha = byte(3);
2096
+ return value;
2097
+ }
2098
+ var Parser = class {
2099
+ constructor(toks) {
2100
+ this.pos = 0;
2101
+ this.issues = [];
2102
+ // Tokens synthesized while parsing (inline literals hoisted out of alias slots —
2103
+ // the canonical AST's `refs` holds only token ids, so an inline value must become
2104
+ // its own token). Deduped by (type, serialized value). Flushed into the layer.
2105
+ this.synth = [];
2106
+ this.synthKeys = /* @__PURE__ */ new Map();
2107
+ this.toks = toks;
2108
+ }
2109
+ // Hoist an inline literal into a synthesized token and return its id.
2110
+ hoistLiteral(astType, raw, tok) {
2111
+ const value = this.typedRaw(astType, raw, tok);
2112
+ const key = `${astType}\0${JSON.stringify(value)}`;
2113
+ const existing = this.synthKeys.get(key);
2114
+ if (existing) return existing;
2115
+ const id = `tok.lit-${seg(String(astType))}-${this.synth.length + 1}`;
2116
+ this.synth.push({ id, name: `(inline ${astType})`, type: astType, value });
2117
+ this.synthKeys.set(key, id);
2118
+ this.issue("warning", "inline-literal", `inline ${astType} literal in an alias slot hoisted to token ${id}`, tok);
2119
+ return id;
2120
+ }
2121
+ peek() {
2122
+ return this.toks[this.pos];
2123
+ }
2124
+ next() {
2125
+ return this.toks[this.pos++];
2126
+ }
2127
+ at(type) {
2128
+ return this.peek().type === type;
2129
+ }
2130
+ // A `word` whose text equals `w` (case-sensitive keyword check).
2131
+ atWord(w) {
2132
+ const t = this.peek();
2133
+ return t.type === "word" && t.value === w;
2134
+ }
2135
+ skipNewlines() {
2136
+ while (this.at("newline") || this.at("semi")) this.pos++;
2137
+ }
2138
+ // Safety net: if a loop body made no progress (a malformed token that no rule
2139
+ // consumed), force one token forward so parsing can never spin. Returns true when
2140
+ // it had to intervene (the caller can also choose to break).
2141
+ ensureProgress(mark) {
2142
+ if (this.pos !== mark) return false;
2143
+ if (!this.at("eof")) this.next();
2144
+ return true;
2145
+ }
2146
+ issue(severity, code, message, tok = this.peek()) {
2147
+ this.issues.push({ severity, code, message, line: tok.line, col: tok.col });
2148
+ }
2149
+ // Consume a token of `type`, or record an error and synthesize one.
2150
+ expect(type, code) {
2151
+ if (this.at(type)) return this.next();
2152
+ const tok = this.peek();
2153
+ this.issue("error", code, `expected ${type}, got ${tok.type} ${JSON.stringify(tok.value)}`);
2154
+ return tok;
2155
+ }
2156
+ // A bare word (ident/path/number token).
2157
+ expectWord(code) {
2158
+ if (this.at("word")) return this.next().value;
2159
+ this.issue("error", code, `expected identifier, got ${this.peek().type}`);
2160
+ return "";
2161
+ }
2162
+ // Skip a balanced `{ … }` block (already positioned at or before the `{`).
2163
+ skipBlock() {
2164
+ while (!this.at("lbrace") && !this.at("eof") && !this.at("newline")) this.next();
2165
+ if (!this.at("lbrace")) return;
2166
+ let depth = 0;
2167
+ do {
2168
+ const t = this.next();
2169
+ if (t.type === "lbrace") depth++;
2170
+ else if (t.type === "rbrace") depth--;
2171
+ else if (t.type === "eof") break;
2172
+ } while (depth > 0);
2173
+ }
2174
+ // ── context-group NAME { ctx ctx … } ──
2175
+ parseContextGroup() {
2176
+ this.next();
2177
+ const name = this.expectWord("cg-name");
2178
+ this.expect("lbrace", "cg-lbrace");
2179
+ const contexts = [];
2180
+ while (!this.at("rbrace") && !this.at("eof")) {
2181
+ if (this.at("newline") || this.at("semi")) {
2182
+ this.pos++;
2183
+ continue;
2184
+ }
2185
+ const ctxName = this.expectWord("cg-ctx");
2186
+ if (!ctxName) break;
2187
+ contexts.push({ id: ctxId(name, ctxName), name: ctxName });
2188
+ }
2189
+ this.expect("rbrace", "cg-rbrace");
2190
+ return { id: cgId(name), name, contexts };
2191
+ }
2192
+ // ── raw value literal → a JS value (string | number | nested object) ──
2193
+ parseRawLiteral() {
2194
+ const t = this.peek();
2195
+ if (t.type === "string") return this.next().value;
2196
+ if (t.type === "lbrace") return this.parseStructured();
2197
+ if (t.type === "word") {
2198
+ const w = this.next().value;
2199
+ return NUMBER_RE.test(w) ? Number(w) : w;
2200
+ }
2201
+ this.issue("error", "raw-value", `expected a value, got ${t.type}`);
2202
+ return "";
2203
+ }
2204
+ parseStructured() {
2205
+ this.expect("lbrace", "struct-lbrace");
2206
+ const obj = {};
2207
+ while (!this.at("rbrace") && !this.at("eof")) {
2208
+ const mark = this.pos;
2209
+ if (this.at("newline") || this.at("semi")) {
2210
+ this.pos++;
2211
+ continue;
2212
+ }
2213
+ const key = this.expectWord("struct-key");
2214
+ this.expect("colon", "struct-colon");
2215
+ const val = this.parseRawLiteral();
2216
+ if (key) obj[key] = val;
2217
+ if (this.at("semi") || this.at("newline")) this.pos++;
2218
+ this.ensureProgress(mark);
2219
+ }
2220
+ this.expect("rbrace", "struct-rbrace");
2221
+ return obj;
2222
+ }
2223
+ // ── convert a raw JS value to the canonical typed value for `astType` ──
2224
+ typedRaw(astType, raw, tok) {
2225
+ if (astType === "color" && typeof raw === "string") {
2226
+ const color = hexToColor(raw);
2227
+ if (color) return { kind: "raw", value: color };
2228
+ this.issue("warning", "color-nonhex", `non-hex color "${raw}" kept as an escape value`, tok);
2229
+ return { kind: "escape", raw, note: "non-hex color literal" };
2230
+ }
2231
+ if (astType === "spacing" || astType === "radius" || astType === "size") {
2232
+ if (typeof raw === "number") return { kind: "raw", value: { value: raw, unit: "scaled" } };
2233
+ if (raw && typeof raw === "object" && "value" in raw) {
2234
+ const r = raw;
2235
+ return { kind: "raw", value: { value: Number(r.value), unit: r.unit ?? "scaled" } };
2236
+ }
2237
+ }
2238
+ if (astType === "opacity" && typeof raw === "number") return { kind: "raw", value: { value: raw } };
2239
+ if (astType === "duration" && typeof raw === "number") return { kind: "raw", value: { ms: raw } };
2240
+ if (astType === "tonal" && typeof raw === "number") return { kind: "raw", value: { level: raw } };
2241
+ return { kind: "raw", value: raw };
2242
+ }
2243
+ // ── alias by GROUP { Ctx -> <path | inline literal> ; default -> … } ──
2244
+ // A slot value is normally a token path (ref). The authored surface also allows an
2245
+ // inline literal (a hex color, a structured value) in a slot; since the AST's `refs`
2246
+ // holds only ids, such literals are hoisted to synthesized tokens (hoistLiteral).
2247
+ parseAlias(astType) {
2248
+ this.next();
2249
+ if (this.atWord("by")) this.next();
2250
+ else this.issue("error", "alias-by", "expected 'by'");
2251
+ const groupName = this.expectWord("alias-group");
2252
+ this.expect("lbrace", "alias-lbrace");
2253
+ const refs = {};
2254
+ while (!this.at("rbrace") && !this.at("eof")) {
2255
+ const mark = this.pos;
2256
+ if (this.at("newline") || this.at("semi")) {
2257
+ this.pos++;
2258
+ continue;
2259
+ }
2260
+ const ctxName = this.expectWord("alias-ctx");
2261
+ this.expect("arrow", "alias-arrow");
2262
+ const slot = this.peek();
2263
+ let refId = "";
2264
+ if (slot.type === "string" || slot.type === "lbrace" || slot.type === "word" && NUMBER_RE.test(slot.value)) {
2265
+ refId = this.hoistLiteral(astType, this.parseRawLiteral(), slot);
2266
+ } else {
2267
+ const refPath = this.expectWord("alias-ref");
2268
+ if (refPath) refId = tokId(refPath);
2269
+ }
2270
+ if (ctxName && refId) {
2271
+ const key = ctxName === "default" ? "default" : ctxId(groupName, ctxName);
2272
+ refs[key] = refId;
2273
+ }
2274
+ if (this.at("semi") || this.at("newline")) this.pos++;
2275
+ this.ensureProgress(mark);
2276
+ }
2277
+ this.expect("rbrace", "alias-rbrace");
2278
+ return { kind: "alias", by: cgId(groupName), refs };
2279
+ }
2280
+ // ── the right-hand side of `token … =` ──
2281
+ parseTokenValue(astType) {
2282
+ const t = this.peek();
2283
+ if (t.type === "word" && t.value === "alias") return this.parseAlias(astType);
2284
+ if (t.type === "word" && t.value === "escape") {
2285
+ this.next();
2286
+ const raw = this.at("string") ? this.next().value : this.expectWord("escape-raw");
2287
+ let escNote;
2288
+ if (this.atWord("note")) {
2289
+ this.next();
2290
+ escNote = this.at("string") ? this.next().value : this.expectWord("escape-note");
2291
+ }
2292
+ const esc = { kind: "escape", raw };
2293
+ if (escNote) esc.note = escNote;
2294
+ return esc;
2295
+ }
2296
+ if (t.type === "word" && t.value === "raw") {
2297
+ this.next();
2298
+ const raw = this.parseRawLiteral();
2299
+ return this.typedRaw(astType, raw, t);
2300
+ }
2301
+ if (t.type === "lbrace") {
2302
+ const raw = this.parseStructured();
2303
+ return this.typedRaw(astType, raw, t);
2304
+ }
2305
+ if (t.type === "word") {
2306
+ const refPath = this.next().value;
2307
+ this.issue("warning", "ref-sugar", `unconditional ref "${refPath}" lowered to a default-only alias`, t);
2308
+ return { kind: "alias", by: "", refs: { default: tokId(refPath) } };
2309
+ }
2310
+ this.issue("error", "token-value", `expected a token value, got ${t.type}`);
2311
+ return { kind: "raw", value: "" };
2312
+ }
2313
+ // ── foundations { token PATH : TYPE = VALUE … } → one Layer ──
2314
+ parseFoundations(layer) {
2315
+ this.next();
2316
+ this.expect("lbrace", "foundations-lbrace");
2317
+ let order = 0;
2318
+ while (!this.at("rbrace") && !this.at("eof")) {
2319
+ const mark = this.pos;
2320
+ if (this.at("newline") || this.at("semi")) {
2321
+ this.pos++;
2322
+ continue;
2323
+ }
2324
+ if (this.atWord("token")) {
2325
+ const tok = this.parseTokenDecl(order);
2326
+ if (tok) {
2327
+ layer.tokens.push(tok);
2328
+ order++;
2329
+ }
2330
+ this.ensureProgress(mark);
2331
+ continue;
2332
+ }
2333
+ this.issue("error", "foundations-stmt", `unexpected ${JSON.stringify(this.peek().value)} in foundations`);
2334
+ while (!this.at("newline") && !this.at("semi") && !this.at("rbrace") && !this.at("eof")) this.next();
2335
+ this.ensureProgress(mark);
2336
+ }
2337
+ this.expect("rbrace", "foundations-rbrace");
2338
+ if (this.synth.length) {
2339
+ layer.tokens.push(...this.synth);
2340
+ this.synth = [];
2341
+ }
2342
+ }
2343
+ parseTokenDecl(_order) {
2344
+ const startTok = this.next();
2345
+ const path = this.expectWord("token-path");
2346
+ if (!path) return null;
2347
+ this.expect("colon", "token-colon");
2348
+ const typeName = this.expectWord("token-type");
2349
+ const astType = TYPE_MAP[typeName];
2350
+ if (!astType) this.issue("warning", "token-type", `non-canonical token type "${typeName}" kept as-is`, startTok);
2351
+ this.expect("equals", "token-equals");
2352
+ const value = this.parseTokenValue(astType ?? typeName);
2353
+ return {
2354
+ id: tokId(path),
2355
+ name: path,
2356
+ type: astType ?? typeName,
2357
+ value
2358
+ };
2359
+ }
2360
+ // ════════════════════════════════════════════════════════════════════════
2361
+ // Slice 2 — shared types
2362
+ // ════════════════════════════════════════════════════════════════════════
2363
+ skipToTerminator() {
2364
+ while (!this.at("newline") && !this.at("semi") && !this.at("rbrace") && !this.at("eof")) this.next();
2365
+ }
2366
+ parseIdentList() {
2367
+ const out = [];
2368
+ if (this.at("word")) out.push(this.next().value);
2369
+ while (this.at("comma")) {
2370
+ this.next();
2371
+ if (this.at("word")) out.push(this.next().value);
2372
+ }
2373
+ return out;
2374
+ }
2375
+ parseObjectType() {
2376
+ this.expect("lbrace", "obj-lbrace");
2377
+ const fields = [];
2378
+ while (!this.at("rbrace") && !this.at("eof")) {
2379
+ const mark = this.pos;
2380
+ if (this.at("newline") || this.at("semi")) {
2381
+ this.pos++;
2382
+ continue;
2383
+ }
2384
+ const name = this.expectWord("field-name");
2385
+ let required = true;
2386
+ if (this.at("question")) {
2387
+ this.next();
2388
+ required = false;
2389
+ }
2390
+ this.expect("colon", "field-colon");
2391
+ const type = this.parseTypeExpr();
2392
+ if (name) fields.push({ id: `fld.${seg(name)}`, name, type, required });
2393
+ if (this.at("semi") || this.at("newline")) this.pos++;
2394
+ this.ensureProgress(mark);
2395
+ }
2396
+ this.expect("rbrace", "obj-rbrace");
2397
+ return { kind: "object", fields };
2398
+ }
2399
+ parseTypeExpr() {
2400
+ const t = this.peek();
2401
+ if (t.type !== "word") {
2402
+ this.issue("error", "type-expr", `expected a type, got ${t.type}`);
2403
+ return { kind: "string" };
2404
+ }
2405
+ const w = t.value;
2406
+ if (w === "string" || w === "number" || w === "boolean") {
2407
+ this.next();
2408
+ return { kind: w };
2409
+ }
2410
+ if (w === "enum") {
2411
+ this.next();
2412
+ this.expect("lparen", "enum-lparen");
2413
+ const values = this.parseIdentList();
2414
+ this.expect("rparen", "enum-rparen");
2415
+ return { kind: "enum", values };
2416
+ }
2417
+ if (w === "ref") {
2418
+ this.next();
2419
+ const name = this.expectWord("ref-name");
2420
+ return { kind: "ref", ref: stId(name) };
2421
+ }
2422
+ if (w === "array") {
2423
+ this.next();
2424
+ this.expect("lt", "array-lt");
2425
+ const item = this.parseTypeExpr();
2426
+ this.expect("gt", "array-gt");
2427
+ return { kind: "array", item };
2428
+ }
2429
+ if (w === "union") {
2430
+ this.next();
2431
+ this.expect("lparen", "union-lparen");
2432
+ const members = [this.parseTypeExpr()];
2433
+ while (this.at("comma")) {
2434
+ this.next();
2435
+ members.push(this.parseTypeExpr());
2436
+ }
2437
+ this.expect("rparen", "union-rparen");
2438
+ return { kind: "union", members };
2439
+ }
2440
+ if (w === "object") {
2441
+ this.next();
2442
+ return this.parseObjectType();
2443
+ }
2444
+ if (w === "component") {
2445
+ this.next();
2446
+ let componentIds;
2447
+ if (this.at("lparen")) {
2448
+ this.next();
2449
+ componentIds = this.parseIdentList().map(cmpId);
2450
+ this.expect("rparen", "component-rparen");
2451
+ }
2452
+ return { kind: "component", componentIds };
2453
+ }
2454
+ if (w === "context") {
2455
+ this.next();
2456
+ const group = this.expectWord("context-type");
2457
+ this.issue("warning", "context-type", `context-typed "${group}" lowered to ref (proposal 0001)`, t);
2458
+ return { kind: "ref", ref: cgId(group) };
2459
+ }
2460
+ this.next();
2461
+ return { kind: "ref", ref: stId(w) };
2462
+ }
2463
+ parseSharedType() {
2464
+ this.next();
2465
+ const name = this.expectWord("type-name");
2466
+ if (!name) return null;
2467
+ this.expect("equals", "type-equals");
2468
+ const definition = this.parseTypeExpr();
2469
+ return { id: stId(name), name, definition };
2470
+ }
2471
+ // ════════════════════════════════════════════════════════════════════════
2472
+ // Slice 3 — components
2473
+ // ════════════════════════════════════════════════════════════════════════
2474
+ // A condition's raw text: `prop` or `prop=value` (reused by wire/gate `when`).
2475
+ parseConditionText() {
2476
+ let text = this.expectWord("cond");
2477
+ if (this.at("equals")) {
2478
+ this.next();
2479
+ const v = this.at("string") ? this.next().value : this.expectWord("cond-val");
2480
+ text += `=${v}`;
2481
+ }
2482
+ return text;
2483
+ }
2484
+ // A prop condition → RealizeCond { prop, eq } (single `prop=value`).
2485
+ parseRealizeCond() {
2486
+ const prop = this.expectWord("rc-prop");
2487
+ let eq = true;
2488
+ if (this.at("equals")) {
2489
+ this.next();
2490
+ if (this.at("string")) eq = this.next().value;
2491
+ else {
2492
+ const v = this.expectWord("rc-val");
2493
+ eq = NUMBER_RE.test(v) ? Number(v) : v === "true" ? true : v === "false" ? false : v;
2494
+ }
2495
+ }
2496
+ return { prop, eq };
2497
+ }
2498
+ parsePropDefault() {
2499
+ const t = this.peek();
2500
+ if (t.type === "string") return this.next().value;
2501
+ if (t.type === "word" && t.value === "alias") {
2502
+ this.next();
2503
+ if (this.atWord("by")) this.next();
2504
+ this.expectWord("alias-group");
2505
+ this.skipBlock();
2506
+ this.issue("warning", "alias-default", "context-aliased prop default skipped (proposal 0001)", t);
2507
+ return void 0;
2508
+ }
2509
+ if (t.type === "word") {
2510
+ this.next();
2511
+ if (NUMBER_RE.test(t.value)) return Number(t.value);
2512
+ if (t.value === "true") return true;
2513
+ if (t.value === "false") return false;
2514
+ return t.value;
2515
+ }
2516
+ this.issue("error", "prop-default", `expected a default value, got ${t.type}`);
2517
+ return void 0;
2518
+ }
2519
+ parsePropDecl() {
2520
+ const name = this.expectWord("prop-name");
2521
+ if (!name) return null;
2522
+ const type = this.parseTypeExpr();
2523
+ const prop = { id: `prop.${seg(name)}`, name, type };
2524
+ if (this.at("equals")) {
2525
+ this.next();
2526
+ const def = this.parsePropDefault();
2527
+ if (def !== void 0) prop.default = def;
2528
+ }
2529
+ if (this.atWord("bind")) {
2530
+ this.next();
2531
+ prop.binding = this.expectWord("bind-target");
2532
+ }
2533
+ return prop;
2534
+ }
2535
+ parseProps(props) {
2536
+ this.next();
2537
+ this.expect("lbrace", "props-lbrace");
2538
+ while (!this.at("rbrace") && !this.at("eof")) {
2539
+ const mark = this.pos;
2540
+ if (this.at("newline") || this.at("semi")) {
2541
+ this.pos++;
2542
+ continue;
2543
+ }
2544
+ const p = this.parsePropDecl();
2545
+ if (p) props.push(p);
2546
+ if (this.at("semi") || this.at("newline")) this.pos++;
2547
+ this.ensureProgress(mark);
2548
+ }
2549
+ this.expect("rbrace", "props-rbrace");
2550
+ }
2551
+ parseEvents(events) {
2552
+ this.next();
2553
+ this.expect("lbrace", "events-lbrace");
2554
+ while (!this.at("rbrace") && !this.at("eof")) {
2555
+ const mark = this.pos;
2556
+ if (this.at("newline") || this.at("semi")) {
2557
+ this.pos++;
2558
+ continue;
2559
+ }
2560
+ const name = this.expectWord("event-name");
2561
+ if (name) {
2562
+ const e = { name };
2563
+ if (this.atWord("from")) {
2564
+ this.next();
2565
+ e.from = this.expectWord("event-from");
2566
+ }
2567
+ events.push(e);
2568
+ }
2569
+ if (this.at("semi") || this.at("newline")) this.pos++;
2570
+ this.ensureProgress(mark);
2571
+ }
2572
+ this.expect("rbrace", "events-rbrace");
2573
+ }
2574
+ // `uses ATOM [ ( params ) ] as ALIAS`
2575
+ parseUses() {
2576
+ this.next();
2577
+ const atom = this.expectWord("uses-atom");
2578
+ if (!atom) return null;
2579
+ if (this.at("lparen")) {
2580
+ this.next();
2581
+ while (!this.at("rparen") && !this.at("eof") && !this.at("newline")) this.next();
2582
+ if (this.at("rparen")) this.next();
2583
+ }
2584
+ if (this.atWord("as")) this.next();
2585
+ else this.issue("warning", "uses-as", "expected 'as' in uses");
2586
+ const as = this.expectWord("uses-alias");
2587
+ return { atom, as };
2588
+ }
2589
+ // The `machineref` target of a wire, with an optional `(arg)`.
2590
+ parseWireEnd(code) {
2591
+ const end = this.expectWord(code);
2592
+ let arg;
2593
+ if (this.at("lparen")) {
2594
+ this.next();
2595
+ if (this.at("word")) arg = this.next().value;
2596
+ while (!this.at("rparen") && !this.at("eof") && !this.at("newline")) this.next();
2597
+ if (this.at("rparen")) this.next();
2598
+ }
2599
+ return { end, arg };
2600
+ }
2601
+ // `wire FROM [when COND] -> TO[(arg)]`
2602
+ parseWire() {
2603
+ this.next();
2604
+ const from = this.expectWord("wire-from");
2605
+ if (!from) return null;
2606
+ let when;
2607
+ if (this.atWord("when")) {
2608
+ this.next();
2609
+ when = this.parseConditionText();
2610
+ }
2611
+ this.expect("arrow", "wire-arrow");
2612
+ const { end: to, arg } = this.parseWireEnd("wire-to");
2613
+ const wire = { kind: "wire", from, to };
2614
+ if (arg) wire.arg = arg;
2615
+ if (when) wire.when = when;
2616
+ return wire;
2617
+ }
2618
+ // `gate MACHINE active when COND`
2619
+ parseGate() {
2620
+ this.next();
2621
+ const machine = this.expectWord("gate-machine");
2622
+ if (!machine) return null;
2623
+ if (this.atWord("active")) this.next();
2624
+ if (this.atWord("when")) this.next();
2625
+ else this.issue("warning", "gate-when", "expected 'when' in gate");
2626
+ const activeWhen = this.parseConditionText();
2627
+ return { kind: "gate", machine, activeWhen };
2628
+ }
2629
+ // ── layout / facet bodies ──
2630
+ parseAxisIntent() {
2631
+ const w = this.expectWord("axis");
2632
+ if (w === "fit" || w === "fill") return w;
2633
+ if (w === "weight") {
2634
+ if (this.at("lparen")) {
2635
+ this.next();
2636
+ const n = Number(this.expectWord("weight-n"));
2637
+ if (this.at("rparen")) this.next();
2638
+ return { weight: n };
2639
+ }
2640
+ return { weight: 1 };
2641
+ }
2642
+ if (w === "fixed") {
2643
+ if (this.at("lparen")) {
2644
+ this.next();
2645
+ const p = this.expectWord("fixed-token");
2646
+ if (this.at("rparen")) this.next();
2647
+ return { fixed: tokId(p) };
2648
+ }
2649
+ this.issue("warning", "axis-fixed", "bare 'fixed' axis has no dimension; treated as fit");
2650
+ return "fit";
2651
+ }
2652
+ this.issue("warning", "axis-unknown", `unknown size intent "${w}"; treated as fit`);
2653
+ return "fit";
2654
+ }
2655
+ parseInsetArgs() {
2656
+ const args = [];
2657
+ while (this.at("word") && args.length < 2) args.push(this.next().value);
2658
+ if (args.length >= 2) return { block: tokId(args[0]), inline: tokId(args[1]) };
2659
+ if (args.length === 1) return { all: tokId(args[0]) };
2660
+ return { all: "" };
2661
+ }
2662
+ parseLayoutBody() {
2663
+ this.expect("lbrace", "layout-lbrace");
2664
+ const spec = {};
2665
+ while (!this.at("rbrace") && !this.at("eof")) {
2666
+ const mark = this.pos;
2667
+ if (this.at("newline") || this.at("semi")) {
2668
+ this.pos++;
2669
+ continue;
2670
+ }
2671
+ const key = this.expectWord("layout-key");
2672
+ switch (key) {
2673
+ case "stack":
2674
+ spec.stack = this.expectWord("stack");
2675
+ break;
2676
+ case "flow":
2677
+ spec.flow = true;
2678
+ break;
2679
+ case "gap":
2680
+ spec.gap = tokId(this.expectWord("gap"));
2681
+ break;
2682
+ case "align":
2683
+ spec.align = this.expectWord("align");
2684
+ break;
2685
+ case "distribute":
2686
+ spec.distribute = this.expectWord("distribute");
2687
+ break;
2688
+ case "wrap": {
2689
+ const w = this.expectWord("wrap");
2690
+ if (w === "wrap" || w === "wrap-reverse") spec.flow = true;
2691
+ break;
2692
+ }
2693
+ case "position": {
2694
+ const p = this.expectWord("position");
2695
+ spec.position = p === "overlay" || p === "flow" ? p : { pinned: p };
2696
+ break;
2697
+ }
2698
+ case "size": {
2699
+ const width = this.parseAxisIntent();
2700
+ const height = this.parseAxisIntent();
2701
+ spec.size = { width, height };
2702
+ break;
2703
+ }
2704
+ case "ratio": {
2705
+ const wv = Number(this.expectWord("ratio-w"));
2706
+ const hv = Number(this.expectWord("ratio-h"));
2707
+ spec.size = { ...spec.size ?? {}, width: { ratio: { w: wv, h: hv } } };
2708
+ break;
2709
+ }
2710
+ case "inset":
2711
+ spec.inset = this.parseInsetArgs();
2712
+ break;
2713
+ default:
2714
+ this.issue("warning", "layout-key", `unknown layout key "${key}" skipped`);
2715
+ this.skipToTerminator();
2716
+ }
2717
+ if (this.at("semi") || this.at("newline")) this.pos++;
2718
+ this.ensureProgress(mark);
2719
+ }
2720
+ this.expect("rbrace", "layout-rbrace");
2721
+ return spec;
2722
+ }
2723
+ // A single facet statement `facetname value+` → [FacetName, FacetValue] | null.
2724
+ parseFacetStmt() {
2725
+ const raw = this.expectWord("facet-name");
2726
+ const mapped = FACET_MAP[raw];
2727
+ if (mapped === void 0) {
2728
+ this.issue("warning", "facet-unknown", `unknown facet "${raw}" skipped`);
2729
+ this.skipToTerminator();
2730
+ return null;
2731
+ }
2732
+ if (mapped === null) {
2733
+ this.issue("warning", "facet-nohome", `facet "${raw}" has no AST FacetMap home (\xA73) \u2014 skipped`);
2734
+ this.skipToTerminator();
2735
+ return null;
2736
+ }
2737
+ if (mapped === "inset") {
2738
+ return { name: "inset", value: { inset: this.parseInsetArgs() } };
2739
+ }
2740
+ const t = this.peek();
2741
+ let value;
2742
+ if (t.type === "string") {
2743
+ value = { raw: this.next().value };
2744
+ } else if (t.type === "word") {
2745
+ const v = this.next().value;
2746
+ if (NUMBER_RE.test(v)) value = { literal: Number(v) };
2747
+ else if (isRawValue(v)) value = { raw: v };
2748
+ else value = { token: tokId(v) };
2749
+ } else {
2750
+ this.issue("error", "facet-value", `expected a facet value, got ${t.type}`);
2751
+ this.skipToTerminator();
2752
+ return null;
2753
+ }
2754
+ if (!this.at("semi") && !this.at("newline") && !this.at("rbrace")) this.skipToTerminator();
2755
+ return { name: mapped, value };
2756
+ }
2757
+ // `state NAME [when COND] { … }` → a PartVariant with one interaction state.
2758
+ parseStateBlock() {
2759
+ this.next();
2760
+ const name = this.expectWord("state-name");
2761
+ const variant = { states: [name] };
2762
+ if (this.atWord("when")) {
2763
+ this.next();
2764
+ variant.when = this.parseRealizeCond();
2765
+ }
2766
+ this.applyStyleBody(variant);
2767
+ return variant;
2768
+ }
2769
+ // `when COND { … }` → a state-less prop-conditioned PartVariant (proposal 0025).
2770
+ parseWhenBlock() {
2771
+ this.next();
2772
+ const when = this.parseRealizeCond();
2773
+ const variant = { when };
2774
+ this.applyStyleBody(variant);
2775
+ return variant;
2776
+ }
2777
+ // Parse a `{ facet… / layout… }` style body into a variant's facets/layout.
2778
+ applyStyleBody(target) {
2779
+ this.expect("lbrace", "style-lbrace");
2780
+ const facets2 = {};
2781
+ while (!this.at("rbrace") && !this.at("eof")) {
2782
+ const mark = this.pos;
2783
+ if (this.at("newline") || this.at("semi")) {
2784
+ this.pos++;
2785
+ continue;
2786
+ }
2787
+ if (this.atWord("layout")) {
2788
+ this.next();
2789
+ target.layout = this.parseLayoutBody();
2790
+ } else {
2791
+ const f = this.parseFacetStmt();
2792
+ if (f) facets2[f.name] = f.value;
2793
+ }
2794
+ if (this.at("semi") || this.at("newline")) this.pos++;
2795
+ this.ensureProgress(mark);
2796
+ }
2797
+ this.expect("rbrace", "style-rbrace");
2798
+ if (Object.keys(facets2).length) target.facets = facets2;
2799
+ }
2800
+ // `part NAME { pstmt* }` → [name, Part]
2801
+ parsePart() {
2802
+ this.next();
2803
+ const name = this.expectWord("part-name");
2804
+ if (!name) return null;
2805
+ this.expect("lbrace", "part-lbrace");
2806
+ const part = {};
2807
+ const facets2 = {};
2808
+ const variants = [];
2809
+ while (!this.at("rbrace") && !this.at("eof")) {
2810
+ const mark = this.pos;
2811
+ if (this.at("newline") || this.at("semi")) {
2812
+ this.pos++;
2813
+ continue;
2814
+ }
2815
+ if (this.atWord("layout")) {
2816
+ this.next();
2817
+ if (this.atWord("when")) {
2818
+ this.next();
2819
+ const when = this.parseRealizeCond();
2820
+ variants.push({ when, layout: this.parseLayoutBody() });
2821
+ } else {
2822
+ part.layout = this.parseLayoutBody();
2823
+ }
2824
+ } else if (this.atWord("state")) {
2825
+ variants.push(this.parseStateBlock());
2826
+ } else if (this.atWord("when")) {
2827
+ variants.push(this.parseWhenBlock());
2828
+ } else if (this.atWord("slot")) {
2829
+ this.issue("warning", "slot", "part `slot` has no AST Part field yet \u2014 skipped");
2830
+ this.skipToTerminator();
2831
+ } else {
2832
+ const f = this.parseFacetStmt();
2833
+ if (f) facets2[f.name] = f.value;
2834
+ }
2835
+ if (this.at("semi") || this.at("newline")) this.pos++;
2836
+ this.ensureProgress(mark);
2837
+ }
2838
+ this.expect("rbrace", "part-rbrace");
2839
+ if (Object.keys(facets2).length) part.facets = facets2;
2840
+ if (variants.length) part.variants = variants;
2841
+ return [name, part];
2842
+ }
2843
+ // `realize TARGET [when COND] { rstmt* }`
2844
+ parseRealize() {
2845
+ this.next();
2846
+ const target = this.expectWord("realize-target");
2847
+ if (!target) return null;
2848
+ let when = null;
2849
+ if (this.atWord("when")) {
2850
+ this.next();
2851
+ when = this.parseRealizeCond();
2852
+ }
2853
+ const r = { target, when };
2854
+ this.expect("lbrace", "realize-lbrace");
2855
+ const wires = [];
2856
+ while (!this.at("rbrace") && !this.at("eof")) {
2857
+ const mark = this.pos;
2858
+ if (this.at("newline") || this.at("semi")) {
2859
+ this.pos++;
2860
+ continue;
2861
+ }
2862
+ const kw = this.peek().value;
2863
+ if (this.atWord("presentation")) {
2864
+ this.next();
2865
+ const surface = this.at("word") ? this.next().value : void 0;
2866
+ const pres = {};
2867
+ if (surface) pres.surface = surface;
2868
+ if (this.at("lbrace")) this.parsePresentationBody(pres);
2869
+ r.presentation = pres;
2870
+ } else if (this.atWord("browse")) {
2871
+ this.next();
2872
+ const mode = this.at("word") ? this.next().value : void 0;
2873
+ r.browse = { mode };
2874
+ if (this.at("lbrace")) this.skipBlock();
2875
+ } else if (this.atWord("commit")) {
2876
+ this.next();
2877
+ r.commit = this.parseCommit();
2878
+ } else if (this.atWord("drop")) {
2879
+ this.next();
2880
+ r.drop = this.parseIdentListWords();
2881
+ } else if (this.atWord("note")) {
2882
+ this.next();
2883
+ r.note = this.at("string") ? this.next().value : this.expectWord("note");
2884
+ } else if (this.atWord("wire")) {
2885
+ const w = this.parseWire();
2886
+ if (w && w.kind === "wire") wires.push({ from: w.from, to: w.to, ...w.arg ? { arg: w.arg } : {} });
2887
+ } else {
2888
+ this.issue("warning", "realize-stmt", `unknown realize statement "${kw}" skipped`);
2889
+ if (this.at("lbrace")) this.skipBlock();
2890
+ else this.skipToTerminator();
2891
+ }
2892
+ if (this.at("semi") || this.at("newline")) this.pos++;
2893
+ this.ensureProgress(mark);
2894
+ }
2895
+ this.expect("rbrace", "realize-rbrace");
2896
+ if (wires.length) r.wire = wires;
2897
+ return r;
2898
+ }
2899
+ parsePresentationBody(pres) {
2900
+ this.expect("lbrace", "pres-lbrace");
2901
+ while (!this.at("rbrace") && !this.at("eof")) {
2902
+ const mark = this.pos;
2903
+ if (this.at("newline") || this.at("semi")) {
2904
+ this.pos++;
2905
+ continue;
2906
+ }
2907
+ const key = this.expectWord("pres-key");
2908
+ if (key === "place") pres.place = this.expectWord("place");
2909
+ else if (key === "flip") pres.flip = true;
2910
+ else if (key === "matchWidth") pres.matchWidth = true;
2911
+ else this.skipToTerminator();
2912
+ if (this.at("semi") || this.at("newline")) this.pos++;
2913
+ this.ensureProgress(mark);
2914
+ }
2915
+ this.expect("rbrace", "pres-rbrace");
2916
+ }
2917
+ parseCommit() {
2918
+ const modeWord = this.expectWord("commit-mode");
2919
+ const mode = modeWord === "continuous" ? "continuous" : "explicit";
2920
+ const commit = { mode };
2921
+ if (this.atWord("on")) {
2922
+ this.next();
2923
+ commit.on = this.parseIdentListWords();
2924
+ }
2925
+ return commit;
2926
+ }
2927
+ // A run of bare words up to a terminator (for `drop a b c`, `commit … on click enter`).
2928
+ parseIdentListWords() {
2929
+ const out = [];
2930
+ while (this.at("word")) out.push(this.next().value);
2931
+ return out;
2932
+ }
2933
+ parseComponent() {
2934
+ this.next();
2935
+ const name = this.expectWord("component-name");
2936
+ if (!name) return null;
2937
+ let description;
2938
+ if (this.at("string")) description = this.next().value;
2939
+ this.expect("lbrace", "component-lbrace");
2940
+ let archetype = "";
2941
+ const props = [];
2942
+ const events = [];
2943
+ const uses = [];
2944
+ const coordination = [];
2945
+ const anatomy = {};
2946
+ const realize = [];
2947
+ while (!this.at("rbrace") && !this.at("eof")) {
2948
+ const mark = this.pos;
2949
+ if (this.at("newline") || this.at("semi")) {
2950
+ this.pos++;
2951
+ continue;
2952
+ }
2953
+ if (this.atWord("archetype")) {
2954
+ this.next();
2955
+ archetype = this.expectWord("archetype");
2956
+ } else if (this.atWord("props")) {
2957
+ this.parseProps(props);
2958
+ } else if (this.atWord("events")) {
2959
+ this.parseEvents(events);
2960
+ } else if (this.atWord("uses")) {
2961
+ const u = this.parseUses();
2962
+ if (u) uses.push(u);
2963
+ } else if (this.atWord("wire")) {
2964
+ const w = this.parseWire();
2965
+ if (w) coordination.push(w);
2966
+ } else if (this.atWord("gate")) {
2967
+ const g = this.parseGate();
2968
+ if (g) coordination.push(g);
2969
+ } else if (this.atWord("motion")) {
2970
+ this.issue("warning", "motion", "component-level motion parsed as a note \u2014 skipped");
2971
+ this.skipToTerminator();
2972
+ } else if (this.atWord("requires")) {
2973
+ this.next();
2974
+ this.parseIdentListWords();
2975
+ this.issue("warning", "requires", "component `requires` (capability gate) has no AST field \u2014 skipped");
2976
+ } else if (this.atWord("part")) {
2977
+ const p = this.parsePart();
2978
+ if (p) anatomy[p[0]] = p[1];
2979
+ } else if (this.atWord("realize")) {
2980
+ const r = this.parseRealize();
2981
+ if (r) realize.push(r);
2982
+ } else {
2983
+ this.issue("error", "component-stmt", `unexpected ${JSON.stringify(this.peek().value)} in component`);
2984
+ if (this.at("lbrace")) this.skipBlock();
2985
+ else this.skipToTerminator();
2986
+ }
2987
+ this.ensureProgress(mark);
2988
+ }
2989
+ this.expect("rbrace", "component-rbrace");
2990
+ const component = {
2991
+ id: cmpId(name),
2992
+ name,
2993
+ archetype,
2994
+ contract: { params: {}, props, events },
2995
+ anatomy,
2996
+ realize
2997
+ };
2998
+ if (description) component.description = description;
2999
+ if (uses.length || coordination.length) {
3000
+ const composition = { uses, coordination };
3001
+ component.composition = composition;
3002
+ }
3003
+ return component;
3004
+ }
3005
+ // ── top-level document driver ──
3006
+ parseDocument(opts) {
3007
+ const contextGroups = [];
3008
+ const layer = { id: "layer.foundations", name: "Foundations", order: 0, tokens: [] };
3009
+ const sharedTypes = [];
3010
+ const components = [];
3011
+ this.skipNewlines();
3012
+ while (!this.at("eof")) {
3013
+ const mark = this.pos;
3014
+ if (this.atWord("context-group")) {
3015
+ const cg = this.parseContextGroup();
3016
+ if (cg) contextGroups.push(cg);
3017
+ } else if (this.atWord("foundations")) {
3018
+ this.parseFoundations(layer);
3019
+ } else if (this.atWord("type")) {
3020
+ const st = this.parseSharedType();
3021
+ if (st) sharedTypes.push(st);
3022
+ } else if (this.atWord("component")) {
3023
+ const c = this.parseComponent();
3024
+ if (c) components.push(c);
3025
+ } else {
3026
+ const tok = this.peek();
3027
+ this.issue("error", "top-level", `unexpected top-level ${JSON.stringify(tok.value || tok.type)}`);
3028
+ this.next();
3029
+ }
3030
+ this.ensureProgress(mark);
3031
+ this.skipNewlines();
3032
+ }
3033
+ return {
3034
+ milda: opts.milda ?? "0.1",
3035
+ kind: "design-system",
3036
+ prelude: opts.prelude ?? "0.0.2",
3037
+ meta: {
3038
+ id: `ds.${seg(opts.name ?? "design-system")}`,
3039
+ name: opts.name ?? "Design system",
3040
+ version: "0.0.0"
3041
+ },
3042
+ foundations: {
3043
+ contextGroups,
3044
+ layers: layer.tokens.length ? [layer] : [],
3045
+ sharedTypes,
3046
+ metadata: []
3047
+ },
3048
+ components
3049
+ };
3050
+ }
3051
+ };
3052
+ function parseMildaDocument(src, opts = {}) {
3053
+ let toks;
3054
+ try {
3055
+ toks = lex(src);
3056
+ } catch (e) {
3057
+ const le = e;
3058
+ return {
3059
+ document: emptyDocument(opts),
3060
+ issues: [{ severity: "error", code: "lex", message: le.message, line: le.line ?? 1, col: le.col ?? 1 }]
3061
+ };
3062
+ }
3063
+ const p = new Parser(toks);
3064
+ const document = p.parseDocument(opts);
3065
+ return { document, issues: p.issues };
3066
+ }
3067
+ function emptyDocument(opts) {
3068
+ return {
3069
+ milda: opts.milda ?? "0.1",
3070
+ kind: "design-system",
3071
+ prelude: opts.prelude ?? "0.0.2",
3072
+ meta: { id: `ds.${seg(opts.name ?? "design-system")}`, name: opts.name ?? "Design system", version: "0.0.0" },
3073
+ foundations: { contextGroups: [], layers: [], sharedTypes: [], metadata: [] },
3074
+ components: []
3075
+ };
3076
+ }
3077
+ // Annotate the CommonJS export names for ESM import in node:
3078
+ 0 && (module.exports = {
3079
+ LexError,
3080
+ TOKEN_VALUE_KIND,
3081
+ allTokensOfType,
3082
+ archetypesByName,
3083
+ atomsByName,
3084
+ collectActiveStates,
3085
+ conformance,
3086
+ findToken,
3087
+ lex,
3088
+ parseMildaDocument,
3089
+ preludeV0,
3090
+ resolveFacets,
3091
+ resolveLayout,
3092
+ resolveToken,
3093
+ validate
3094
+ });