@pipelex/mthds-form 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +5 -2
  2. package/dist/brand/index.d.ts +4 -2
  3. package/dist/brand/index.js +4 -2
  4. package/dist/brand/index.js.map +1 -1
  5. package/dist/{chunk-UZNRJWN5.js → chunk-3GO65HWP.js} +3 -31
  6. package/dist/chunk-3GO65HWP.js.map +1 -0
  7. package/dist/chunk-4SRSGZWM.js +2447 -0
  8. package/dist/chunk-4SRSGZWM.js.map +1 -0
  9. package/dist/{chunk-XVGAQ4RK.js → chunk-IBBAJXIO.js} +4 -4
  10. package/dist/{chunk-XVGAQ4RK.js.map → chunk-IBBAJXIO.js.map} +1 -1
  11. package/dist/chunk-QGFR57OH.js +35 -0
  12. package/dist/chunk-QGFR57OH.js.map +1 -0
  13. package/dist/{chunk-ENK5QTUK.js → chunk-UKTYADY3.js} +6 -6
  14. package/dist/{chunk-ENK5QTUK.js.map → chunk-UKTYADY3.js.map} +1 -1
  15. package/dist/{chunk-HVWE3TTC.js → chunk-ZB4RYUKV.js} +3 -3
  16. package/dist/{chunk-HVWE3TTC.js.map → chunk-ZB4RYUKV.js.map} +1 -1
  17. package/dist/{chunk-2UCH4AAO.js → chunk-ZX52CQJ4.js} +3 -3
  18. package/dist/{chunk-2UCH4AAO.js.map → chunk-ZX52CQJ4.js.map} +1 -1
  19. package/dist/core/derive.js +2 -2
  20. package/dist/core/gate.js +3 -3
  21. package/dist/core/index.js +12 -12
  22. package/dist/core/values.js +3 -3
  23. package/dist/{field-renderer-Dz03dpig.d.ts → field-renderer-BYN6YrU-.d.ts} +6 -6
  24. package/dist/generative/headless/index.d.ts +1733 -0
  25. package/dist/generative/headless/index.js +6 -0
  26. package/dist/generative/headless/index.js.map +1 -0
  27. package/dist/generative/index.d.ts +112 -1710
  28. package/dist/generative/index.js +628 -2301
  29. package/dist/generative/index.js.map +1 -1
  30. package/dist/manifest-D3DLyNEv.d.ts +17 -0
  31. package/dist/react/index.d.ts +27 -27
  32. package/dist/react/index.js +7 -7
  33. package/dist/{styles-BoXPBjF4.d.ts → styles-Bn8byNdo.d.ts} +1 -17
  34. package/package.json +5 -1
  35. package/dist/chunk-UZNRJWN5.js.map +0 -1
@@ -1,2083 +1,34 @@
1
1
  "use client";
2
- import { cn, headingClass, checkedAccentClass, focusOutlineClass, actionShadowClass, panelSurfaceClass, panelRaisedShadowClass, linkTextClass, selectedBorderClass, selectedFillClass, fieldControlClass, useResolveUrl, panelFlatShadowClass, ResultRoot, ResultField, FieldRenderer, enumLabeler, useFieldPresentation, ToggleGroup, ToggleGroupItem, selectedFillStrongClass, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, isThenable } from '../chunk-ENK5QTUK.js';
3
- import '../chunk-2UCH4AAO.js';
2
+ import { cn, headingClass, checkedAccentClass, focusOutlineClass, useResolveUrl, panelSurfaceClass, panelFlatShadowClass, linkTextClass, actionShadowClass, panelRaisedShadowClass, fieldControlClass, ResultRoot, ResultField, FieldRenderer, enumLabeler, useFieldPresentation, ToggleGroup, ToggleGroupItem, selectedFillStrongClass, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, selectedBorderClass, selectedFillClass, isThenable } from '../chunk-UKTYADY3.js';
3
+ import { baseCatalog, catalog, frameComponents, shareAmount, resultFieldAtPath, segmentsUnder, RESULT_ROOT, inputFieldAtPath, definitionChain, unwrapped } from '../chunk-4SRSGZWM.js';
4
+ export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, catalog, componentNamesFor, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheck, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, validateAgainstCatalog } from '../chunk-4SRSGZWM.js';
4
5
  import '../chunk-4VAO6BDE.js';
5
6
  export { seedInputs } from '../chunk-VH5CDQDR.js';
6
7
  import '../chunk-L23R3JNQ.js';
7
8
  import '../chunk-56WECFBD.js';
9
+ import '../chunk-ZX52CQJ4.js';
10
+ import '../chunk-GHVVULHY.js';
11
+ import { readImageContent, viewableUrl, readDateContent } from '../chunk-QQK6Y55E.js';
12
+ export { brandManifestSchema } from '../chunk-QGFR57OH.js';
13
+ import '../chunk-3GO65HWP.js';
14
+ import '../chunk-ZB4RYUKV.js';
15
+ import '../chunk-FABY3DEY.js';
16
+ import '../chunk-XCO6GTEC.js';
8
17
  import '../chunk-E6BIXJ6C.js';
9
18
  import '../chunk-XO5OENNF.js';
10
- import '../chunk-GHVVULHY.js';
11
- import { readImageContent, viewableUrl, readDateContent, isNativeValueNode, formatDateContent, isNativeHtmlNode, isNativeDateNode, isNativeCompositeNode } from '../chunk-QQK6Y55E.js';
12
- import { APP_STYLES } from '../chunk-UZNRJWN5.js';
13
- export { brandManifestSchema } from '../chunk-UZNRJWN5.js';
14
- import '../chunk-HVWE3TTC.js';
15
19
  import '../chunk-OD3VYXUY.js';
16
20
  import '../chunk-WYH56SOZ.js';
17
21
  import { hasOwnProp } from '../chunk-V3OV2XQT.js';
18
- import '../chunk-FABY3DEY.js';
19
- import '../chunk-XCO6GTEC.js';
20
22
  import '../chunk-GKV77RAS.js';
21
- import { defineSchema, defineCatalog, validateSpec, compileSpecStream } from '@json-render/core';
22
- export { createStateStore } from '@json-render/core';
23
- import { z } from 'zod';
24
- import * as React4 from 'react';
25
- import { defineRegistry, useStateValue, useStateStore, useBoundProp, useFieldValidation, useRepeatScope, JSONUIProvider, Renderer } from '@json-render/react';
23
+ import * as React6 from 'react';
24
+ import { defineRegistry, useStateValue, useRepeatScope, useStateStore, useBoundProp, useFieldValidation, useOptionalValidation, JSONUIProvider, Renderer, useVisibility } from '@json-render/react';
26
25
  import { jsx, jsxs } from 'react/jsx-runtime';
27
- import { Clock, ArrowRight, Send, Pencil, Zap, Lightbulb, Truck, Package, Briefcase, Bookmark, Flag, Shield, Info, AlertCircle, CheckCircle2, ListChecks, Layers, Tag, Phone, Mail, Paperclip, Upload, FileText, BarChart3, TrendingUp, Hash, Percent, Receipt, Coins, Banknote, Wallet, Gift, Ticket, Image, Camera, Music, Coffee, Utensils, Star, Heart, Accessibility, Baby, User, Users, Hourglass, Calendar, Moon, Sun, Leaf, Mountain, Globe, Landmark, Building2, Home, Hotel, Ship, Car, Train, Plane, Route, MapPin, Map as Map$1, Compass, Rocket, Sparkles, ChevronDown } from 'lucide-react';
28
- import * as TabsPrimitive from '@radix-ui/react-tabs';
29
-
30
- var generativeSchema = defineSchema(
31
- (s) => ({
32
- spec: s.object({
33
- root: s.string(),
34
- elements: s.record(
35
- s.object({
36
- type: s.ref("catalog.components"),
37
- props: s.propsOf("catalog.components"),
38
- children: s.array(s.string()),
39
- slots: { ...s.record(s.array(s.string())), ...s.optional() },
40
- visible: { ...s.any(), ...s.optional() },
41
- repeat: { ...s.any(), ...s.optional() }
42
- })
43
- )
44
- }),
45
- catalog: s.object({
46
- components: s.map({
47
- props: s.zod(),
48
- slots: s.array(s.string()),
49
- description: s.string(),
50
- example: s.any()
51
- }),
52
- actions: s.map({
53
- params: s.zod(),
54
- description: s.string()
55
- })
56
- })
57
- }),
58
- {
59
- // The runtime handles these four without a registry handler; the names and
60
- // the wording are the React schema's, so a model that has seen json-render
61
- // before recognises them. The validator reads them to know which actions
62
- // need no handler, and `designerCatalog()` lists them first, marked
63
- // built-in, so the method's prompt names them beside the catalog's own.
64
- builtInActions: [
65
- {
66
- name: "setState",
67
- description: "Update a value in the state model at the given statePath. Params: { statePath: string, value: any }"
68
- },
69
- {
70
- name: "pushState",
71
- description: "Append an item to an array in state. Params: { statePath: string, value: any, clearStatePath?: string }."
72
- },
73
- {
74
- name: "removeState",
75
- description: "Remove an item from an array in state by index. Params: { statePath: string, index: number }"
76
- },
77
- {
78
- name: "validateForm",
79
- description: "Validate all registered form fields and write the result to state. Params: { statePath?: string }. Defaults to /formValidation."
80
- }
81
- ]
82
- }
83
- );
84
- var validationCheckSchema = z.array(
85
- z.object({
86
- type: z.string(),
87
- message: z.string(),
88
- args: z.record(z.string(), z.unknown()).optional()
89
- })
90
- ).nullable();
91
- var validateOnSchema = z.enum(["change", "blur", "submit"]).nullable();
92
- var shadcnComponentDefinitions = {
93
- // ── Layout ────────────────────────────────────────────────────────────────
94
- Card: {
95
- props: z.object({
96
- title: z.string().nullable(),
97
- description: z.string().nullable(),
98
- maxWidth: z.enum(["sm", "md", "lg", "full"]).nullable(),
99
- centered: z.boolean().nullable(),
100
- className: z.string().nullable().describe("Additional CSS classes")
101
- }),
102
- slots: ["default"],
103
- description: "Container card for content sections. Use for forms/content boxes, NOT for page headers.",
104
- example: { title: "Overview", description: "Your account summary" }
105
- },
106
- Stack: {
107
- props: z.object({
108
- direction: z.enum(["horizontal", "vertical"]).nullable(),
109
- gap: z.enum(["none", "sm", "md", "lg", "xl"]).nullable(),
110
- align: z.enum(["start", "center", "end", "stretch"]).nullable(),
111
- justify: z.enum(["start", "center", "end", "between", "around"]).nullable(),
112
- className: z.string().nullable().describe("Additional CSS classes")
113
- }),
114
- slots: ["default"],
115
- description: "Flex container for layouts",
116
- example: { direction: "vertical", gap: "md" }
117
- },
118
- Grid: {
119
- props: z.object({
120
- columns: z.number().nullable(),
121
- gap: z.enum(["sm", "md", "lg", "xl"]).nullable(),
122
- className: z.string().nullable().describe("Additional CSS classes")
123
- }),
124
- slots: ["default"],
125
- description: "Grid layout (1-6 columns)",
126
- example: { columns: 3, gap: "md" }
127
- },
128
- Separator: {
129
- props: z.object({
130
- orientation: z.enum(["horizontal", "vertical"]).nullable()
131
- }),
132
- description: "Visual separator line"
133
- },
134
- Collapsible: {
135
- props: z.object({
136
- title: z.string(),
137
- defaultOpen: z.boolean().nullable()
138
- }),
139
- slots: ["default"],
140
- description: "Collapsible section with trigger. Children render inside."
141
- },
142
- // ── Content ───────────────────────────────────────────────────────────────
143
- Heading: {
144
- props: z.object({
145
- text: z.string(),
146
- level: z.enum(["h1", "h2", "h3", "h4"]).nullable()
147
- }),
148
- description: "Heading text (h1-h4)",
149
- example: { text: "Welcome", level: "h1" }
150
- },
151
- Text: {
152
- props: z.object({
153
- text: z.string(),
154
- variant: z.enum(["body", "caption", "muted", "lead", "code"]).nullable()
155
- }),
156
- description: "Paragraph text",
157
- example: { text: "Hello, world!" }
158
- },
159
- Avatar: {
160
- props: z.object({
161
- src: z.string().nullable(),
162
- name: z.string(),
163
- size: z.enum(["sm", "md", "lg"]).nullable()
164
- }),
165
- description: "User avatar with fallback initials",
166
- example: { name: "Jane Doe", size: "md" }
167
- },
168
- Badge: {
169
- props: z.object({
170
- text: z.string(),
171
- variant: z.enum(["default", "secondary", "destructive", "outline"]).nullable()
172
- }),
173
- description: "Status badge",
174
- example: { text: "Active", variant: "default" }
175
- },
176
- Alert: {
177
- props: z.object({
178
- title: z.string(),
179
- message: z.string().nullable(),
180
- type: z.enum(["info", "success", "warning", "error"]).nullable()
181
- }),
182
- description: "Alert banner",
183
- example: {
184
- title: "Note",
185
- message: "Your changes have been saved.",
186
- type: "success"
187
- }
188
- },
189
- Progress: {
190
- props: z.object({
191
- value: z.number(),
192
- max: z.number().nullable(),
193
- label: z.string().nullable()
194
- }),
195
- description: "Progress bar (value 0-100)",
196
- example: { value: 65, max: 100, label: "Upload progress" }
197
- },
198
- // ── Inputs ────────────────────────────────────────────────────────────────
199
- Input: {
200
- props: z.object({
201
- label: z.string(),
202
- name: z.string(),
203
- type: z.enum(["text", "email", "password", "number"]).nullable(),
204
- placeholder: z.string().nullable(),
205
- value: z.string().nullable(),
206
- checks: validationCheckSchema,
207
- validateOn: validateOnSchema
208
- }),
209
- events: ["submit", "focus", "blur"],
210
- description: "Text input field. Use { $bindState } on value for two-way binding. Use checks for validation (e.g. required, email, minLength). validateOn controls timing (default: blur).",
211
- example: {
212
- label: "Email",
213
- name: "email",
214
- type: "email",
215
- placeholder: "you@example.com"
216
- }
217
- },
218
- Textarea: {
219
- props: z.object({
220
- label: z.string(),
221
- name: z.string(),
222
- placeholder: z.string().nullable(),
223
- rows: z.number().nullable(),
224
- value: z.string().nullable(),
225
- checks: validationCheckSchema,
226
- validateOn: validateOnSchema
227
- }),
228
- description: "Multi-line text input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: blur)."
229
- },
230
- Select: {
231
- props: z.object({
232
- label: z.string(),
233
- name: z.string(),
234
- options: z.array(z.string().min(1)),
235
- placeholder: z.string().nullable(),
236
- value: z.string().nullable(),
237
- checks: validationCheckSchema,
238
- validateOn: validateOnSchema
239
- }),
240
- events: ["change"],
241
- description: "Dropdown select input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change)."
242
- },
243
- Radio: {
244
- props: z.object({
245
- label: z.string(),
246
- name: z.string(),
247
- options: z.array(z.string().min(1)),
248
- value: z.string().nullable(),
249
- checks: validationCheckSchema,
250
- validateOn: validateOnSchema
251
- }),
252
- events: ["change"],
253
- description: "Radio button group. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change)."
254
- },
255
- Checkbox: {
256
- props: z.object({
257
- label: z.string(),
258
- name: z.string(),
259
- checked: z.boolean().nullable(),
260
- checks: validationCheckSchema,
261
- validateOn: validateOnSchema
262
- }),
263
- events: ["change"],
264
- description: "Checkbox input. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change)."
265
- },
266
- Switch: {
267
- props: z.object({
268
- label: z.string(),
269
- name: z.string(),
270
- checked: z.boolean().nullable(),
271
- checks: validationCheckSchema,
272
- validateOn: validateOnSchema
273
- }),
274
- events: ["change"],
275
- description: "Toggle switch. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change)."
276
- },
277
- // ── Actions ───────────────────────────────────────────────────────────────
278
- Button: {
279
- props: z.object({
280
- label: z.string(),
281
- variant: z.enum(["primary", "secondary", "danger"]).nullable(),
282
- disabled: z.boolean().nullable()
283
- }),
284
- events: ["press"],
285
- description: "Clickable button. Bind on.press for handler.",
286
- example: { label: "Submit", variant: "primary" }
287
- }
288
- };
289
-
290
- // src/generative/components.ts
291
- var shadcn = shadcnComponentDefinitions;
292
- var PICKED_SHADCN = [
293
- "Card",
294
- "Stack",
295
- "Grid",
296
- "Separator",
297
- "Heading",
298
- "Text",
299
- "Badge",
300
- "Alert",
301
- "Collapsible",
302
- "Progress",
303
- "Avatar",
304
- "Input",
305
- "Textarea",
306
- "Select",
307
- "Radio",
308
- "Checkbox",
309
- "Switch",
310
- "Button"
311
- ];
312
- var CUSTOM_COMPONENTS = [
313
- "Split",
314
- "Tabs",
315
- "Steps",
316
- "Icon",
317
- "Segmented",
318
- "NumberInput",
319
- "MthdsField",
320
- "MthdsResult",
321
- "DataTable",
322
- "Metric"
323
- ];
324
- var METRIC_FORMATS = ["plain", "integer", "decimal", "compact"];
325
- var ICON_NAMES = [
326
- "Sparkles",
327
- "Rocket",
328
- "Compass",
329
- "Map",
330
- "MapPin",
331
- "Route",
332
- "Plane",
333
- "Train",
334
- "Car",
335
- "Ship",
336
- "Hotel",
337
- "Home",
338
- "Building2",
339
- "Landmark",
340
- "Globe",
341
- "Mountain",
342
- "Leaf",
343
- "Sun",
344
- "Moon",
345
- "Calendar",
346
- "Clock",
347
- "Hourglass",
348
- "Users",
349
- "User",
350
- "Baby",
351
- "Accessibility",
352
- "Heart",
353
- "Star",
354
- "Utensils",
355
- "Coffee",
356
- "Music",
357
- "Camera",
358
- "Image",
359
- "Ticket",
360
- "Gift",
361
- "Wallet",
362
- "Banknote",
363
- "Coins",
364
- "Receipt",
365
- "Percent",
366
- "Hash",
367
- "TrendingUp",
368
- "BarChart3",
369
- "FileText",
370
- "Upload",
371
- "Paperclip",
372
- "Mail",
373
- "Phone",
374
- "Tag",
375
- "Layers",
376
- "ListChecks",
377
- "CheckCircle2",
378
- "AlertCircle",
379
- "Info",
380
- "Shield",
381
- "Flag",
382
- "Bookmark",
383
- "Briefcase",
384
- "Package",
385
- "Truck",
386
- "Lightbulb",
387
- "Zap",
388
- "Pencil",
389
- "Send"
390
- ];
391
- var components = {
392
- // ── Layout (shadcn) ────────────────────────────────────────────────────
393
- Card: {
394
- ...shadcn.Card,
395
- props: shadcn.Card.props.omit({ className: true, maxWidth: true, centered: true }),
396
- description: 'Container card for content sections. Use for forms/content boxes, NOT for page headers. Its optional "title" renders as an h3, so a titled Card sits under an h2.'
397
- },
398
- Stack: { ...shadcn.Stack, props: shadcn.Stack.props.omit({ className: true }) },
399
- Grid: { ...shadcn.Grid, props: shadcn.Grid.props.omit({ className: true }) },
400
- Separator: shadcn.Separator,
401
- Collapsible: shadcn.Collapsible,
402
- // ── Content (shadcn) ───────────────────────────────────────────────────
403
- Heading: shadcn.Heading,
404
- Text: shadcn.Text,
405
- Badge: shadcn.Badge,
406
- Alert: shadcn.Alert,
407
- Progress: shadcn.Progress,
408
- Avatar: {
409
- ...shadcn.Avatar,
410
- description: `A round avatar showing the initials of "name" - bind it to a person's name in the state ({ "$state" } or { "$item" }); never set "src".`
411
- },
412
- // ── Inputs (shadcn) ────────────────────────────────────────────────────
413
- Input: shadcn.Input,
414
- Textarea: shadcn.Textarea,
415
- Select: shadcn.Select,
416
- Radio: shadcn.Radio,
417
- Checkbox: shadcn.Checkbox,
418
- Switch: shadcn.Switch,
419
- Button: shadcn.Button,
420
- // ── Layout (ours) ──────────────────────────────────────────────────────
421
- Split: {
422
- props: z.object({
423
- ratio: z.enum(["1:1", "1:2", "2:1"]).nullable(),
424
- gap: z.enum(["md", "lg", "xl"]).nullable()
425
- }),
426
- slots: ["default"],
427
- description: 'Two columns side by side, from exactly two children: the first is the left column. "1:2" puts a narrow rail (a summary, the essentials, the run) beside the work; "2:1" the reverse; "1:1" two halves. Columns stack on a narrow screen.',
428
- example: { ratio: "1:2", gap: "lg" }
429
- },
430
- Tabs: {
431
- props: z.object({
432
- tabs: z.array(z.object({ label: z.string(), value: z.string() })).min(2)
433
- }),
434
- slots: ["default"],
435
- description: "Concerns of equal weight, one visible at a time. Exactly one child per tab, in the same order - each child is that tab's panel (usually a Stack). The first tab opens. The active tab is the component's own state, never /state.",
436
- example: {
437
- tabs: [
438
- { label: "The trip", value: "trip" },
439
- { label: "Who is going", value: "people" }
440
- ]
441
- }
442
- },
443
- Steps: {
444
- props: z.object({
445
- steps: z.array(z.string()).min(2),
446
- nextLabel: z.string().nullable(),
447
- backLabel: z.string().nullable()
448
- }),
449
- slots: ["default"],
450
- description: "A journey with a natural order: a numbered indicator, one panel at a time, and its own Back and Next. Exactly one child per step, in order - each child is that step's panel. Put the Button that runs inside the LAST panel; Next disappears there. The current step is the component's own state, never /state.",
451
- example: { steps: ["Where", "Who", "How"], nextLabel: "Continue" }
452
- },
453
- Icon: {
454
- props: z.object({
455
- name: z.enum(ICON_NAMES),
456
- size: z.enum(["sm", "md", "lg"]).nullable()
457
- }),
458
- slots: [],
459
- description: "A decorative icon, one of the listed lucide names, to give a section or a figure a face: put it in a horizontal Stack beside a Heading or a Metric. It carries no meaning on its own and no text.",
460
- example: { name: "Plane", size: "md" }
461
- },
462
- // ── Inputs (ours) ──────────────────────────────────────────────────────
463
- Segmented: {
464
- props: z.object({
465
- label: z.string(),
466
- name: z.string(),
467
- options: z.array(z.string().min(1)).min(2).max(6),
468
- value: z.string().nullable()
469
- }),
470
- slots: [],
471
- description: `A choice of two to six options as a row of pills, one selected - the app's answer to a short Select. "options" are EXACTLY the brief's choices, spelled exactly; bind "value" with { "$bindState": "/inputs/..." }.`,
472
- example: {
473
- label: "Pace",
474
- name: "pace",
475
- options: ["slow", "balanced", "packed"],
476
- value: { $bindState: "/inputs/request/pace" }
477
- }
478
- },
479
- NumberInput: {
480
- props: z.object({
481
- label: z.string(),
482
- name: z.string(),
483
- value: z.number().nullable(),
484
- placeholder: z.string().nullable(),
485
- unit: z.string().nullable(),
486
- min: z.number().nullable(),
487
- max: z.number().nullable(),
488
- step: z.number().nullable()
489
- }),
490
- slots: [],
491
- description: 'A figure the person types: writes a NUMBER to the bound path, which Input type "number" does not (it writes text). Bind "value" with { "$bindState": "/inputs/..." }. "unit" is a short suffix ONLY when the brief states one; "min", "max" and "step" only when the brief states them.',
492
- example: {
493
- label: "Budget",
494
- name: "budget",
495
- value: { $bindState: "/inputs/request/budget" },
496
- placeholder: "2500"
497
- }
498
- },
499
- // ── The escape hatches and the bound equivalents (ours) ────────────────
500
- MthdsField: {
501
- props: z.object({
502
- path: z.string(),
503
- // The page words the label and the line under it, as it words every other control.
504
- label: z.string().nullable(),
505
- hint: z.string().nullable()
506
- }),
507
- slots: [],
508
- description: `The kernel's own control for ONE input, bound two-way at "path" (a /inputs path from the brief). Use it for every path the brief marks as delegated - a file, a date, a list, a structure you would rather not lay out - and for any scalar you would rather not style. It renders the descriptor's label and description unless the page words them: "label" replaces the label, "hint" the line under it. Inside a repeat, "path" is the item's field name relative to the item ("lines"), never an $item expression.`,
509
- example: { path: "/inputs/source" }
510
- },
511
- MthdsResult: {
512
- props: z.object({
513
- path: z.string(),
514
- hideLabel: z.boolean().nullable()
515
- }),
516
- slots: [],
517
- description: `The kernel's own read-only rendering of ONE result subtree at "path" (a /result path from the brief). Use it for every path the brief marks as delegated - a file, a date, a document, prose, markup - and for any structure you choose not to lay out. It renders its own label unless hideLabel is true. Inside a repeat, "path" is the item's field name relative to the item ("teams"), never an $item expression - the way to delegate one subtree of every item.`,
518
- example: { path: "/result/issued_on" }
519
- },
520
- DataTable: {
521
- props: z.object({
522
- rows: z.any(),
523
- // A column says how its cells read, from the kind the brief states.
524
- columns: z.array(
525
- z.object({
526
- path: z.string(),
527
- label: z.string(),
528
- format: z.enum(["text", "date", "number"]).nullable().optional()
529
- })
530
- ),
531
- caption: z.string().nullable()
532
- }),
533
- slots: [],
534
- description: 'A table over a list of structures. "rows" MUST be { "$state": "/path/to/the/list" } (never inline rows); each column names a field of one item by its relative path ("label", "unit_price"), the header to show for it, and how its cells read: "date" for a date path (written "9 Mar 2025"), "number" for a number path (digits grouped), "text" otherwise.',
535
- example: {
536
- rows: { $state: "/result/lines" },
537
- columns: [
538
- { path: "label", label: "Item" },
539
- { path: "quantity", label: "Qty" }
540
- ]
541
- }
542
- },
543
- Metric: {
544
- props: z.object({
545
- label: z.string(),
546
- value: z.any(),
547
- unit: z.string().nullable(),
548
- format: z.enum(METRIC_FORMATS).nullable()
549
- }),
550
- slots: [],
551
- description: 'A labelled figure - the one number a reader looks for first. "value" is bound with { "$state": "/path" } (never inline); "unit" is a short suffix and ONLY one the brief states (a currency the structure carries, "%" for a rate, "M" when the brief says "in millions") - never invent a currency the state does not carry; "format" is plain, integer, decimal (two places) or compact.',
552
- example: { label: "Total", value: { $state: "/result/total" }, unit: "EUR", format: "decimal" }
553
- }
554
- };
555
- var actions = {
556
- run: {
557
- params: z.object({}),
558
- description: "Start the run with the current /inputs. The host handles it; an input page binds it to its one Button after validateForm."
559
- }
560
- };
561
- defineCatalog(generativeSchema, { components, actions });
562
- var generativeComponents = components;
563
- var generativeActions = actions;
564
- var FRAMES = ["page", "composer", "result"];
565
- var COMPOSER_COMPONENTS = ["Composer", "FieldGroup"];
566
- var RESULT_COMPONENTS = [
567
- "ResultHeader",
568
- "ProportionBar",
569
- "Swatches",
570
- "Picture",
571
- "Fact",
572
- "Detail",
573
- "DateText",
574
- "TextList"
575
- ];
576
- var FRAME_COMPONENTS = [...COMPOSER_COMPONENTS, ...RESULT_COMPONENTS];
577
- var frameComponents = {
578
- Composer: {
579
- props: z.object({
580
- headline: z.string(),
581
- lede: z.string().nullable(),
582
- runLabel: z.string(),
583
- columns: z.enum(["1", "2"]).nullable(),
584
- notes: z.array(z.string()).nullable()
585
- }),
586
- slots: ["default"],
587
- events: ["press"],
588
- description: `The composer: the page's headline (rendered as the h1) and one muted line under it, then a single raised panel holding its children - the inputs - in one column, or two columns side by side when "columns" is "2", and at the panel's foot the host's action row: how long a run takes, as the host knows it, and the one button that runs the method, worded by "runLabel". The action row stays on screen while a long panel scrolls. It is the control that runs the method: bind on.press to validateForm then run. "notes" are a few short lines set under the panel, each after a check mark, in two columns: what one run gives back, in a person's words.`,
589
- example: {
590
- headline: "A day-by-day plan for your trip",
591
- lede: "Tell us who is going, where and when.",
592
- runLabel: "Plan my trip",
593
- columns: "2"
594
- }
595
- },
596
- FieldGroup: {
597
- props: z.object({ title: z.string().nullable() }),
598
- slots: ["default"],
599
- description: "A quiet group of inputs inside a panel: an optional small h2 title, then its children stacked with even spacing. No box, no rule: the panel around it is the surface.",
600
- example: { title: "Where and when" }
601
- },
602
- ResultHeader: {
603
- props: z.object({
604
- eyebrow: z.string().nullable(),
605
- title: z.string(),
606
- subtitle: z.string().nullable()
607
- }),
608
- slots: [],
609
- description: `The result's heading: a small eyebrow in the accent colour, the title (rendered as the h1) and one muted line under it, with the host's actions on the result (download, share) at the right. Text props may be read from the state with { "$state" } or { "$template" }.`,
610
- example: {
611
- eyebrow: "Your plan",
612
- title: { $state: "/result/title" },
613
- subtitle: { $template: "${/result/currency} ${/result/estimated_total} estimated" }
614
- }
615
- },
616
- ProportionBar: {
617
- props: z.object({
618
- label: z.string(),
619
- total: z.number().nullable(),
620
- of: z.number().nullable(),
621
- unit: z.string().nullable(),
622
- items: z.array(z.any()).nullable(),
623
- labelKey: z.string(),
624
- valueKey: z.string()
625
- }),
626
- slots: [],
627
- description: 'A labelled total over its parts, in a raised panel: the label and the total in large figures ("of" a ceiling when there is one), then one bar split in proportion to each part, and a legend naming each part with its amount. "items" is { "$state": ... } on a list of structures; "labelKey" and "valueKey" name the member of one item that labels it and the number that sizes it. "unit" is a currency or unit the state carries, read with { "$state" }.',
628
- example: {
629
- label: "Estimated cost",
630
- total: { $state: "/result/estimated_total" },
631
- unit: { $state: "/result/currency" },
632
- items: { $state: "/result/costs" },
633
- labelKey: "category",
634
- valueKey: "amount"
635
- }
636
- },
637
- Swatches: {
638
- props: z.object({ colors: z.array(z.string()) }),
639
- slots: [],
640
- description: 'A row of colour swatches, each a small tile filled with the colour and its value written under it in mono. "colors" is a list of colour values, each read from a text path ({ "$state" }, or { "$item" } inside a repeat).',
641
- example: { colors: [{ $item: "colors/primary" }, { $item: "colors/accent" }] }
642
- },
643
- Picture: {
644
- props: z.object({
645
- path: z.string(),
646
- alt: z.string().nullable(),
647
- aspect: z.enum(["16:9", "4:3", "1:1", "auto"]).nullable()
648
- }),
649
- slots: [],
650
- description: 'A picture the run produced, shown whole and edge to edge in its column with a hairline border, cropped to "aspect" when one is set. "path" is an image path of the brief, literally ("/result/cover"), or relative to the item inside a repeat ("" for the item itself, when the repeat is over a list of images).',
651
- example: { path: "/result/cover", alt: "The cover", aspect: "16:9" }
652
- },
653
- Fact: {
654
- props: z.object({
655
- label: z.string(),
656
- value: z.any(),
657
- detail: z.string().nullable()
658
- }),
659
- slots: [],
660
- description: 'One fact as a small boxed tile: a muted label, the value under it, and an optional muted detail. Several in a Grid make a band of facts. "value" is a scalar read with { "$state" } or { "$template" }.',
661
- example: { label: "Order number", value: { $state: "/result/number" } }
662
- },
663
- DateText: {
664
- props: z.object({
665
- value: z.string().nullable(),
666
- style: z.enum(["long", "short", "weekday"]).nullable(),
667
- tone: z.enum(["default", "muted", "accent"]).nullable()
668
- }),
669
- slots: [],
670
- description: 'A date written in words: "long" is "Wednesday 14 April 2027", "short" is "14 Apr 2027", "weekday" is "Wed 14 April". "value" is a date path read with { "$state" } or { "$item" }; "tone" colours it muted or in the accent colour, as an eyebrow over a title.',
671
- example: { value: { $item: "day" }, style: "weekday", tone: "accent" }
672
- },
673
- TextList: {
674
- props: z.object({
675
- items: z.array(z.string()).nullable(),
676
- marker: z.enum(["bullet", "number", "check"]).nullable()
677
- }),
678
- slots: [],
679
- description: 'A list of short texts as one list, each entry a line with a small marker before it: a dot, its number or a check. "items" reads a list of text path with { "$state" }, or { "$item" } inside a repeat - the whole list at once, so it needs no repeat of its own.',
680
- example: { items: { $state: "/result/notes" }, marker: "bullet" }
681
- },
682
- Detail: {
683
- props: z.object({
684
- label: z.string(),
685
- value: z.any()
686
- }),
687
- slots: [],
688
- description: 'A labelled line of text: the label muted in a narrow column at the left, the value wrapping beside it, a hairline under the line. Several in a column read as a list of details. "value" is a scalar read with { "$state" }, { "$item" } or { "$template" }; the line hides itself while the value is empty.',
689
- example: { label: "Morning", value: { $item: "morning" } }
690
- }
691
- };
692
-
693
- // src/generative/catalog.ts
694
- var PRODUCT_COMPONENTS = [
695
- "AppBar",
696
- "Hero",
697
- "Workspace",
698
- "Section",
699
- "Rail",
700
- "SummaryRow",
701
- "Cta",
702
- "Footer"
703
- ];
704
- var product = {
705
- AppBar: {
706
- props: z.object({
707
- app: z.string(),
708
- links: z.array(z.string()).nullable(),
709
- tag: z.string().nullable()
710
- }),
711
- slots: [],
712
- description: "A bar across the top of the page: the brand's logo, the name of this app beside it, a few muted links, and a small mono tag at the right (the method behind the page). It renders as the page's banner, so at most one.",
713
- example: { app: "Trip planner", links: ["Methods", "Runs"], tag: "plan_trip" }
714
- },
715
- Hero: {
716
- props: z.object({
717
- headline: z.string(),
718
- lede: z.string().nullable(),
719
- eyebrow: z.string().nullable()
720
- }),
721
- slots: [],
722
- description: "An opening: one bold headline that says what happens here, one muted line under it at most, and an optional small eyebrow in the accent colour above. The headline renders as an h1, so at most one on a page.",
723
- example: { headline: "Plan a trip worth taking.", lede: "Tell us where and who is coming." }
724
- },
725
- Workspace: {
726
- props: z.object({ rail: z.enum(["right", "left"]).nullable() }),
727
- slots: ["default"],
728
- description: 'Two children side by side on a wide screen and stacked on a narrow one: the work, and beside it a narrower panel that stays sticky while the work scrolls. Exactly two children; "rail" says which side the panel takes, right by default.',
729
- example: { rail: "right" }
730
- },
731
- Section: {
732
- props: z.object({
733
- number: z.string().nullable(),
734
- title: z.string(),
735
- lede: z.string().nullable()
736
- }),
737
- slots: ["default"],
738
- description: "A titled stage of the work: an optional small mono number in the accent colour, an h2 title, an optional muted line, then its children - flat, spaced, separated from the next Section by a hairline, never boxed.",
739
- example: { number: "01", title: "Where and when" }
740
- },
741
- Rail: {
742
- props: z.object({ title: z.string() }),
743
- slots: ["default"],
744
- description: "A raised, boxed panel: a small uppercase h2 title, then its children in a column. Made to stand beside the work as the panel of a Workspace, and the one boxed surface in this vocabulary.",
745
- example: { title: "Your trip" }
746
- },
747
- SummaryRow: {
748
- props: z.object({
749
- label: z.string(),
750
- value: z.any().nullable(),
751
- detail: z.any().nullable(),
752
- separator: z.string().nullable(),
753
- placeholder: z.string().nullable()
754
- }),
755
- slots: [],
756
- description: 'A line that restates a value: a muted label at the left, a bound value at the right. "value" and the optional "detail" are read with { "$state": "/inputs/..." } and joined by "separator" (a space by default); each is a scalar path - text, number, boolean, date or choice - never a structure, a list or a file. While both are empty the row shows "placeholder", or a dash.',
757
- example: {
758
- label: "Budget",
759
- value: { $state: "/inputs/request/budget" },
760
- detail: { $state: "/inputs/request/currency" }
761
- }
762
- },
763
- Cta: {
764
- props: z.object({ label: z.string(), hint: z.string().nullable() }),
765
- slots: [],
766
- events: ["press"],
767
- description: "A call to action: a full-width button in the accent colour that runs the method, with an optional one-line hint under it. Bind on.press to validateForm then run.",
768
- example: { label: "Plan my trip", hint: "Nothing runs until the request is complete." }
769
- },
770
- Footer: {
771
- props: z.object({ text: z.string(), tag: z.string().nullable() }),
772
- slots: [],
773
- description: "A closing line, muted, with an optional mono tag at the right. It renders as the page's contentinfo, so at most one.",
774
- example: { text: "Runs on Pipelex.", tag: "MTHDS" }
775
- }
776
- };
777
- var COMPONENT_RENDERINGS = {
778
- Hero: { heading: { level: 1 } },
779
- Section: { heading: { level: 2 } },
780
- Rail: { heading: { level: 2 } },
781
- Card: { heading: { level: 3, when: "title" } },
782
- AppBar: { once: "the page's banner" },
783
- Footer: { once: "the page's contentinfo" },
784
- Split: { children: { count: 2, roles: "left, right" } },
785
- Workspace: { children: { count: 2, roles: "work, rail" } },
786
- Tabs: { panels: { prop: "tabs", noun: "tab" } },
787
- Steps: { panels: { prop: "steps", noun: "step" } },
788
- Composer: { heading: { level: 1 }, once: "the composer" },
789
- ResultHeader: { heading: { level: 1 }, once: "the result's header" },
790
- FieldGroup: { heading: { level: 2, when: "title" } }
791
- };
792
- var catalog = Object.assign(
793
- defineCatalog(generativeSchema, {
794
- components: { ...generativeComponents, ...product, ...frameComponents },
795
- actions: generativeActions
796
- }),
797
- { renders: COMPONENT_RENDERINGS }
798
- );
799
- var COMPONENT_NAMES = [
800
- ...PICKED_SHADCN,
801
- ...CUSTOM_COMPONENTS,
802
- ...PRODUCT_COMPONENTS
803
- ];
804
- var FRAME_EXCLUDED = {
805
- page: [...FRAME_COMPONENTS],
806
- composer: [
807
- "AppBar",
808
- "Footer",
809
- "Workspace",
810
- "Rail",
811
- "Cta",
812
- "Button",
813
- "Hero",
814
- "SummaryRow",
815
- "MthdsResult",
816
- "DataTable",
817
- "Metric",
818
- "Progress",
819
- // `TextList` stays: a composer may list short texts too, such as what a
820
- // run gives back.
821
- ...RESULT_COMPONENTS.filter((name) => name !== "TextList")
822
- ],
823
- result: [
824
- "AppBar",
825
- "Footer",
826
- "Workspace",
827
- "Rail",
828
- "Cta",
829
- "Button",
830
- "Hero",
831
- "MthdsField",
832
- "Input",
833
- "Textarea",
834
- "Select",
835
- "Radio",
836
- "Checkbox",
837
- "Switch",
838
- "Segmented",
839
- "NumberInput",
840
- "Steps",
841
- ...COMPOSER_COMPONENTS
842
- ]
843
- };
844
- function componentNamesFor(frame) {
845
- const excluded = new Set(FRAME_EXCLUDED[frame]);
846
- const own = frame === "page" ? [] : [...FRAME_COMPONENTS];
847
- return [...COMPONENT_NAMES, ...own].filter((name) => !excluded.has(name));
848
- }
849
- function frameCatalog(frame) {
850
- return {
851
- componentNames: componentNamesFor(frame),
852
- actionNames: catalog.actionNames,
853
- data: catalog.data,
854
- schema: catalog.schema,
855
- renders: COMPONENT_RENDERINGS
856
- };
857
- }
858
-
859
- // src/generative/props-signature.ts
860
- function definitionOf(schema) {
861
- return schema._zod.def;
862
- }
863
- function propsSignature(schema) {
864
- const def = definitionOf(schema);
865
- switch (def.type) {
866
- case "string":
867
- return "string";
868
- case "number":
869
- return "number";
870
- case "boolean":
871
- return "boolean";
872
- case "literal":
873
- return JSON.stringify(def.values?.[0]);
874
- case "enum":
875
- return Object.values(def.entries ?? {}).map((value) => `"${String(value)}"`).join(" | ");
876
- case "array":
877
- return `Array<${def.element ? propsSignature(def.element) : "unknown"}>`;
878
- case "object": {
879
- const props = Object.entries(def.shape ?? {}).map(([key, value]) => {
880
- const inner = definitionOf(value).type;
881
- const optional = inner === "optional" || inner === "nullable";
882
- return `${key}${optional ? "?" : ""}: ${propsSignature(value)}`;
883
- });
884
- return `{ ${props.join(", ")} }`;
885
- }
886
- case "optional":
887
- case "nullable":
888
- case "default":
889
- return def.innerType ? propsSignature(def.innerType) : "unknown";
890
- case "union":
891
- return def.options ? def.options.map((option) => propsSignature(option)).join(" | ") : "unknown";
892
- case "record": {
893
- const key = def.keyType ? propsSignature(def.keyType) : "string";
894
- const value = def.valueType ? propsSignature(def.valueType) : "unknown";
895
- return `Record<${key}, ${value}>`;
896
- }
897
- default:
898
- return "unknown";
899
- }
900
- }
901
-
902
- // src/generative/designer-catalog.ts
903
- var DESIGNER_CATALOG_CONCEPT = "generative.Catalog";
904
- function designerCatalog({ frame = "page" } = {}) {
905
- const definitions = catalog.data.components;
906
- const components3 = componentNamesFor(frame).map((name) => {
907
- const definition = definitions[name];
908
- if (!definition) throw new Error(`the catalog defines no component named '${name}'`);
909
- const slots = definition.slots ?? [];
910
- return {
911
- name,
912
- props: propsSignature(definition.props),
913
- description: definition.description,
914
- accepts_children: slots.includes("default"),
915
- slots: slots.filter((slot) => slot !== "default"),
916
- events: [...definition.events ?? []]
917
- };
918
- });
919
- const builtIn = (generativeSchema.builtInActions ?? []).map((action) => ({
920
- name: action.name,
921
- description: action.description,
922
- built_in: true
923
- }));
924
- const own = Object.entries(catalog.data.actions).map(
925
- ([name, action]) => ({
926
- name,
927
- description: action.description,
928
- built_in: false
929
- })
930
- );
931
- return { components: components3, actions: [...builtIn, ...own] };
932
- }
933
-
934
- // src/generative/prompt-hash.ts
935
- var PROMPT_HASH = "30cf9bc059f6";
936
- function promptHashSubject(method) {
937
- const catalogs = FRAMES.map((frame) => designerCatalog({ frame }));
938
- return `${method}
939
- ${JSON.stringify(catalogs)}`;
940
- }
941
-
942
- // src/generative/paths.ts
943
- var INPUTS_ROOT = "/inputs";
944
- var RESULT_ROOT = "/result";
945
- function joinPath(root, ...segments) {
946
- return segments.reduce((path, segment) => `${path}/${escapeSegment(segment)}`, root);
947
- }
948
- function escapeSegment(segment) {
949
- return segment.replace(/~/g, "~0").replace(/\//g, "~1");
950
- }
951
- function unescapeSegment(segment) {
952
- return segment.replace(/~1/g, "/").replace(/~0/g, "~");
953
- }
954
- function segmentsUnder(root, path) {
955
- if (path === root) return [];
956
- if (!path.startsWith(`${root}/`)) return void 0;
957
- return path.slice(root.length + 1).split("/").map(unescapeSegment);
958
- }
959
- function fieldAtSegments(fields, segments) {
960
- const [head, ...rest] = segments;
961
- if (head === void 0) return void 0;
962
- const field = fields.find((candidate) => candidate.name === head);
963
- if (!field) return void 0;
964
- return rest.length === 0 ? field : descend(field, rest);
965
- }
966
- function descend(field, segments) {
967
- const [head, ...rest] = segments;
968
- if (head === void 0) return field;
969
- if (field.kind === "object") return fieldAtSegments(field.fields, segments);
970
- if (field.kind === "list" && /^\d+$/.test(head)) return descend(field.item, rest);
971
- return void 0;
972
- }
973
- function inputFieldAtPath(fields, path) {
974
- const segments = segmentsUnder(INPUTS_ROOT, path);
975
- return segments && segments.length > 0 ? fieldAtSegments(fields, segments) : void 0;
976
- }
977
- function resultFieldAtPath(field, path) {
978
- const segments = segmentsUnder(RESULT_ROOT, path);
979
- if (!segments) return void 0;
980
- return segments.length === 0 ? field : descend(field, segments);
981
- }
982
- function parentMapOf(spec) {
983
- const parents = /* @__PURE__ */ new Map();
984
- for (const [parentKey, element] of Object.entries(spec.elements)) {
985
- for (const child of element.children ?? []) parents.set(child, parentKey);
986
- }
987
- return parents;
988
- }
989
- function repeatBasePathOf(spec, key, parents = parentMapOf(spec)) {
990
- const chain = [];
991
- const seen = /* @__PURE__ */ new Set();
992
- let current = key;
993
- while (current !== void 0 && !seen.has(current)) {
994
- seen.add(current);
995
- chain.unshift(current);
996
- current = parents.get(current);
997
- }
998
- let base;
999
- for (const elementKey of chain) {
1000
- const repeat = spec.elements[elementKey]?.repeat;
1001
- if (!repeat) continue;
1002
- const statePath = repeat.statePath;
1003
- if (typeof statePath === "string") {
1004
- base = `${statePath}/0`;
1005
- continue;
1006
- }
1007
- const item = statePath?.$item;
1008
- if (base === void 0 || typeof item !== "string") return void 0;
1009
- base = `${joinPath(base, item)}/0`;
1010
- }
1011
- return base;
1012
- }
1013
- function absoluteHatchPath(spec, key, path, parents) {
1014
- if (path.startsWith("/")) return path;
1015
- const base = repeatBasePathOf(spec, key, parents);
1016
- return base ? `${base}/${path}` : void 0;
1017
- }
1018
- function repeatListPathOf(spec, key, parents = parentMapOf(spec)) {
1019
- const statePath = spec.elements[key]?.repeat?.statePath;
1020
- if (typeof statePath === "string") return statePath;
1021
- const item = statePath?.$item;
1022
- const parent = parents.get(key);
1023
- const outer = parent === void 0 ? void 0 : repeatBasePathOf(spec, parent, parents);
1024
- return outer !== void 0 && typeof item === "string" ? joinPath(outer, item) : void 0;
1025
- }
1026
-
1027
- // src/generative/layout-fits.ts
1028
- function isUnder(root, path) {
1029
- return path === root || path.startsWith(`${root}/`);
1030
- }
1031
- function itemPathResolver(spec, key, parents) {
1032
- let base;
1033
- let resolvedBase = false;
1034
- return (field) => {
1035
- if (!resolvedBase) {
1036
- base = repeatBasePathOf(spec, key, parents);
1037
- resolvedBase = true;
1038
- }
1039
- if (base === void 0) return void 0;
1040
- const under = field.startsWith("/") ? field.slice(1) : field;
1041
- return under === "" ? base : `${base}/${under}`;
1042
- };
1043
- }
1044
- function interpolatedPaths(template, itemPath) {
1045
- return [...template.matchAll(/\$\{([^}]+)\}/g)].flatMap((match) => {
1046
- const raw = match[1];
1047
- if (raw === void 0) return [];
1048
- if (raw.startsWith("/")) return [raw];
1049
- return [itemPath(raw) ?? `/${raw}`];
1050
- });
1051
- }
1052
- function mentionedPaths(spec, parents) {
1053
- const found = { bound: [], read: [], adrift: [] };
1054
- for (const [key, element] of Object.entries(spec.elements)) {
1055
- const itemPath = itemPathResolver(spec, key, parents);
1056
- const walk = (value) => {
1057
- if (Array.isArray(value)) {
1058
- value.forEach(walk);
1059
- return;
1060
- }
1061
- if (typeof value !== "object" || value === null) return;
1062
- const record = value;
1063
- if (typeof record.$bindState === "string") found.bound.push(record.$bindState);
1064
- if (typeof record.$state === "string") found.read.push(record.$state);
1065
- if (typeof record.$template === "string") {
1066
- found.read.push(...interpolatedPaths(record.$template, itemPath));
1067
- }
1068
- if (typeof record.$bindItem === "string") {
1069
- const path = itemPath(record.$bindItem);
1070
- if (path === void 0) found.adrift.push(key);
1071
- else found.bound.push(path);
1072
- }
1073
- if (typeof record.$item === "string") {
1074
- const path = itemPath(record.$item);
1075
- if (path === void 0) found.adrift.push(key);
1076
- else found.read.push(path);
1077
- }
1078
- Object.values(record).forEach(walk);
1079
- };
1080
- walk(element.props);
1081
- walk(element.visible);
1082
- }
1083
- return found;
1084
- }
1085
- function staleReads(read, root, resolves) {
1086
- const under = read.filter((path) => isUnder(root, path));
1087
- return [...new Set(under)].filter((path) => !resolves(path));
1088
- }
1089
- function staleRepeats(spec, parents, root, resolves, side) {
1090
- const problems = [];
1091
- for (const [key, element] of Object.entries(spec.elements)) {
1092
- if (!element.repeat) continue;
1093
- const listPath = repeatListPathOf(spec, key, parents);
1094
- if (listPath === void 0 || !isUnder(root, listPath)) continue;
1095
- if (!resolves(listPath)) {
1096
- problems.push(`${key}: repeats over ${listPath}, which no ${side} has`);
1097
- }
1098
- }
1099
- return problems;
1100
- }
1101
- var NOT_A_VALUE = {
1102
- object: { what: "a structure", instead: "a structure is shown through its members" },
1103
- list: { what: "a list", instead: "a list is laid out as a repeat or a DataTable" },
1104
- document: { what: "a file", instead: "a file is delegated to the kernel's own control" },
1105
- image: { what: "a file", instead: "a file is delegated to the kernel's own control" }
1106
- };
1107
- var NOT_VALUES = {
1108
- object: "structures",
1109
- list: "lists",
1110
- document: "files",
1111
- image: "files"
1112
- };
1113
- var listPropsCache;
1114
- function listProps() {
1115
- if (listPropsCache) return listPropsCache;
1116
- const found = /* @__PURE__ */ new Map([
1117
- ["DataTable", /* @__PURE__ */ new Map([["rows", "anything"]])]
1118
- ]);
1119
- const components3 = catalog.data.components;
1120
- for (const [type, definition] of Object.entries(components3)) {
1121
- const shape = definition.props?.shape;
1122
- for (const [prop, schema] of Object.entries(shape ?? {})) {
1123
- const items = listItemsOf(schema);
1124
- if (items === void 0) continue;
1125
- if (!found.has(type)) found.set(type, /* @__PURE__ */ new Map());
1126
- found.get(type).set(prop, items);
1127
- }
1128
- }
1129
- listPropsCache = found;
1130
- return found;
1131
- }
1132
- var VALUE_SCHEMAS = /* @__PURE__ */ new Set(["string", "number", "boolean", "enum", "literal"]);
1133
- function unwrapped(schema) {
1134
- let current = schema;
1135
- for (let depth = 0; depth < 8; depth += 1) {
1136
- const def = current.def;
1137
- if (!def) return void 0;
1138
- if (def.innerType === void 0) return def;
1139
- current = def.innerType;
1140
- }
1141
- return void 0;
1142
- }
1143
- function listItemsOf(schema) {
1144
- const def = unwrapped(schema);
1145
- if (def?.type !== "array") return void 0;
1146
- const element = unwrapped(def.element);
1147
- return element?.type !== void 0 && VALUE_SCHEMAS.has(element.type) ? "values" : "anything";
1148
- }
1149
- function nonScalarReads(spec, parents, root, fieldAt) {
1150
- const problems = [];
1151
- const lists = listProps();
1152
- for (const [key, element] of Object.entries(spec.elements)) {
1153
- const itemPath = itemPathResolver(spec, key, parents);
1154
- const listy = lists.get(element.type);
1155
- const check = (prop, path) => {
1156
- if (!isUnder(root, path)) return;
1157
- const field = fieldAt(path);
1158
- if (!field) return;
1159
- const wrong = hasOwnProp(NOT_A_VALUE, field.kind) ? NOT_A_VALUE[field.kind] : void 0;
1160
- if (!wrong) return;
1161
- problems.push(
1162
- `${key}: ${prop} shows ${path}, which is ${wrong.what} and not a value; a prop that shows a value takes a scalar path, and ${wrong.instead}`
1163
- );
1164
- };
1165
- const checkList = (prop, path, items) => {
1166
- if (!isUnder(root, path)) return;
1167
- const field = fieldAt(path);
1168
- if (!field || field.kind === "unknown") return;
1169
- if (field.kind !== "list") {
1170
- const what = hasOwnProp(NOT_A_VALUE, field.kind) ? NOT_A_VALUE[field.kind].what : "a value";
1171
- problems.push(
1172
- `${key}: ${prop} takes a list, and ${path} is ${what}; a prop that takes a list reads a list path`
1173
- );
1174
- return;
1175
- }
1176
- if (items !== "values") return;
1177
- const of = hasOwnProp(NOT_VALUES, field.item.kind) ? NOT_VALUES[field.item.kind] : void 0;
1178
- if (!of) return;
1179
- problems.push(
1180
- `${key}: ${prop} takes a list of values, and ${path} is a list of ${of}; a list of ${of} is laid out as a repeat or a DataTable`
1181
- );
1182
- };
1183
- const checkShares = (path) => {
1184
- if (element.type !== "ProportionBar" || !isUnder(root, path)) return;
1185
- const field = fieldAt(path);
1186
- if (field?.kind !== "list") return;
1187
- if (field.item.kind !== "object") {
1188
- problems.push(
1189
- `${key}: ProportionBar reads ${path}, a list of ${field.item.kind} values; its parts are structures, each with a label member and a number member`
1190
- );
1191
- return;
1192
- }
1193
- const members = field.item.fields;
1194
- const { labelKey, valueKey } = element.props ?? {};
1195
- const member = (name) => typeof name === "string" ? members.find((m) => m.name === name) : void 0;
1196
- if (!member(labelKey)) {
1197
- problems.push(
1198
- `${key}: ProportionBar's labelKey ${JSON.stringify(labelKey)} is no member of ${path}'s items`
1199
- );
1200
- }
1201
- const value = member(valueKey);
1202
- if (!value) {
1203
- problems.push(
1204
- `${key}: ProportionBar's valueKey ${JSON.stringify(valueKey)} is no member of ${path}'s items`
1205
- );
1206
- } else if (value.kind !== "number") {
1207
- problems.push(
1208
- `${key}: ProportionBar's valueKey ${JSON.stringify(valueKey)} holds a ${value.kind}, not a number`
1209
- );
1210
- }
1211
- };
1212
- const walk = (prop, value) => {
1213
- if (Array.isArray(value)) {
1214
- value.forEach((entry) => walk(prop, entry));
1215
- return;
1216
- }
1217
- if (typeof value !== "object" || value === null) return;
1218
- const record = value;
1219
- if (typeof record.$state === "string") check(prop, record.$state);
1220
- if (typeof record.$item === "string") {
1221
- const path = itemPath(record.$item);
1222
- if (path !== void 0) check(prop, path);
1223
- }
1224
- if (typeof record.$template === "string") {
1225
- for (const path of interpolatedPaths(record.$template, itemPath)) check(prop, path);
1226
- }
1227
- for (const [name, entry] of Object.entries(record)) {
1228
- if (name !== "$cond") walk(prop, entry);
1229
- }
1230
- };
1231
- for (const [prop, value] of Object.entries(element.props ?? {})) {
1232
- const items = listy?.get(prop);
1233
- if (items === void 0 || Array.isArray(value)) {
1234
- walk(prop, value);
1235
- continue;
1236
- }
1237
- if (typeof value !== "object" || value === null) continue;
1238
- const record = value;
1239
- const listPath = typeof record.$state === "string" ? record.$state : typeof record.$item === "string" ? itemPath(record.$item) : void 0;
1240
- if (listPath !== void 0) {
1241
- checkList(prop, listPath, items);
1242
- if (prop === "items") checkShares(listPath);
1243
- }
1244
- }
1245
- }
1246
- return problems;
1247
- }
1248
- function boundProblems(bound, inputs) {
1249
- const problems = [];
1250
- const underInputs = bound.filter((path) => isUnder(INPUTS_ROOT, path));
1251
- if (!inputs) {
1252
- if (underInputs.length > 0) {
1253
- problems.push(`a result page binds ${underInputs.join(", ")}; a result page writes nothing`);
1254
- }
1255
- } else {
1256
- for (const path of underInputs) {
1257
- if (!inputFieldAtPath(inputs, path)) {
1258
- problems.push(`${path} is bound, and no input has it`);
1259
- }
1260
- }
1261
- }
1262
- for (const path of bound) {
1263
- if (isUnder(RESULT_ROOT, path)) {
1264
- problems.push(
1265
- `${path} is bound; a layout may not write into ${RESULT_ROOT}, which the run fills`
1266
- );
1267
- }
1268
- }
1269
- return problems;
1270
- }
1271
- function repeatPaths(spec) {
1272
- const found = [];
1273
- for (const element of Object.values(spec.elements)) {
1274
- const statePath = element.repeat?.statePath;
1275
- if (typeof statePath === "string") found.push(statePath);
1276
- }
1277
- return found;
1278
- }
1279
- function isOffered(path, offered) {
1280
- if (offered.bound.has(path) || offered.repeated.has(path)) return true;
1281
- return offered.delegated.some((at) => path === at || path.startsWith(`${at}/`));
1282
- }
1283
- function uncoveredUnder(field, path, offered) {
1284
- if (isOffered(path, offered)) return [];
1285
- if (field.kind === "object") {
1286
- return field.fields.filter((child) => child.required).flatMap((child) => uncoveredUnder(child, joinPath(path, child.name), offered));
1287
- }
1288
- return [path];
1289
- }
1290
- function layoutProblems(descriptor, spec) {
1291
- try {
1292
- return collectLayoutProblems(descriptor, spec);
1293
- } catch (error) {
1294
- return [`the layout is not a well-formed spec: ${String(error)}`];
1295
- }
1296
- }
1297
- function collectLayoutProblems(descriptor, spec) {
1298
- const problems = [];
1299
- const delegated = [];
1300
- const parents = parentMapOf(spec);
1301
- for (const [key, element] of Object.entries(spec.elements)) {
1302
- const written = element.props?.path;
1303
- const path = typeof written === "string" ? absoluteHatchPath(spec, key, written, parents) : void 0;
1304
- if (element.type === "MthdsField") {
1305
- if (!path || !inputFieldAtPath(descriptor.inputs ?? [], path)) {
1306
- problems.push(`${key}: MthdsField delegates ${String(written)}, which no input has`);
1307
- } else {
1308
- delegated.push(path);
1309
- }
1310
- }
1311
- if (element.type === "MthdsResult") {
1312
- if (!path || !descriptor.result || !resultFieldAtPath(descriptor.result, path)) {
1313
- problems.push(`${key}: MthdsResult delegates ${String(written)}, which no result has`);
1314
- }
1315
- }
1316
- const [onlyKey] = element.children?.length === 1 ? element.children : [];
1317
- const only = onlyKey === void 0 ? void 0 : spec.elements[onlyKey];
1318
- if (element.type === "Grid" && element.repeat === void 0 && only?.repeat !== void 0) {
1319
- problems.push(
1320
- `${key}: Grid's only child ${onlyKey} carries the repeat, so every item lands in one cell; put the repeat on ${key} itself, with the item's element as its child`
1321
- );
1322
- }
1323
- if (element.type === "Picture") {
1324
- const shown = written === "" ? repeatBasePathOf(spec, key, parents) : path ?? void 0;
1325
- const field = shown && descriptor.result ? resultFieldAtPath(descriptor.result, shown) : void 0;
1326
- if (!field) {
1327
- problems.push(`${key}: Picture shows ${JSON.stringify(written)}, which no result has`);
1328
- } else if (field.kind !== "image") {
1329
- problems.push(`${key}: Picture shows ${shown}, which holds a ${field.kind}, not an image`);
1330
- }
1331
- }
1332
- }
1333
- const { bound, read, adrift } = mentionedPaths(spec, parents);
1334
- for (const key of new Set(adrift)) {
1335
- problems.push(`${key}: reads the current item, and no repeat above it has one`);
1336
- }
1337
- problems.push(...boundProblems(bound, descriptor.inputs));
1338
- if (!descriptor.inputs) {
1339
- const result = descriptor.result;
1340
- const fieldAt2 = (path) => result ? resultFieldAtPath(result, path) : void 0;
1341
- const resolves2 = (path) => fieldAt2(path) !== void 0;
1342
- problems.push(...staleRepeats(spec, parents, RESULT_ROOT, resolves2, "result"));
1343
- for (const path of staleReads(read, RESULT_ROOT, resolves2)) {
1344
- problems.push(`${path} is read, and no result has it`);
1345
- }
1346
- problems.push(...nonScalarReads(spec, parents, RESULT_ROOT, fieldAt2));
1347
- return problems;
1348
- }
1349
- const inputs = descriptor.inputs;
1350
- const fieldAt = (path) => inputFieldAtPath(inputs, path);
1351
- const resolves = (path) => fieldAt(path) !== void 0;
1352
- problems.push(...staleRepeats(spec, parents, INPUTS_ROOT, resolves, "input"));
1353
- for (const path of staleReads(read, INPUTS_ROOT, resolves)) {
1354
- problems.push(`${path} is read, and no input has it`);
1355
- }
1356
- problems.push(...nonScalarReads(spec, parents, INPUTS_ROOT, fieldAt));
1357
- const offered = {
1358
- bound: new Set(bound),
1359
- repeated: new Set(repeatPaths(spec)),
1360
- delegated
1361
- };
1362
- for (const field of inputs) {
1363
- if (!field.required) continue;
1364
- for (const path of uncoveredUnder(field, joinPath(INPUTS_ROOT, field.name), offered)) {
1365
- problems.push(`${path} is required, and the layout offers nowhere to enter it`);
1366
- }
1367
- }
1368
- return problems;
1369
- }
1370
- function layoutFits(descriptor, spec) {
1371
- return layoutProblems(descriptor, spec).length === 0;
1372
- }
1373
- var EXPRESSION_KEYS = [
1374
- "$state",
1375
- "$item",
1376
- "$index",
1377
- "$bindState",
1378
- "$bindItem",
1379
- "$cond",
1380
- "$computed",
1381
- "$template"
1382
- ];
1383
- var HATCHES = /* @__PURE__ */ new Set(["MthdsField", "MthdsResult"]);
1384
- function isExpression(value) {
1385
- return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).some((key) => EXPRESSION_KEYS.includes(key));
1386
- }
1387
- function valueAt(value, path) {
1388
- let at = value;
1389
- for (const step of path) {
1390
- if (typeof at !== "object" || at === null) return void 0;
1391
- at = at[step];
1392
- }
1393
- return at;
1394
- }
1395
- function isClosedVocabulary(schema) {
1396
- let current = schema;
1397
- for (let depth = 0; depth < 8; depth += 1) {
1398
- const def = current.def;
1399
- if (!def) return false;
1400
- if (def.type === "enum" || def.type === "literal") return true;
1401
- if (def.innerType === void 0) return false;
1402
- current = def.innerType;
1403
- }
1404
- return false;
1405
- }
1406
- function definitionOf2(catalog2, type) {
1407
- const components3 = catalog2.data.components;
1408
- return hasOwnProp(components3, type) ? components3[type] : void 0;
1409
- }
1410
- function propsSchemaOf(catalog2, type) {
1411
- const props = definitionOf2(catalog2, type)?.props;
1412
- return props && typeof props === "object" && "shape" in props ? props : void 0;
1413
- }
1414
- function eventsOf(catalog2, type) {
1415
- const definition = definitionOf2(catalog2, type);
1416
- if (!definition) return void 0;
1417
- return Array.isArray(definition.events) ? definition.events.filter((event) => typeof event === "string") : [];
1418
- }
1419
- var MAX_ELEMENT_DEPTH = 512;
1420
- function childKeysOf(spec, key) {
1421
- const element = spec.elements[key];
1422
- if (!element) return [];
1423
- const children = Array.isArray(element.children) ? element.children : [];
1424
- const slots = typeof element.slots === "object" && element.slots !== null ? Object.values(element.slots).flatMap(
1425
- (slot) => Array.isArray(slot) ? slot : []
1426
- ) : [];
1427
- return [...children, ...slots].filter((child) => typeof child === "string");
1428
- }
1429
- function deepestChain(spec) {
1430
- const below = /* @__PURE__ */ new Map();
1431
- const onPath = /* @__PURE__ */ new Set();
1432
- let deepest = 0;
1433
- for (const start of Object.keys(spec.elements)) {
1434
- if (below.has(start)) continue;
1435
- const stack = [start];
1436
- while (stack.length > 0) {
1437
- const key = stack[stack.length - 1];
1438
- if (below.has(key)) {
1439
- onPath.delete(key);
1440
- stack.pop();
1441
- continue;
1442
- }
1443
- const children = childKeysOf(spec, key).filter((child) => !onPath.has(child));
1444
- const pending = children.filter((child) => !below.has(child));
1445
- if (pending.length > 0) {
1446
- onPath.add(key);
1447
- stack.push(...pending);
1448
- continue;
1449
- }
1450
- below.set(
1451
- key,
1452
- children.reduce((tallest, child) => Math.max(tallest, below.get(child) ?? 0), 0) + 1
1453
- );
1454
- onPath.delete(key);
1455
- stack.pop();
1456
- }
1457
- deepest = Math.max(deepest, below.get(start) ?? 0);
1458
- }
1459
- return deepest;
1460
- }
1461
- function validateAgainstCatalog(spec, catalog2 = frameCatalog("page")) {
1462
- try {
1463
- return checkAgainstCatalog(spec, catalog2);
1464
- } catch (error) {
1465
- return {
1466
- ok: false,
1467
- problems: [{ message: `the layout is not a well-formed spec: ${String(error)}` }]
1468
- };
1469
- }
1470
- }
1471
- function checkAgainstCatalog(spec, catalog2) {
1472
- const problems = [];
1473
- const depth = deepestChain(spec);
1474
- if (depth > MAX_ELEMENT_DEPTH) {
1475
- return {
1476
- ok: false,
1477
- problems: [
1478
- {
1479
- message: `the element tree is ${depth} deep; this entry renders at most ${MAX_ELEMENT_DEPTH}.`
1480
- }
1481
- ]
1482
- };
1483
- }
1484
- const structure = validateSpec(spec, { checkOrphans: true });
1485
- for (const issue of structure.issues) {
1486
- if (issue.severity === "error" || issue.code === "orphaned_element") {
1487
- problems.push({ elementKey: issue.elementKey, message: issue.message });
1488
- }
1489
- }
1490
- const cyclic = /* @__PURE__ */ new Set();
1491
- const state = /* @__PURE__ */ new Map();
1492
- const descend2 = (key) => {
1493
- const mark = state.get(key);
1494
- if (mark === "done") return;
1495
- if (mark === "open") {
1496
- cyclic.add(key);
1497
- return;
1498
- }
1499
- state.set(key, "open");
1500
- const element = spec.elements[key];
1501
- for (const child of element?.children ?? []) descend2(child);
1502
- for (const slot of Object.values(element?.slots ?? {}))
1503
- for (const child of slot) descend2(child);
1504
- state.set(key, "done");
1505
- };
1506
- for (const key of Object.keys(spec.elements)) descend2(key);
1507
- for (const key of cyclic) {
1508
- problems.push({
1509
- elementKey: key,
1510
- message: `"${key}" is its own descendant: an element tree may not contain a cycle.`
1511
- });
1512
- }
1513
- const known = new Set(catalog2.componentNames);
1514
- for (const [key, element] of Object.entries(spec.elements)) {
1515
- if (!known.has(element.type)) {
1516
- problems.push({ elementKey: key, message: `unknown component type "${element.type}"` });
1517
- }
1518
- }
1519
- for (const [key, element] of Object.entries(spec.elements)) {
1520
- const schema = propsSchemaOf(catalog2, element.type);
1521
- if (!schema) continue;
1522
- const shape = schema.shape;
1523
- const props = element.props ?? {};
1524
- for (const [name, value] of Object.entries(props)) {
1525
- const propSchema = shape[name];
1526
- if (!propSchema) {
1527
- problems.push({
1528
- elementKey: key,
1529
- message: `${element.type} has no prop "${name}" (known: ${Object.keys(shape).join(", ")})`
1530
- });
1531
- continue;
1532
- }
1533
- if (isExpression(value)) {
1534
- if (HATCHES.has(element.type) && name === "path") {
1535
- problems.push({
1536
- elementKey: key,
1537
- message: `${element.type}.path must be a literal string - absolute, or the item's field name inside a repeat - never an expression.`
1538
- });
1539
- } else if (isClosedVocabulary(propSchema)) {
1540
- problems.push({
1541
- elementKey: key,
1542
- message: `${element.type}.${name} is one of a fixed set of names, so it must be written as a literal, never as an expression.`
1543
- });
1544
- }
1545
- continue;
1546
- }
1547
- const parsed = propSchema.safeParse(value);
1548
- const issues = parsed.success ? [] : parsed.error.issues.filter(
1549
- (issue) => !issue.path.some((_, n) => isExpression(valueAt(value, issue.path.slice(0, n + 1))))
1550
- );
1551
- if (issues.length > 0) {
1552
- problems.push({
1553
- elementKey: key,
1554
- message: `${element.type}.${name}: ${issues.map((issue) => issue.message).join("; ")}`
1555
- });
1556
- }
1557
- }
1558
- for (const [name, propSchema] of Object.entries(shape)) {
1559
- if (name in props) continue;
1560
- if (propSchema.safeParse(void 0).success || propSchema.safeParse(null).success) continue;
1561
- problems.push({
1562
- elementKey: key,
1563
- message: `${element.type} is missing required prop "${name}"`
1564
- });
1565
- }
1566
- }
1567
- const renders = catalog2.renders ?? {};
1568
- const renderingOf = (type) => hasOwnProp(renders, type) ? renders[type] : void 0;
1569
- for (const [key, element] of Object.entries(spec.elements)) {
1570
- const panelled = renderingOf(element.type)?.panels;
1571
- if (!panelled) continue;
1572
- const props = element.props ?? {};
1573
- const listed = props[panelled.prop];
1574
- if (!Array.isArray(listed)) continue;
1575
- const panels = listed.length;
1576
- const children = element.children?.length ?? 0;
1577
- if (children !== panels) {
1578
- problems.push({
1579
- elementKey: key,
1580
- message: `${element.type} declares ${panels} panel${panels === 1 ? "" : "s"} but has ${children} child${children === 1 ? "" : "ren"}: exactly one child per ${panelled.noun}, in order.`
1581
- });
1582
- }
1583
- const runs = (element.children ?? []).filter(
1584
- (child) => spec.elements[child]?.type === "Button"
1585
- );
1586
- if (runs.length > 0) {
1587
- problems.push({
1588
- elementKey: key,
1589
- message: `${element.type} has a Button as a direct child; a panel is a container (a Stack), and the Button belongs inside the last one.`
1590
- });
1591
- }
1592
- }
1593
- const present3 = (value) => value !== void 0 && value !== null && value !== "";
1594
- const levels = [];
1595
- const onlyOnce = /* @__PURE__ */ new Map();
1596
- const seen = /* @__PURE__ */ new Set();
1597
- const walk = (key) => {
1598
- if (seen.has(key)) return;
1599
- seen.add(key);
1600
- const element = spec.elements[key];
1601
- if (!element) return;
1602
- const props = element.props ?? {};
1603
- const rendering = renderingOf(element.type);
1604
- if (element.type === "Heading") {
1605
- const level = String(props.level ?? "h2");
1606
- const match = /^h([1-4])$/.exec(level);
1607
- if (match) levels.push({ key, type: "Heading", level: Number(match[1]) });
1608
- } else if (rendering?.heading) {
1609
- const rendered = rendering.heading;
1610
- const has = rendered.when === void 0 || present3(props[rendered.when]);
1611
- if (has) levels.push({ key, type: element.type, level: rendered.level });
1612
- }
1613
- if (rendering?.once !== void 0) {
1614
- const entry = onlyOnce.get(element.type) ?? { reason: rendering.once, keys: [] };
1615
- entry.keys.push(key);
1616
- onlyOnce.set(element.type, entry);
1617
- }
1618
- for (const child of element.children ?? []) walk(child);
1619
- for (const slot of Object.values(element.slots ?? {})) for (const child of slot) walk(child);
1620
- };
1621
- if (spec.root) walk(spec.root);
1622
- const describe2 = (heading) => heading.type === "Heading" ? `h${heading.level}` : `${heading.type}'s h${heading.level}`;
1623
- let previous;
1624
- for (const heading of levels) {
1625
- if (previous !== void 0 && heading.level > previous.level + 1) {
1626
- problems.push({
1627
- elementKey: heading.key,
1628
- message: `${describe2(heading)} jumps after ${describe2(previous)}: heading levels increase by one, never by more.`
1629
- });
1630
- }
1631
- previous = heading;
1632
- }
1633
- const firsts = levels.filter((heading) => heading.level === 1);
1634
- const h1Ways = [
1635
- ...known.has("Heading") ? ["as a Heading at level h1"] : [],
1636
- ...Object.entries(renders).filter(([, rendering]) => rendering.heading?.level === 1).map(([type]) => `as the heading a ${type} renders`)
1637
- ];
1638
- if (firsts.length === 0 && spec.root && h1Ways.length > 0) {
1639
- problems.push({
1640
- elementKey: spec.root,
1641
- message: `the page has no h1: exactly one, ${h1Ways.join(" or ")}.`
1642
- });
1643
- }
1644
- for (const extra of firsts.slice(1)) {
1645
- problems.push({
1646
- elementKey: extra.key,
1647
- message: `a second h1 (${describe2(extra)}, after ${describe2(firsts[0])} at [${firsts[0].key}]): exactly one on a page.`
1648
- });
1649
- }
1650
- for (const [type, { reason, keys }] of onlyOnce) {
1651
- for (const extra of keys.slice(1)) {
1652
- problems.push({
1653
- elementKey: extra,
1654
- message: `a second ${type} (after [${keys[0]}]): at most one on a page, since it renders as ${reason}.`
1655
- });
1656
- }
1657
- }
1658
- for (const [key, element] of Object.entries(spec.elements)) {
1659
- const rule = renderingOf(element.type)?.children;
1660
- if (!rule) continue;
1661
- const children = element.children?.length ?? 0;
1662
- if (children !== rule.count) {
1663
- problems.push({
1664
- elementKey: key,
1665
- message: `${element.type} takes exactly ${rule.count} children (${rule.roles}); this one has ${children}.`
1666
- });
1667
- }
1668
- }
1669
- const declared = [
1670
- ...catalog2.actionNames ?? [],
1671
- ...(catalog2.schema?.builtInActions ?? []).map((action) => action.name)
1672
- ];
1673
- const knownActions = declared.length > 0 ? new Set(declared) : void 0;
1674
- const STATE_WRITES = {
1675
- setState: [{ param: "statePath" }],
1676
- pushState: [{ param: "statePath" }, { param: "clearStatePath", optional: true }],
1677
- removeState: [{ param: "statePath" }],
1678
- validateForm: [{ param: "statePath", optional: true }]
1679
- };
1680
- const OWNED_ROOTS = [INPUTS_ROOT, RESULT_ROOT];
1681
- const ownedRootOf = (written) => {
1682
- const statePath = written.startsWith("/") ? written : `/${written}`;
1683
- return OWNED_ROOTS.find((root) => statePath === root || statePath.startsWith(`${root}/`));
1684
- };
1685
- const bindingsOf = (binding) => Array.isArray(binding) ? binding : [binding];
1686
- const refusal = (owned, statePath) => `a layout may not write into ${owned}, which the host fills. Bind the value with { "$bindState": "${statePath}" } or delegate it with MthdsField instead.`;
1687
- const checkBinding = (key, site, entry) => {
1688
- const action = entry?.action;
1689
- if (typeof action !== "string") {
1690
- problems.push({ elementKey: key, message: `${site} has an entry with no "action" name.` });
1691
- return;
1692
- }
1693
- if (knownActions && !knownActions.has(action)) {
1694
- problems.push({
1695
- elementKey: key,
1696
- message: `${site} names the unknown action "${action}" (known: ${[...knownActions].join(", ")}); nothing would handle it.`
1697
- });
1698
- return;
1699
- }
1700
- const writes = hasOwnProp(STATE_WRITES, action) ? STATE_WRITES[action] : void 0;
1701
- if (writes) {
1702
- const params = entry.params;
1703
- for (const { param, optional } of writes) {
1704
- const written = typeof params === "object" && params !== null ? params[param] : void 0;
1705
- if (written === void 0 || written === null) {
1706
- if (!optional) {
1707
- problems.push({
1708
- elementKey: key,
1709
- message: `${site} calls ${action} without a literal "${param}".`
1710
- });
1711
- }
1712
- continue;
1713
- }
1714
- if (typeof written !== "string") {
1715
- problems.push({
1716
- elementKey: key,
1717
- message: `${site} calls ${action} with a "${param}" that is not a literal string; where an action writes must be named, never computed.`
1718
- });
1719
- continue;
1720
- }
1721
- const owned = ownedRootOf(written);
1722
- if (owned) {
1723
- const where = param === "statePath" ? `on "${written}"` : `with ${param} "${written}"`;
1724
- problems.push({
1725
- elementKey: key,
1726
- message: `${site} calls ${action} ${where}: ${refusal(owned, written.startsWith("/") ? written : `/${written}`)}`
1727
- });
1728
- }
1729
- }
1730
- }
1731
- for (const hook of ["onSuccess", "onError"]) {
1732
- const follow = entry[hook];
1733
- if (follow === void 0 || follow === null) continue;
1734
- const hookSite = `${site}.${hook}`;
1735
- if (typeof follow !== "object" || Array.isArray(follow)) {
1736
- problems.push({
1737
- elementKey: key,
1738
- message: `${hookSite} must be an object: a { "set" } of state paths to values, or an { "action" } binding.`
1739
- });
1740
- continue;
1741
- }
1742
- const record = follow;
1743
- if (record.set !== void 0 && record.set !== null) {
1744
- if (typeof record.set !== "object" || Array.isArray(record.set)) {
1745
- problems.push({
1746
- elementKey: key,
1747
- message: `${hookSite}.set must be an object mapping a state path to its value.`
1748
- });
1749
- } else {
1750
- for (const written of Object.keys(record.set)) {
1751
- const owned = ownedRootOf(written);
1752
- if (owned) {
1753
- problems.push({
1754
- elementKey: key,
1755
- message: `${hookSite} sets "${written}": ${refusal(owned, written.startsWith("/") ? written : `/${written}`)}`
1756
- });
1757
- }
1758
- }
1759
- }
1760
- }
1761
- if (record.action !== void 0) checkBinding(key, hookSite, record);
1762
- }
1763
- };
1764
- for (const [key, element] of Object.entries(spec.elements)) {
1765
- const on = element.on;
1766
- if (on !== void 0 && on !== null) {
1767
- if (typeof on !== "object" || Array.isArray(on)) {
1768
- problems.push({
1769
- elementKey: key,
1770
- message: '"on" must be an object mapping an event name to its actions.'
1771
- });
1772
- } else {
1773
- const emitted = eventsOf(catalog2, element.type);
1774
- for (const [event, binding] of Object.entries(on)) {
1775
- if (emitted && !emitted.includes(event)) {
1776
- problems.push({
1777
- elementKey: key,
1778
- message: emitted.length > 0 ? `on.${event}: ${element.type} never emits "${event}" (it emits: ${emitted.join(", ")}), so nothing bound there would ever fire.` : `on.${event}: ${element.type} emits no events, so nothing bound there would ever fire.`
1779
- });
1780
- }
1781
- for (const entry of bindingsOf(binding)) checkBinding(key, `on.${event}`, entry);
1782
- }
1783
- }
1784
- }
1785
- const watch = element.watch;
1786
- if (watch !== void 0 && watch !== null) {
1787
- if (typeof watch !== "object" || Array.isArray(watch)) {
1788
- problems.push({
1789
- elementKey: key,
1790
- message: '"watch" must be an object mapping a state path to the actions its change fires.'
1791
- });
1792
- } else {
1793
- for (const [path, binding] of Object.entries(watch)) {
1794
- for (const entry of bindingsOf(binding)) checkBinding(key, `watch.${path}`, entry);
1795
- }
1796
- }
1797
- }
1798
- }
1799
- return { ok: problems.length === 0, problems };
1800
- }
1801
- function formatProblems(problems) {
1802
- return problems.map(
1803
- (problem) => problem.elementKey ? `[${problem.elementKey}] ${problem.message}` : problem.message
1804
- ).join("\n");
1805
- }
1806
-
1807
- // src/generative/designer-run.ts
1808
- function seedLine(seed) {
1809
- return `CREATIVE SEED (derive your direction from it; never reveal it): ${seed}`;
1810
- }
1811
- var REPAIR_ATTEMPTS = 2;
1812
- function layoutCheckProblems(spec, { frame, descriptor }) {
1813
- const verdict = validateAgainstCatalog(spec, frameCatalog(frame));
1814
- const invalid = verdict.ok ? [] : formatProblems(verdict.problems).split("\n");
1815
- const runless = frame !== "result" && descriptor.inputs && !bindsRun(spec) ? ["the layout has no control that runs the method: bind `run` under a control's `on`"] : [];
1816
- return [...invalid, ...runless, ...layoutProblems(descriptor, spec)];
1817
- }
1818
- function bindsRun(spec) {
1819
- const isRun = (binding) => typeof binding === "object" && binding !== null && binding.action === "run";
1820
- const elements = spec.elements;
1821
- if (typeof elements !== "object" || elements === null) return false;
1822
- return Object.values(elements).some((element) => {
1823
- if (typeof element !== "object" || element === null) return false;
1824
- const on = element.on;
1825
- if (typeof on !== "object" || on === null) return false;
1826
- return Object.values(on).some(
1827
- (binding) => Array.isArray(binding) ? binding.some(isRun) : isRun(binding)
1828
- );
1829
- });
1830
- }
26
+ import { resolveRepeatStatePath, getByPath, evaluateVisibility, resolveElementProps } from '@json-render/core';
27
+ export { createStateStore } from '@json-render/core';
28
+ import { Clock, ArrowRight, Send, Pencil, Zap, Lightbulb, Truck, Package, Briefcase, Bookmark, Flag, Shield, Info, AlertCircle, CheckCircle2, ListChecks, Layers, Tag, Phone, Mail, Paperclip, Upload, FileText, BarChart3, TrendingUp, Hash, Percent, Receipt, Coins, Banknote, Wallet, Gift, Ticket, Image, Camera, Music, Coffee, Utensils, Star, Heart, Accessibility, Baby, User, Users, Hourglass, Calendar, Moon, Sun, Leaf, Mountain, Globe, Landmark, Building2, Home, Hotel, Ship, Car, Train, Plane, Route, MapPin, Map as Map$1, Compass, Rocket, Sparkles, ChevronDown } from 'lucide-react';
29
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
1831
30
 
1832
- // src/generative/brief.ts
1833
- var DESIGNER_BRIEF_CONCEPT = "generative.Brief";
1834
- var RUN_COMPONENT = "Cta";
1835
- var COMPOSER_RUN_COMPONENT = "Composer";
1836
- function isDelegatedInput(field) {
1837
- return field.kind === "document" || field.kind === "image" || field.kind === "date" || field.kind === "list" || field.kind === "unknown" || field.kind === "enum" && field.options.includes("");
1838
- }
1839
- function isDelegatedResult(field, frame = "page") {
1840
- if (isNativeValueNode(field)) return true;
1841
- if (frame === "result" && (field.kind === "image" || field.kind === "date" && !field.datetime))
1842
- return false;
1843
- if (field.kind === "list" && isNativeValueNode(field.item)) return true;
1844
- return field.kind === "document" || field.kind === "image" || field.kind === "date" || field.kind === "prose" || field.kind === "unknown";
1845
- }
1846
- function kindLabel(field) {
1847
- if (isNativeHtmlNode(field)) return "markup (native.Html)";
1848
- if (isNativeDateNode(field)) return "date (native.Date)";
1849
- if (isNativeCompositeNode(field)) return "composite (native.Composite)";
1850
- switch (field.kind) {
1851
- case "text":
1852
- return "text";
1853
- case "prose":
1854
- return "prose (markdown)";
1855
- case "number":
1856
- return field.integer ? "integer" : "number";
1857
- case "boolean":
1858
- return "boolean";
1859
- case "enum":
1860
- return `choice of ${field.options.map((option) => JSON.stringify(option)).join(" | ")}`;
1861
- case "date":
1862
- return field.datetime ? "date with time" : "date";
1863
- case "document":
1864
- return "document (a file)";
1865
- case "image":
1866
- return "image (a file)";
1867
- case "object":
1868
- return `structure${field.conceptRef ? ` ${field.conceptRef}` : ""}`;
1869
- case "list":
1870
- return `list of ${kindLabel(field.item)}`;
1871
- case "unknown":
1872
- return "unknown";
1873
- default:
1874
- return field;
1875
- }
1876
- }
1877
- function constraints(field) {
1878
- const notes = [];
1879
- if (field.kind === "number") {
1880
- if (field.min !== void 0) notes.push(`min ${field.min}`);
1881
- if (field.max !== void 0) notes.push(`max ${field.max}`);
1882
- }
1883
- if (field.kind === "text" || field.kind === "prose") {
1884
- if (field.minLength !== void 0) notes.push(`at least ${field.minLength} characters`);
1885
- if (field.maxLength !== void 0) notes.push(`at most ${field.maxLength} characters`);
1886
- if (field.format) notes.push(`format ${field.format}`);
1887
- }
1888
- if (field.kind === "list") {
1889
- if (field.itemCount !== void 0) notes.push(`at least ${field.itemCount} items`);
1890
- if (field.maxItemCount !== void 0) notes.push(`at most ${field.maxItemCount} items`);
1891
- }
1892
- if (field.defaultValue !== void 0) notes.push(`default ${JSON.stringify(field.defaultValue)}`);
1893
- if (field.examples && field.examples.length > 0) {
1894
- notes.push(`examples ${field.examples.map((example) => JSON.stringify(example)).join(", ")}`);
1895
- }
1896
- if (field.hints && Object.keys(field.hints).length > 0) {
1897
- notes.push(
1898
- `hints ${Object.entries(field.hints).map(([key, value]) => `${key}=${value}`).join(", ")}`
1899
- );
1900
- }
1901
- return notes;
1902
- }
1903
- function entryOf(field, path, depth, side, relative, seeded, frame = "page") {
1904
- const notes = constraints(field);
1905
- return {
1906
- depth,
1907
- path,
1908
- kind: kindLabel(field),
1909
- name: field.name,
1910
- title: field.title && field.title !== field.name ? field.title : void 0,
1911
- description: field.description,
1912
- required: side === "input" ? field.required : void 0,
1913
- gating: side === "input" && field.gating ? true : void 0,
1914
- notes: notes.length > 0 ? notes : void 0,
1915
- default: seeded || field.defaultValue === void 0 ? void 0 : JSON.stringify(field.defaultValue),
1916
- delegated: side === "input" ? isDelegatedInput(field) : isDelegatedResult(field, frame),
1917
- relative: relative ? true : void 0
1918
- };
1919
- }
1920
- function describe(field, path, depth, side, entries, seeded = false, frame = "page") {
1921
- const entry = entryOf(field, path, depth, side, false, seeded, frame);
1922
- entries.push(entry);
1923
- if (entry.delegated) return;
1924
- if (field.kind === "object") {
1925
- const seededBelow = seeded || field.defaultValue !== void 0 || side === "input" && !field.required;
1926
- for (const child of field.fields) {
1927
- describe(child, joinPath(path, child.name), depth + 1, side, entries, seededBelow, frame);
1928
- }
1929
- }
1930
- if (field.kind === "list" && side === "result") {
1931
- const item = field.item;
1932
- entry.item_kind = kindLabel(item);
1933
- entry.item_description = item.description;
1934
- if (item.kind === "object" && !isDelegatedResult(item, frame)) {
1935
- entry.item_laid_out = true;
1936
- for (const child of item.fields) {
1937
- describeRelative(child, child.name, depth + 2, entries, side, frame);
1938
- }
1939
- }
1940
- }
1941
- }
1942
- function describeRelative(field, relativePath, depth, entries, side = "result", frame = "page") {
1943
- const entry = entryOf(field, relativePath, depth, side, true, false, frame);
1944
- entries.push(entry);
1945
- if (entry.delegated) return;
1946
- if (field.kind === "object") {
1947
- for (const child of field.fields) {
1948
- describeRelative(child, joinPath(relativePath, child.name), depth + 1, entries, side, frame);
1949
- }
1950
- }
1951
- if (field.kind === "list" && side === "result") {
1952
- const item = field.item;
1953
- entry.item_kind = kindLabel(item);
1954
- entry.item_description = item.description;
1955
- if (item.kind === "object" && !isDelegatedResult(item, frame)) {
1956
- entry.item_laid_out = true;
1957
- for (const child of item.fields) {
1958
- describeRelative(
1959
- child,
1960
- joinPath(relativePath, "<i>", child.name),
1961
- depth + 2,
1962
- entries,
1963
- side,
1964
- frame
1965
- );
1966
- }
1967
- }
1968
- }
1969
- }
1970
- function returnsOf(result) {
1971
- const shape = result.kind === "list" ? result.item : result;
1972
- const members = shape.kind === "object" ? shape.fields : [shape];
1973
- return members.map((member) => {
1974
- const title = member.title ?? member.name.replace(/_/g, " ");
1975
- return member.description && member.description !== title ? `${title}: ${member.description}` : title;
1976
- });
1977
- }
1978
- function styleOf(style) {
1979
- if (!style) return void 0;
1980
- const { name, direction } = APP_STYLES[style];
1981
- return { name, direction };
1982
- }
1983
- function inputBrief(subject, fields, { frame = "page", style, result } = {}) {
1984
- const paths = [];
1985
- for (const field of fields) {
1986
- describe(field, joinPath(INPUTS_ROOT, field.name), 0, "input", paths, false, frame);
1987
- }
1988
- return {
1989
- side: "input",
1990
- frame,
1991
- pipe_ref: subject.pipeRef,
1992
- description: subject.description,
1993
- name: subject.name,
1994
- paths,
1995
- run_control: frame === "composer" ? COMPOSER_RUN_COMPONENT : RUN_COMPONENT,
1996
- ...frame === "composer" && result ? { returns: returnsOf(result) } : {},
1997
- ...style ? { style: styleOf(style) } : {}
1998
- };
1999
- }
2000
- function resultBrief(subject, field, state, { frame = "page", style } = {}) {
2001
- const paths = [];
2002
- if (field.kind === "object" && !isDelegatedResult(field, frame)) {
2003
- paths.push({
2004
- depth: 0,
2005
- path: RESULT_ROOT,
2006
- kind: kindLabel(field),
2007
- description: field.description,
2008
- delegated: false
2009
- });
2010
- for (const child of field.fields) {
2011
- describe(child, joinPath(RESULT_ROOT, child.name), 1, "result", paths, false, frame);
2012
- }
2013
- } else {
2014
- describe(field, RESULT_ROOT, 0, "result", paths, false, frame);
2015
- }
2016
- return {
2017
- side: "result",
2018
- frame,
2019
- pipe_ref: subject.pipeRef,
2020
- description: subject.description,
2021
- paths,
2022
- ...style ? { style: styleOf(style) } : {},
2023
- sample_state: JSON.stringify(state, null, 2)
2024
- };
2025
- }
2026
- function specToJsonl(spec) {
2027
- const lines = [];
2028
- const push = (op, path, value) => lines.push(JSON.stringify({ op, path, value }));
2029
- push("add", "/root", spec.root);
2030
- const emitted = /* @__PURE__ */ new Set();
2031
- const queue = [spec.root];
2032
- while (queue.length > 0) {
2033
- const key = queue.shift();
2034
- if (emitted.has(key)) continue;
2035
- const element = spec.elements[key];
2036
- if (!element) continue;
2037
- emitted.add(key);
2038
- push("add", `/elements/${escapeSegment(key)}`, element);
2039
- for (const child of element.children ?? []) queue.push(child);
2040
- for (const slotChildren of Object.values(element.slots ?? {})) queue.push(...slotChildren);
2041
- }
2042
- for (const key of Object.keys(spec.elements)) {
2043
- if (!emitted.has(key)) push("add", `/elements/${escapeSegment(key)}`, spec.elements[key]);
2044
- }
2045
- for (const [key, value] of Object.entries(spec.state ?? {})) {
2046
- push("add", `/state/${escapeSegment(key)}`, value);
2047
- }
2048
- return `${lines.join("\n")}
2049
- `;
2050
- }
2051
- var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
2052
- function pointerReachesPrototype(pointer) {
2053
- return typeof pointer === "string" && pointer.split("/").some((segment) => FORBIDDEN_SEGMENTS.has(unescapeSegment(segment)));
2054
- }
2055
- function reachesPrototype(line) {
2056
- let parsed;
2057
- try {
2058
- parsed = JSON.parse(line);
2059
- } catch {
2060
- return false;
2061
- }
2062
- const patch = parsed;
2063
- return pointerReachesPrototype(patch?.path) || pointerReachesPrototype(patch?.from);
2064
- }
2065
- function specFromJsonl(jsonl) {
2066
- let spec = { root: "", elements: {} };
2067
- for (const line of jsonl.split("\n")) {
2068
- if (reachesPrototype(line)) continue;
2069
- try {
2070
- spec = compileSpecStream(line, spec);
2071
- } catch {
2072
- continue;
2073
- }
2074
- }
2075
- return spec;
2076
- }
2077
- function jsonlLines(jsonl) {
2078
- return jsonl.split("\n").filter((line) => line.trim().length > 0);
2079
- }
2080
- var BrandContext = React4.createContext(null);
31
+ var BrandContext = React6.createContext(null);
2081
32
  function BrandProvider({
2082
33
  manifest,
2083
34
  children
@@ -2086,7 +37,7 @@ function BrandProvider({
2086
37
  return /* @__PURE__ */ jsx(BrandContext.Provider, { value: manifest, children });
2087
38
  }
2088
39
  function useBrand() {
2089
- const manifest = React4.useContext(BrandContext);
40
+ const manifest = React6.useContext(BrandContext);
2090
41
  if (!manifest)
2091
42
  throw new Error(
2092
43
  "useBrand: no brand in scope. Pass `brand` to GenerativePage, or wrap the tree in BrandProvider."
@@ -2097,7 +48,7 @@ var WEIGHTS = "400;500;600;700";
2097
48
  function useWebfont(webfont) {
2098
49
  const provider = webfont?.provider;
2099
50
  const family = webfont?.family;
2100
- React4.useEffect(() => {
51
+ React6.useEffect(() => {
2101
52
  if (!provider || !family) return;
2102
53
  const key = `${provider}:${family}`;
2103
54
  if (document.head.querySelector(`link[data-brand-webfont="${CSS.escape(key)}"]`)) return;
@@ -2117,7 +68,422 @@ function useWebfont(webfont) {
2117
68
  document.head.appendChild(link);
2118
69
  }, [provider, family]);
2119
70
  }
2120
- var FrameHostContext = React4.createContext({});
71
+ var CheckedFields = class {
72
+ constructor() {
73
+ // One entry per mounted field, so two fields handed one config object are
74
+ // two entries, and one leaving does not take the other's checks with it.
75
+ this.byPath = /* @__PURE__ */ new Map();
76
+ this.validation = null;
77
+ }
78
+ /** Hands the record json-render's validation, and tells it what is already mounted. */
79
+ attach(validation) {
80
+ if (validation === this.validation) return;
81
+ const first = this.validation === null;
82
+ this.validation = validation;
83
+ if (first) for (const path of this.byPath.keys()) this.publish(path);
84
+ }
85
+ /** Enters one mounted field's checks; what it returns leaves them again. */
86
+ enter(path, config) {
87
+ const configs = this.byPath.get(path) ?? /* @__PURE__ */ new Set();
88
+ this.byPath.set(path, configs);
89
+ const entry = { config };
90
+ configs.add(entry);
91
+ this.publish(path);
92
+ return () => {
93
+ configs.delete(entry);
94
+ if (configs.size === 0) this.byPath.delete(path);
95
+ this.publish(path);
96
+ };
97
+ }
98
+ /** The checks of every field mounted at `path`, combined, or none when no field is. */
99
+ configFor(path) {
100
+ const configs = this.byPath.get(path);
101
+ if (!configs || configs.size === 0) return void 0;
102
+ const [first] = configs;
103
+ return {
104
+ checks: [...configs].flatMap(({ config }) => config.checks ?? []),
105
+ ...first?.config.validateOn ? { validateOn: first.config.validateOn } : {}
106
+ };
107
+ }
108
+ /**
109
+ * Runs the checks of every path a mounted field carries, as the record
110
+ * holds them now, and answers whether they all pass; every path is checked,
111
+ * so each failing field shows its message. json-render's own `validateAll`
112
+ * reads what it was told as of its provider's last render, which a press
113
+ * can beat by one task when a field has just mounted.
114
+ */
115
+ check(validation) {
116
+ let pass = true;
117
+ for (const path of this.byPath.keys()) {
118
+ const combined = this.configFor(path);
119
+ if (combined && !validation.validate(path, combined).valid) pass = false;
120
+ }
121
+ return pass;
122
+ }
123
+ publish(path) {
124
+ this.validation?.registerField(path, this.configFor(path) ?? { checks: [] });
125
+ }
126
+ };
127
+ var CheckedFieldsContext = React6.createContext(null);
128
+ function useCheckedField(path, config) {
129
+ const fields = React6.useContext(CheckedFieldsContext);
130
+ const field = useFieldValidation(path, fields ? void 0 : config);
131
+ const validation = useOptionalValidation();
132
+ const signature = config ? JSON.stringify(config) : "";
133
+ const latest = React6.useRef(config);
134
+ React6.useInsertionEffect(() => {
135
+ latest.current = config;
136
+ });
137
+ React6.useEffect(() => {
138
+ const entered = latest.current;
139
+ if (!fields || !path || !entered) return void 0;
140
+ return fields.enter(path, entered);
141
+ }, [fields, path, signature]);
142
+ const fallback = field.validate;
143
+ const validate = React6.useCallback(() => {
144
+ const combined = fields?.configFor(path);
145
+ return validation && combined ? validation.validate(path, combined) : fallback();
146
+ }, [fields, path, validation, fallback]);
147
+ return { ...field, validate };
148
+ }
149
+ var ReportingContext = React6.createContext(false);
150
+ function said(error) {
151
+ try {
152
+ return String(error);
153
+ } catch {
154
+ return "an error that cannot be written out";
155
+ }
156
+ }
157
+ function contentOf(value) {
158
+ const open = /* @__PURE__ */ new WeakSet();
159
+ try {
160
+ return JSON.stringify(value, function(_key, entry) {
161
+ if (typeof entry === "bigint") return `${entry}n`;
162
+ if (typeof entry === "object" && entry !== null) {
163
+ if (open.has(entry)) return "[cycle]";
164
+ open.add(entry);
165
+ }
166
+ return entry;
167
+ }) ?? "undefined";
168
+ } catch {
169
+ return "an input that cannot be written out";
170
+ }
171
+ }
172
+ function sameCue(a, b) {
173
+ if (Array.isArray(a) && Array.isArray(b)) {
174
+ return a.length === b.length && a.every((entry, index) => Object.is(entry, b[index]));
175
+ }
176
+ return Object.is(a, b);
177
+ }
178
+ var ElementBoundary = class extends React6.Component {
179
+ constructor(props) {
180
+ super(props);
181
+ this.state = { failed: false, error: void 0 };
182
+ }
183
+ static getDerivedStateFromError(error) {
184
+ return { failed: true, error };
185
+ }
186
+ /** What the element renders from, as text; total, since it reads a model's layout and the store. */
187
+ input() {
188
+ try {
189
+ return contentOf(this.props.inputOf ? this.props.inputOf() : this.props.resetOn);
190
+ } catch (error) {
191
+ return `the input could not be read: ${said(error)}`;
192
+ }
193
+ }
194
+ componentDidCatch(error, info) {
195
+ if (this.props.loading) {
196
+ this.failedOn = void 0;
197
+ return;
198
+ }
199
+ this.failedOn = this.input();
200
+ const componentStack = info.componentStack ?? void 0;
201
+ if (this.props.reporting) {
202
+ this.props.report.current?.(error, {
203
+ type: this.props.type,
204
+ ...componentStack ? { componentStack } : {}
205
+ });
206
+ return;
207
+ }
208
+ if (!this.props.root) {
209
+ console.error(
210
+ `[json-render] Rendering error in <${this.props.type}>:`,
211
+ error,
212
+ componentStack
213
+ );
214
+ }
215
+ }
216
+ componentDidUpdate(previous, previousState) {
217
+ if (!this.state.failed || !previousState.failed) return;
218
+ const streamEnded = previous.loading === true && !this.props.loading;
219
+ if (streamEnded || this.inputChanged(previous)) {
220
+ this.failedOn = void 0;
221
+ this.setState({ failed: false, error: void 0 });
222
+ }
223
+ }
224
+ /** Whether what the element renders from is no longer what it failed on. */
225
+ inputChanged(previous) {
226
+ if (sameCue(this.props.resetOn, previous.resetOn)) return false;
227
+ if (this.props.loading || this.failedOn === void 0) return true;
228
+ return !Object.is(this.input(), this.failedOn);
229
+ }
230
+ // Whether the root throws the error on is decided as it renders its
231
+ // failure, from whether the host passes a handler in this very render:
232
+ // decided later, it cost a blank commit and a second record of the same
233
+ // error, and decided from the ref, it read the previous render's callback.
234
+ // No host to tell: the root's error goes on, in the same render, to the
235
+ // host's boundary around the page, which records it once.
236
+ render() {
237
+ if (!this.state.failed) return this.props.children;
238
+ if (this.props.root && !this.props.reporting && !this.props.loading) throw this.state.error;
239
+ return null;
240
+ }
241
+ };
242
+ function resolvedOf(element, ctx) {
243
+ if (!element) return void 0;
244
+ try {
245
+ return {
246
+ props: resolveElementProps(element.props ?? {}, ctx),
247
+ visible: element.visible === void 0 ? true : evaluateVisibility(element.visible, ctx)
248
+ };
249
+ } catch (error) {
250
+ return `throws: ${said(error)}`;
251
+ }
252
+ }
253
+ function childElementOf(node) {
254
+ if (!React6.isValidElement(node)) return void 0;
255
+ const { element, elementKey } = node.props;
256
+ if (typeof elementKey !== "string") return void 0;
257
+ return typeof element === "object" && element !== null ? element : void 0;
258
+ }
259
+ function inputOfElement(props, visibility, scope) {
260
+ const element = props.element;
261
+ const state = visibility.stateModel;
262
+ const ctx = {
263
+ ...visibility,
264
+ ...scope ? { repeatItem: scope.item, repeatIndex: scope.index, repeatBasePath: scope.basePath } : {}
265
+ };
266
+ const statePath = element?.repeat?.statePath;
267
+ let list;
268
+ if (statePath !== void 0) {
269
+ try {
270
+ const path = resolveRepeatStatePath(statePath, scope?.basePath);
271
+ list = path === void 0 ? void 0 : getByPath(state, path);
272
+ } catch (error) {
273
+ list = `throws: ${said(error)}`;
274
+ }
275
+ }
276
+ const under = (nodes) => React6.Children.toArray(nodes).map((node) => {
277
+ const child = childElementOf(node);
278
+ return child ? { element: child, resolved: resolvedOf(child, ctx) } : null;
279
+ });
280
+ const slots = typeof props.slots === "object" && props.slots !== null ? Object.fromEntries(Object.entries(props.slots).map(([name, nodes]) => [name, under(nodes)])) : void 0;
281
+ return { props: element?.props, list, children: under(props.children), slots };
282
+ }
283
+ function inputOfRoot(spec, visibility) {
284
+ const root = spec?.elements?.[spec.root];
285
+ return { spec, root: resolvedOf(root, visibility) };
286
+ }
287
+ function RootBoundary(props) {
288
+ const { ctx } = useVisibility();
289
+ return /* @__PURE__ */ jsx(
290
+ ElementBoundary,
291
+ {
292
+ type: props.type,
293
+ report: props.report,
294
+ reporting: props.reporting,
295
+ loading: props.loading,
296
+ resetOn: [props.spec, ctx],
297
+ inputOf: () => inputOfRoot(props.spec, ctx),
298
+ root: true,
299
+ children: props.children
300
+ }
301
+ );
302
+ }
303
+ function reportingRegistry(registry, report, made) {
304
+ const wrapped = {};
305
+ for (const [type, Renderer2] of Object.entries(registry)) {
306
+ const kept = made.get(type);
307
+ if (kept && kept.renderer === Renderer2) {
308
+ wrapped[type] = kept.wrapper;
309
+ continue;
310
+ }
311
+ const Component2 = Renderer2;
312
+ const Reported = (props) => {
313
+ const reporting = React6.useContext(ReportingContext);
314
+ const { ctx } = useVisibility();
315
+ const scope = useRepeatScope();
316
+ return /* @__PURE__ */ jsx(
317
+ ElementBoundary,
318
+ {
319
+ type,
320
+ report,
321
+ reporting,
322
+ loading: props.loading === true,
323
+ resetOn: [props.element, props.children, props.slots, ctx, scope],
324
+ inputOf: () => inputOfElement(props, ctx, scope),
325
+ children: /* @__PURE__ */ jsx(Component2, { ...props })
326
+ }
327
+ );
328
+ };
329
+ Reported.displayName = `Reported(${type})`;
330
+ made.set(type, { renderer: Renderer2, wrapper: Reported });
331
+ wrapped[type] = Reported;
332
+ }
333
+ return wrapped;
334
+ }
335
+ var CONTAINER = "mx-auto w-full max-w-6xl px-6 sm:px-8";
336
+ function BrandBar({ app, links, tag }) {
337
+ const brand = useBrand();
338
+ return /* @__PURE__ */ jsx("header", { className: "border-b border-border", children: /* @__PURE__ */ jsxs("div", { className: cn(CONTAINER, "flex h-16 items-center gap-5"), children: [
339
+ /* @__PURE__ */ jsx(
340
+ "img",
341
+ {
342
+ src: brand.logo.onLight,
343
+ alt: brand.name,
344
+ referrerPolicy: "no-referrer",
345
+ className: "h-7 w-auto dark:hidden"
346
+ }
347
+ ),
348
+ /* @__PURE__ */ jsx(
349
+ "img",
350
+ {
351
+ src: brand.logo.onDark,
352
+ alt: brand.name,
353
+ referrerPolicy: "no-referrer",
354
+ className: "hidden h-7 w-auto dark:block"
355
+ }
356
+ ),
357
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-5 w-px bg-border" }),
358
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground/80", children: app }),
359
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex min-w-0 items-center gap-6", children: [
360
+ links && links.length > 0 ? /* @__PURE__ */ jsx("nav", { "aria-label": "Site", className: "hidden items-center gap-6 sm:flex", children: links.map((link, index) => /* @__PURE__ */ jsx(
361
+ "a",
362
+ {
363
+ href: "#",
364
+ onClick: (event) => event.preventDefault(),
365
+ className: "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-hidden",
366
+ children: link
367
+ },
368
+ `${index}-${link}`
369
+ )) }) : null,
370
+ tag ? /* @__PURE__ */ jsx(
371
+ "span",
372
+ {
373
+ className: cn(
374
+ "min-w-0 truncate rounded-lg border px-2.5 py-1 font-mono text-[11px]",
375
+ selectedBorderClass,
376
+ selectedFillClass,
377
+ linkTextClass
378
+ ),
379
+ children: tag
380
+ }
381
+ ) : null
382
+ ] })
383
+ ] }) });
384
+ }
385
+
386
+ // src/generative/declared-props.ts
387
+ var factsBySchema = /* @__PURE__ */ new WeakMap();
388
+ function factsOf(schema) {
389
+ const known = factsBySchema.get(schema);
390
+ if (known) return known;
391
+ const chain = definitionChain(schema);
392
+ const def = chain[chain.length - 1];
393
+ const acceptsNull = schema.safeParse(null).success;
394
+ const acceptsUndefined = schema.safeParse(void 0).success;
395
+ const absent = acceptsNull ? null : acceptsUndefined ? void 0 : def?.type === "string" ? "" : def?.type === "array" ? [] : void 0;
396
+ const facts = { def, plain: plainType(chain), acceptsNull, acceptsUndefined, absent };
397
+ factsBySchema.set(schema, facts);
398
+ return facts;
399
+ }
400
+ var PASSING_WRAPPERS = /* @__PURE__ */ new Set([
401
+ "optional",
402
+ "nullable",
403
+ "default",
404
+ "prefault",
405
+ "readonly",
406
+ "catch",
407
+ "nonoptional"
408
+ ]);
409
+ function plainType(chain) {
410
+ const def = chain[chain.length - 1];
411
+ if (!def || def.format !== void 0) return void 0;
412
+ if (chain.some((link) => link.checks !== void 0 && link.checks.length > 0)) return void 0;
413
+ if (!chain.slice(0, -1).every((link) => PASSING_WRAPPERS.has(link.type))) return void 0;
414
+ return def.type === "string" || def.type === "boolean" || def.type === "number" ? def.type : void 0;
415
+ }
416
+ function plainlyConforms(plain, value) {
417
+ if (plain === "number") return typeof value === "number" && Number.isFinite(value);
418
+ return plain !== void 0 && typeof value === plain;
419
+ }
420
+ function writtenOut(schema, value) {
421
+ if (typeof value !== "number" && typeof value !== "boolean") return void 0;
422
+ const text = String(value);
423
+ return schema.safeParse(text).success ? text : void 0;
424
+ }
425
+ function keptEntries(schema, element, entries, absent) {
426
+ const textItems = unwrapped(element)?.type === "string";
427
+ const kept = entries.flatMap((entry) => {
428
+ if (element.safeParse(entry).success) return [entry];
429
+ const text = textItems ? writtenOut(element, entry) : void 0;
430
+ return text === void 0 ? [] : [text];
431
+ });
432
+ return schema.safeParse(kept).success ? kept : absent;
433
+ }
434
+ var conformedBySchema = /* @__PURE__ */ new WeakMap();
435
+ function conformValue(schema, value) {
436
+ const facts = factsOf(schema);
437
+ if (value === void 0) return facts.acceptsUndefined ? value : facts.absent;
438
+ if (value === null) return facts.acceptsNull ? value : facts.absent;
439
+ if (typeof value !== "object") {
440
+ if (plainlyConforms(facts.plain, value) || schema.safeParse(value).success) return value;
441
+ if (facts.def?.type === "string") return writtenOut(schema, value) ?? facts.absent;
442
+ return facts.absent;
443
+ }
444
+ let byValue = conformedBySchema.get(schema);
445
+ if (!byValue) {
446
+ byValue = /* @__PURE__ */ new WeakMap();
447
+ conformedBySchema.set(schema, byValue);
448
+ }
449
+ if (byValue.has(value)) return byValue.get(value);
450
+ const held = schema.safeParse(value).success ? value : facts.def?.type === "array" && facts.def.element && Array.isArray(value) ? keptEntries(schema, facts.def.element, value, facts.absent) : facts.absent;
451
+ byValue.set(value, held);
452
+ return held;
453
+ }
454
+ function conformProps(shape, props, asWritten = []) {
455
+ let conformed;
456
+ for (const [name, schema] of Object.entries(shape)) {
457
+ if (asWritten.includes(name)) continue;
458
+ const value = props[name];
459
+ const held = conformValue(schema, value);
460
+ if (held === value) continue;
461
+ conformed ?? (conformed = { ...props });
462
+ conformed[name] = held;
463
+ }
464
+ return conformed ?? props;
465
+ }
466
+ function withDeclaredProps(definitions, renderers, asWritten = {}) {
467
+ const wrapped = {};
468
+ for (const [name, render] of Object.entries(renderers)) {
469
+ const schema = definitions[name]?.props;
470
+ const shape = schema?.shape;
471
+ wrapped[name] = shape ? (ctx) => render({
472
+ ...ctx,
473
+ props: conformProps(shape, ctx.props, asWritten[name])
474
+ }) : render;
475
+ }
476
+ return wrapped;
477
+ }
478
+ function textOf(value) {
479
+ if (value === void 0 || value === null) return "";
480
+ if (Array.isArray(value)) {
481
+ return value.map(textOf).filter((text) => text !== "").join(", ");
482
+ }
483
+ if (typeof value === "object") return "";
484
+ return String(value);
485
+ }
486
+ var FrameHostContext = React6.createContext({});
2121
487
  function FrameHostProvider({
2122
488
  host,
2123
489
  children
@@ -2125,15 +491,15 @@ function FrameHostProvider({
2125
491
  return /* @__PURE__ */ jsx(FrameHostContext.Provider, { value: host, children });
2126
492
  }
2127
493
  function useFrameHost() {
2128
- return React4.useContext(FrameHostContext);
494
+ return React6.useContext(FrameHostContext);
2129
495
  }
2130
496
  function useBoundField(literal, binding, empty, { checks, validateOn }) {
2131
497
  const [boundValue, setBoundValue] = useBoundProp(literal ?? void 0, binding);
2132
- const [localValue, setLocalValue] = React4.useState(literal ?? empty);
498
+ const [localValue, setLocalValue] = React6.useState(literal ?? empty);
2133
499
  const isBound = Boolean(binding);
2134
500
  const hasValidation = Boolean(binding && checks?.length);
2135
501
  const when = validateOn ?? "change";
2136
- const { errors, validate } = useFieldValidation(
502
+ const { errors, validate } = useCheckedField(
2137
503
  binding ?? "",
2138
504
  hasValidation ? { checks: checks ?? [], validateOn: when } : void 0
2139
505
  );
@@ -2401,7 +767,7 @@ function Progress({
2401
767
  ] });
2402
768
  }
2403
769
  function useControlId(name) {
2404
- return `jr-${name}-${React4.useId()}`;
770
+ return `jr-${name}-${React6.useId()}`;
2405
771
  }
2406
772
  function Input({
2407
773
  props,
@@ -2580,9 +946,9 @@ function Button({
2580
946
  }
2581
947
  );
2582
948
  }
2583
- var DescriptorContext = React4.createContext({});
949
+ var DescriptorContext = React6.createContext({});
2584
950
  function useDescriptorScope() {
2585
- return React4.useContext(DescriptorContext);
951
+ return React6.useContext(DescriptorContext);
2586
952
  }
2587
953
  function DescriptorProvider({
2588
954
  scope,
@@ -2598,8 +964,9 @@ function Unresolved({ path, side }) {
2598
964
  className: "rounded-md border border-destructive/50 px-2.5 py-1.5 text-[12px] text-destructive",
2599
965
  children: [
2600
966
  side === "input" ? "No input" : "No result",
2601
- " at ",
2602
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: path })
967
+ " at",
968
+ " ",
969
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: typeof path === "string" ? path : JSON.stringify(path) ?? String(path) })
2603
970
  ]
2604
971
  }
2605
972
  );
@@ -2626,6 +993,7 @@ function pathFromDomId(prefix, id) {
2626
993
  }
2627
994
  function useAbsolutePath(path) {
2628
995
  const repeat = useRepeatScope();
996
+ if (typeof path !== "string") return void 0;
2629
997
  if (path.startsWith("/")) return path;
2630
998
  return repeat ? `${repeat.basePath}/${path}` : void 0;
2631
999
  }
@@ -2635,10 +1003,10 @@ function withoutOptionalMark(label) {
2635
1003
  function MthdsField({
2636
1004
  props
2637
1005
  }) {
2638
- const scope = React4.useContext(DescriptorContext);
1006
+ const scope = React6.useContext(DescriptorContext);
2639
1007
  const path = useAbsolutePath(props.path) ?? "";
2640
1008
  const found = scope.inputs && path ? inputFieldAtPath(scope.inputs, path) : void 0;
2641
- const field = React4.useMemo(
1009
+ const field = React6.useMemo(
2642
1010
  () => found && (props.label || props.hint) ? {
2643
1011
  ...found,
2644
1012
  // The control marks an optional field itself, so a label that
@@ -2650,7 +1018,7 @@ function MthdsField({
2650
1018
  );
2651
1019
  const value = useStateValue(path);
2652
1020
  const { set } = useStateStore();
2653
- const onChange = React4.useCallback((next) => set(path, next), [set, path]);
1021
+ const onChange = React6.useCallback((next) => set(path, next), [set, path]);
2654
1022
  if (!field) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "input" });
2655
1023
  return /* @__PURE__ */ jsx(
2656
1024
  FieldRenderer,
@@ -2664,7 +1032,7 @@ function MthdsField({
2664
1032
  );
2665
1033
  }
2666
1034
  function MthdsResult({ props }) {
2667
- const scope = React4.useContext(DescriptorContext);
1035
+ const scope = React6.useContext(DescriptorContext);
2668
1036
  const path = useAbsolutePath(props.path) ?? "";
2669
1037
  const field = scope.result && path ? resultFieldAtPath(scope.result, path) : void 0;
2670
1038
  const value = useStateValue(path);
@@ -2677,13 +1045,13 @@ function AccessibleSelect({ props, bindings, emit }) {
2677
1045
  props.value ?? void 0,
2678
1046
  bindings?.value
2679
1047
  );
2680
- const [localValue, setLocalValue] = React4.useState("");
1048
+ const [localValue, setLocalValue] = React6.useState("");
2681
1049
  const isBound = Boolean(bindings?.value);
2682
1050
  const value = isBound ? boundValue ?? "" : localValue;
2683
1051
  const setValue = isBound ? setBoundValue : setLocalValue;
2684
1052
  const validateOn = props.validateOn ?? "change";
2685
1053
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
2686
- const { errors, validate } = useFieldValidation(
1054
+ const { errors, validate } = useCheckedField(
2687
1055
  bindings?.value ?? "",
2688
1056
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
2689
1057
  );
@@ -2709,7 +1077,7 @@ function AccessibleSelect({ props, bindings, emit }) {
2709
1077
  errors.length > 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errors[0] }) : null
2710
1078
  ] });
2711
1079
  }
2712
- function valueAt2(row, relativePath) {
1080
+ function valueAt(row, relativePath) {
2713
1081
  let current = row;
2714
1082
  for (const segment of relativePath.split("/").filter((part) => part.length > 0)) {
2715
1083
  if (typeof current !== "object" || current === null) return void 0;
@@ -2755,7 +1123,7 @@ function DataTable({ props }) {
2755
1123
  },
2756
1124
  column.path
2757
1125
  )) }) }),
2758
- /* @__PURE__ */ jsx("tbody", { children: rows.map((row, index) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/60 last:border-b-0", children: columns.map((column) => /* @__PURE__ */ jsx("td", { className: "px-2.5 py-1.5 align-top text-foreground", children: formatColumnCell(valueAt2(row, column.path), column.format) }, column.path)) }, index)) })
1126
+ /* @__PURE__ */ jsx("tbody", { children: rows.map((row, index) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/60 last:border-b-0", children: columns.map((column) => /* @__PURE__ */ jsx("td", { className: "px-2.5 py-1.5 align-top text-foreground", children: formatColumnCell(valueAt(row, column.path), column.format) }, column.path)) }, index)) })
2759
1127
  ] }) });
2760
1128
  }
2761
1129
  function numberFormat(format) {
@@ -2774,7 +1142,7 @@ function numberFormat(format) {
2774
1142
  }
2775
1143
  function Metric({ props }) {
2776
1144
  const value = props.value;
2777
- const text = typeof value === "number" ? numberFormat(props.format ?? "plain").format(value) : value === null || value === void 0 || value === "" ? "\u2014" : String(value);
1145
+ const text = typeof value === "number" ? numberFormat(props.format ?? "plain").format(value) : textOf(value) || "\u2014";
2778
1146
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
2779
1147
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: props.label }),
2780
1148
  /* @__PURE__ */ jsxs("span", { className: "text-2xl font-semibold tabular-nums text-foreground", children: [
@@ -2807,29 +1175,49 @@ function Split({
2807
1175
  }
2808
1176
  function Tabs({ props, children }) {
2809
1177
  const tabs = props.tabs ?? [];
2810
- const panels = React4.Children.toArray(children);
2811
- const [value, setValue] = React4.useState(tabs[0]?.value ?? "");
2812
- return /* @__PURE__ */ jsxs(TabsPrimitive.Root, { value, onValueChange: setValue, className: "w-full", children: [
2813
- /* @__PURE__ */ jsx(TabsPrimitive.List, { className: "inline-flex h-10 items-center justify-start rounded-md bg-muted p-1 text-muted-foreground max-sm:h-auto max-sm:flex-wrap", children: tabs.map((tab) => /* @__PURE__ */ jsx(
1178
+ const panels = React6.Children.toArray(children);
1179
+ const [chosen, setChosen] = React6.useState(null);
1180
+ const open = chosen ? openIndex(tabs, chosen) : 0;
1181
+ const value = String(open);
1182
+ const choose = (next) => {
1183
+ const index = Number(next);
1184
+ const tab = tabs[index];
1185
+ if (!tab) return;
1186
+ const occurrence = tabs.slice(0, index).filter((t) => t.value === tab.value).length;
1187
+ setChosen({ value: tab.value, occurrence });
1188
+ };
1189
+ return /* @__PURE__ */ jsxs(TabsPrimitive.Root, { value, onValueChange: choose, className: "w-full", children: [
1190
+ /* @__PURE__ */ jsx(TabsPrimitive.List, { className: "inline-flex h-10 items-center justify-start rounded-md bg-muted p-1 text-muted-foreground max-sm:h-auto max-sm:flex-wrap", children: tabs.map((tab, index) => /* @__PURE__ */ jsx(
2814
1191
  TabsPrimitive.Trigger,
2815
1192
  {
2816
- value: tab.value,
1193
+ value: String(index),
2817
1194
  className: "inline-flex items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-all focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-hidden data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm max-sm:min-h-11 max-sm:min-w-11",
2818
1195
  children: tab.label
2819
1196
  },
2820
- tab.value
1197
+ index
2821
1198
  )) }),
2822
- tabs.map((tab, index) => /* @__PURE__ */ jsx(
1199
+ tabs.map((_tab, index) => /* @__PURE__ */ jsx(
2823
1200
  TabsPrimitive.Content,
2824
1201
  {
2825
- value: tab.value,
1202
+ value: String(index),
1203
+ forceMount: true,
1204
+ hidden: index !== open,
2826
1205
  className: "mt-4 focus-visible:outline-hidden",
2827
1206
  children: panels[index] ?? null
2828
1207
  },
2829
- tab.value
1208
+ index
2830
1209
  ))
2831
1210
  ] });
2832
1211
  }
1212
+ function openIndex(tabs, { value, occurrence }) {
1213
+ let seen = 0;
1214
+ for (const [index, tab] of tabs.entries()) {
1215
+ if (tab.value !== value) continue;
1216
+ if (seen === occurrence) return index;
1217
+ seen += 1;
1218
+ }
1219
+ return 0;
1220
+ }
2833
1221
  var BUTTON_BASE = "inline-flex min-h-11 items-center justify-center rounded-md px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50";
2834
1222
  var PRIMARY_BUTTON = cn(BUTTON_BASE, "bg-primary text-primary-foreground hover:bg-primary/90");
2835
1223
  var SECONDARY_BUTTON = cn(
@@ -2838,9 +1226,10 @@ var SECONDARY_BUTTON = cn(
2838
1226
  );
2839
1227
  function Steps({ props, children }) {
2840
1228
  const steps = props.steps ?? [];
2841
- const panels = React4.Children.toArray(children);
2842
- const [index, setIndex] = React4.useState(0);
1229
+ const panels = React6.Children.toArray(children);
1230
+ const [chosen, setIndex] = React6.useState(0);
2843
1231
  const last = steps.length - 1;
1232
+ const index = Math.max(0, Math.min(chosen, last));
2844
1233
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
2845
1234
  /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: steps.map((label, position) => /* @__PURE__ */ jsxs(
2846
1235
  "li",
@@ -2866,7 +1255,7 @@ function Steps({ props, children }) {
2866
1255
  },
2867
1256
  `${position}-${label}`
2868
1257
  )) }),
2869
- /* @__PURE__ */ jsx("div", { children: panels[index] ?? null }),
1258
+ steps.map((_step, position) => /* @__PURE__ */ jsx("div", { hidden: position !== index, children: panels[position] ?? null }, position)),
2870
1259
  index > 0 || index < last ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
2871
1260
  index > 0 ? /* @__PURE__ */ jsx(
2872
1261
  "button",
@@ -2968,7 +1357,7 @@ function Segmented({ props, bindings, emit }) {
2968
1357
  props.value ?? void 0,
2969
1358
  bindings?.value
2970
1359
  );
2971
- const [localValue, setLocalValue] = React4.useState("");
1360
+ const [localValue, setLocalValue] = React6.useState("");
2972
1361
  const isBound = Boolean(bindings?.value);
2973
1362
  const value = isBound ? boundValue ?? "" : localValue;
2974
1363
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -2988,7 +1377,7 @@ function Segmented({ props, bindings, emit }) {
2988
1377
  emit("change");
2989
1378
  },
2990
1379
  className: "flex-wrap justify-start gap-1.5",
2991
- children: (props.options ?? []).map((option) => /* @__PURE__ */ jsx(
1380
+ children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsx(
2992
1381
  ToggleGroupItem,
2993
1382
  {
2994
1383
  value: option,
@@ -2997,7 +1386,7 @@ function Segmented({ props, bindings, emit }) {
2997
1386
  className: "rounded-full px-3.5 data-[state=on]:border-primary data-[state=on]:bg-primary data-[state=on]:text-primary-foreground max-sm:h-11 max-sm:min-w-11",
2998
1387
  children: labelOf(option)
2999
1388
  },
3000
- option
1389
+ `${index}-${option}`
3001
1390
  ))
3002
1391
  }
3003
1392
  )
@@ -3008,7 +1397,7 @@ function NumberInput({ props, bindings, emit }) {
3008
1397
  props.value ?? void 0,
3009
1398
  bindings?.value
3010
1399
  );
3011
- const [localValue, setLocalValue] = React4.useState(void 0);
1400
+ const [localValue, setLocalValue] = React6.useState(void 0);
3012
1401
  const isBound = Boolean(bindings?.value);
3013
1402
  const value = isBound ? boundValue : localValue;
3014
1403
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -3042,7 +1431,7 @@ function NumberInput({ props, bindings, emit }) {
3042
1431
  ] })
3043
1432
  ] });
3044
1433
  }
3045
- var components2 = {
1434
+ var components = {
3046
1435
  Card: (ctx) => /* @__PURE__ */ jsx(Card, { ...ctx }),
3047
1436
  // A vertical Stack stretches its children unless the spec says otherwise:
3048
1437
  // hugging the content is the one layout a result page never wants, since a
@@ -3081,7 +1470,13 @@ var components2 = {
3081
1470
  DataTable: (ctx) => /* @__PURE__ */ jsx(DataTable, { ...ctx }),
3082
1471
  Metric: (ctx) => /* @__PURE__ */ jsx(Metric, { ...ctx })
3083
1472
  };
3084
- var generativeRenderers = components2;
1473
+ var generativeRenderers = withDeclaredProps(
1474
+ baseCatalog.data.components,
1475
+ components,
1476
+ // A hatch resolves its `path` as written, relative inside a repeat, so a
1477
+ // refused one reaches it as refused rather than written out as text.
1478
+ { MthdsField: ["path"], MthdsResult: ["path"] }
1479
+ );
3085
1480
  var RADIUS = "rounded-[calc(var(--radius,0.5rem)*1.5)]";
3086
1481
  function useAbsolute(path) {
3087
1482
  const repeat = useRepeatScope();
@@ -3229,10 +1624,12 @@ function amount(value, unit) {
3229
1624
  return unit ? `${n} ${unit}` : n;
3230
1625
  }
3231
1626
  function ProportionBar({ props }) {
3232
- const rows = Array.isArray(props.items) ? props.items : [];
1627
+ const rows = (Array.isArray(props.items) ? props.items : []).filter(
1628
+ (row) => typeof row === "object" && row !== null && !Array.isArray(row)
1629
+ );
3233
1630
  const parts = rows.map((row) => ({
3234
- label: String(row[props.labelKey] ?? ""),
3235
- value: Number(row[props.valueKey] ?? 0)
1631
+ label: textOf(row[props.labelKey]),
1632
+ value: shareAmount(row[props.valueKey])
3236
1633
  })).filter((p) => Number.isFinite(p.value) && p.value > 0);
3237
1634
  const sum = parts.reduce((s, p) => s + p.value, 0);
3238
1635
  const total = typeof props.total === "number" ? props.total : sum;
@@ -3317,10 +1714,11 @@ var ASPECT = {
3317
1714
  function Picture({
3318
1715
  props
3319
1716
  }) {
3320
- const path = useAbsolute(props.path);
1717
+ const written = typeof props.path === "string" ? props.path : void 0;
1718
+ const path = useAbsolute(written ?? "");
3321
1719
  const value = useStateValue(path);
3322
1720
  const resolve = useResolveUrl();
3323
- const content = readImageContent(value);
1721
+ const content = written === void 0 ? void 0 : readImageContent(value);
3324
1722
  const src = content ? viewableUrl(resolve?.(content.url)) ?? viewableUrl(content.publicUrl) ?? viewableUrl(content.url) : void 0;
3325
1723
  const aspect = props.aspect && props.aspect !== "auto" ? ASPECT[props.aspect] : void 0;
3326
1724
  if (!src) return null;
@@ -3337,26 +1735,25 @@ function Picture({
3337
1735
  }
3338
1736
  );
3339
1737
  }
3340
- function present(value) {
3341
- return value !== void 0 && value !== null && value !== "";
3342
- }
3343
1738
  function Fact({
3344
1739
  props
3345
1740
  }) {
1741
+ const text = textOf(props.value);
3346
1742
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 rounded-[var(--radius,0.5rem)] border border-border bg-background px-4 py-3", children: [
3347
1743
  /* @__PURE__ */ jsx("span", { className: "text-[13px] text-muted-foreground", children: props.label }),
3348
- /* @__PURE__ */ jsx("span", { className: "text-[16px] font-medium text-foreground tabular-nums", children: present(props.value) ? String(props.value) : "\u2014" }),
1744
+ /* @__PURE__ */ jsx("span", { className: "text-[16px] font-medium text-foreground tabular-nums", children: text !== "" ? text : "\u2014" }),
3349
1745
  props.detail ? /* @__PURE__ */ jsx("span", { className: "text-[13px] text-muted-foreground", children: props.detail }) : null
3350
1746
  ] });
3351
1747
  }
3352
1748
  function Detail({ props }) {
3353
- if (!present(props.value)) return null;
1749
+ const text = textOf(props.value);
1750
+ if (text === "") return null;
3354
1751
  return (
3355
1752
  // A rule between the rows of a list of details, never under one set in a
3356
1753
  // row: there it read as an underline on every detail but the last.
3357
1754
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[minmax(0,8.5rem)_minmax(0,1fr)] gap-3 border-b border-border py-2.5 text-[14px] last:border-b-0 [.flex-row>&]:border-b-0 [.flex-row>&]:grid-cols-[auto_minmax(0,1fr)]", children: [
3358
1755
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: props.label }),
3359
- /* @__PURE__ */ jsx("span", { className: "text-foreground", children: String(props.value) })
1756
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: text })
3360
1757
  ] })
3361
1758
  );
3362
1759
  }
@@ -3393,19 +1790,25 @@ function TextList({ props }) {
3393
1790
  /* @__PURE__ */ jsx("span", { className: "min-w-0", children: String(text) })
3394
1791
  ] }, index)) });
3395
1792
  }
3396
- var frameRenderers = {
3397
- TextList: (ctx) => /* @__PURE__ */ jsx(TextList, { ...ctx }),
3398
- DateText: (ctx) => /* @__PURE__ */ jsx(DateText, { ...ctx }),
3399
- Composer: (ctx) => /* @__PURE__ */ jsx(Composer, { ...ctx }),
3400
- FieldGroup: (ctx) => /* @__PURE__ */ jsx(FieldGroup, { ...ctx }),
3401
- ResultHeader: (ctx) => /* @__PURE__ */ jsx(ResultHeader, { ...ctx }),
3402
- ProportionBar: (ctx) => /* @__PURE__ */ jsx(ProportionBar, { ...ctx }),
3403
- Swatches: (ctx) => /* @__PURE__ */ jsx(Swatches, { ...ctx }),
3404
- Picture: (ctx) => /* @__PURE__ */ jsx(Picture, { ...ctx }),
3405
- Fact: (ctx) => /* @__PURE__ */ jsx(Fact, { ...ctx }),
3406
- Detail: (ctx) => /* @__PURE__ */ jsx(Detail, { ...ctx })
3407
- };
3408
- var ResultSlotContext = React4.createContext(null);
1793
+ var frameRenderers = withDeclaredProps(
1794
+ frameComponents,
1795
+ {
1796
+ TextList: (ctx) => /* @__PURE__ */ jsx(TextList, { ...ctx }),
1797
+ DateText: (ctx) => /* @__PURE__ */ jsx(DateText, { ...ctx }),
1798
+ Composer: (ctx) => /* @__PURE__ */ jsx(Composer, { ...ctx }),
1799
+ FieldGroup: (ctx) => /* @__PURE__ */ jsx(FieldGroup, { ...ctx }),
1800
+ ResultHeader: (ctx) => /* @__PURE__ */ jsx(ResultHeader, { ...ctx }),
1801
+ ProportionBar: (ctx) => /* @__PURE__ */ jsx(ProportionBar, { ...ctx }),
1802
+ Swatches: (ctx) => /* @__PURE__ */ jsx(Swatches, { ...ctx }),
1803
+ Picture: (ctx) => /* @__PURE__ */ jsx(Picture, { ...ctx }),
1804
+ Fact: (ctx) => /* @__PURE__ */ jsx(Fact, { ...ctx }),
1805
+ Detail: (ctx) => /* @__PURE__ */ jsx(Detail, { ...ctx })
1806
+ },
1807
+ // A refused `path` reaches the renderer as refused: its nearest value, the
1808
+ // empty text, is the item itself inside a repeat.
1809
+ { Picture: ["path"] }
1810
+ );
1811
+ var ResultSlotContext = React6.createContext(null);
3409
1812
  function ResultSlotProvider({
3410
1813
  slot,
3411
1814
  children
@@ -3413,58 +1816,11 @@ function ResultSlotProvider({
3413
1816
  return /* @__PURE__ */ jsx(ResultSlotContext.Provider, { value: slot, children });
3414
1817
  }
3415
1818
  function useResultSlot() {
3416
- return React4.useContext(ResultSlotContext);
1819
+ return React6.useContext(ResultSlotContext);
3417
1820
  }
3418
- var CONTAINER = "mx-auto w-full max-w-6xl px-6 sm:px-8";
3419
1821
  var LABEL2 = "block text-[13.5px] leading-none font-medium text-foreground";
3420
1822
  function AppBar({ props }) {
3421
- const brand = useBrand();
3422
- return /* @__PURE__ */ jsx("header", { className: "border-b border-border", children: /* @__PURE__ */ jsxs("div", { className: cn(CONTAINER, "flex h-16 items-center gap-5"), children: [
3423
- /* @__PURE__ */ jsx(
3424
- "img",
3425
- {
3426
- src: brand.logo.onLight,
3427
- alt: brand.name,
3428
- referrerPolicy: "no-referrer",
3429
- className: "h-7 w-auto dark:hidden"
3430
- }
3431
- ),
3432
- /* @__PURE__ */ jsx(
3433
- "img",
3434
- {
3435
- src: brand.logo.onDark,
3436
- alt: brand.name,
3437
- referrerPolicy: "no-referrer",
3438
- className: "hidden h-7 w-auto dark:block"
3439
- }
3440
- ),
3441
- /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-5 w-px bg-border" }),
3442
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground/80", children: props.app }),
3443
- /* @__PURE__ */ jsxs("div", { className: "ml-auto flex min-w-0 items-center gap-6", children: [
3444
- props.links && props.links.length > 0 ? /* @__PURE__ */ jsx("nav", { "aria-label": "Site", className: "hidden items-center gap-6 sm:flex", children: props.links.map((link) => /* @__PURE__ */ jsx(
3445
- "a",
3446
- {
3447
- href: "#",
3448
- onClick: (event) => event.preventDefault(),
3449
- className: "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-hidden",
3450
- children: link
3451
- },
3452
- link
3453
- )) }) : null,
3454
- props.tag ? /* @__PURE__ */ jsx(
3455
- "span",
3456
- {
3457
- className: cn(
3458
- "min-w-0 truncate rounded-lg border px-2.5 py-1 font-mono text-[11px]",
3459
- selectedBorderClass,
3460
- selectedFillClass,
3461
- linkTextClass
3462
- ),
3463
- children: props.tag
3464
- }
3465
- ) : null
3466
- ] })
3467
- ] }) });
1823
+ return /* @__PURE__ */ jsx(BrandBar, { app: props.app, links: props.links, tag: props.tag });
3468
1824
  }
3469
1825
  function Hero({ props }) {
3470
1826
  return /* @__PURE__ */ jsxs("section", { className: "pt-12 pb-10 sm:pt-14 sm:pb-12", children: [
@@ -3489,7 +1845,7 @@ function Hero({ props }) {
3489
1845
  ] });
3490
1846
  }
3491
1847
  function Workspace({ props, children }) {
3492
- const [work, rail] = React4.Children.toArray(children);
1848
+ const [work, rail] = React6.Children.toArray(children);
3493
1849
  const railLeft = props.rail === "left";
3494
1850
  return /* @__PURE__ */ jsxs(
3495
1851
  "div",
@@ -3561,11 +1917,8 @@ function Rail({ props, children }) {
3561
1917
  )
3562
1918
  );
3563
1919
  }
3564
- function present2(value) {
3565
- return value !== void 0 && value !== null && value !== "";
3566
- }
3567
1920
  function SummaryRow({ props }) {
3568
- const text = [props.value, props.detail].filter(present2).map(String).join(props.separator ?? " ");
1921
+ const text = [props.value, props.detail].map(textOf).filter((half) => half !== "").join(props.separator ?? " ");
3569
1922
  return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-4 border-b border-border/60 py-3 first:pt-0 last:border-b-0", children: [
3570
1923
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[13px] text-muted-foreground", children: props.label }),
3571
1924
  text ? /* @__PURE__ */ jsx("span", { className: "text-right text-[13px] font-medium text-foreground", children: text }) : /* @__PURE__ */ jsx("span", { className: "text-[13px] text-muted-foreground/50", children: props.placeholder ?? "\u2014" })
@@ -3618,13 +1971,13 @@ function useTextField(props, bindings) {
3618
1971
  props.value ?? void 0,
3619
1972
  bindings?.value
3620
1973
  );
3621
- const [localValue, setLocalValue] = React4.useState("");
1974
+ const [localValue, setLocalValue] = React6.useState("");
3622
1975
  const isBound = Boolean(bindings?.value);
3623
1976
  const value = isBound ? boundValue ?? "" : localValue;
3624
1977
  const setValue = isBound ? setBoundValue : setLocalValue;
3625
1978
  const validateOn = props.validateOn ?? "blur";
3626
1979
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
3627
- const { errors, validate } = useFieldValidation(
1980
+ const { errors, validate } = useCheckedField(
3628
1981
  bindings?.value ?? "",
3629
1982
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
3630
1983
  );
@@ -3698,16 +2051,20 @@ function BrandTextarea({
3698
2051
  var { registry: generativeRegistry } = defineRegistry(catalog, {
3699
2052
  components: {
3700
2053
  ...generativeRenderers,
3701
- Input: (ctx) => /* @__PURE__ */ jsx(BrandInput, { ...ctx }),
3702
- Textarea: (ctx) => /* @__PURE__ */ jsx(BrandTextarea, { ...ctx }),
3703
- AppBar: (ctx) => /* @__PURE__ */ jsx(AppBar, { ...ctx }),
3704
- Hero: (ctx) => /* @__PURE__ */ jsx(Hero, { ...ctx }),
3705
- Workspace: (ctx) => /* @__PURE__ */ jsx(Workspace, { ...ctx }),
3706
- Section: (ctx) => /* @__PURE__ */ jsx(Section, { ...ctx }),
3707
- Rail: (ctx) => /* @__PURE__ */ jsx(Rail, { ...ctx }),
3708
- SummaryRow: (ctx) => /* @__PURE__ */ jsx(SummaryRow, { ...ctx }),
3709
- Cta: (ctx) => /* @__PURE__ */ jsx(Cta, { ...ctx }),
3710
- Footer: (ctx) => /* @__PURE__ */ jsx(Footer, { ...ctx }),
2054
+ // Held to their definitions as the base and frame renderers are
2055
+ // (`declared-props.ts`).
2056
+ ...withDeclaredProps(catalog.data.components, {
2057
+ Input: (ctx) => /* @__PURE__ */ jsx(BrandInput, { ...ctx }),
2058
+ Textarea: (ctx) => /* @__PURE__ */ jsx(BrandTextarea, { ...ctx }),
2059
+ AppBar: (ctx) => /* @__PURE__ */ jsx(AppBar, { ...ctx }),
2060
+ Hero: (ctx) => /* @__PURE__ */ jsx(Hero, { ...ctx }),
2061
+ Workspace: (ctx) => /* @__PURE__ */ jsx(Workspace, { ...ctx }),
2062
+ Section: (ctx) => /* @__PURE__ */ jsx(Section, { ...ctx }),
2063
+ Rail: (ctx) => /* @__PURE__ */ jsx(Rail, { ...ctx }),
2064
+ SummaryRow: (ctx) => /* @__PURE__ */ jsx(SummaryRow, { ...ctx }),
2065
+ Cta: (ctx) => /* @__PURE__ */ jsx(Cta, { ...ctx }),
2066
+ Footer: (ctx) => /* @__PURE__ */ jsx(Footer, { ...ctx })
2067
+ }),
3711
2068
  ...frameRenderers
3712
2069
  }
3713
2070
  });
@@ -3718,130 +2075,100 @@ function GenerativePage({
3718
2075
  onRun,
3719
2076
  loading,
3720
2077
  registry = generativeRegistry,
3721
- brand
2078
+ brand,
2079
+ onElementError
3722
2080
  }) {
3723
2081
  const { inputs, result, env, idPrefix } = scope;
3724
- const stableScope = React4.useMemo(
2082
+ const stableScope = React6.useMemo(
3725
2083
  () => ({ inputs, result, env, idPrefix }),
3726
2084
  [inputs, result, env, idPrefix]
3727
2085
  );
3728
- const latest = React4.useRef({ onRun, store });
3729
- React4.useEffect(() => {
2086
+ const latest = React6.useRef({ onRun, store });
2087
+ React6.useEffect(() => {
3730
2088
  latest.current = { onRun, store };
3731
2089
  });
3732
- const running = React4.useRef(false);
3733
- const [handlers] = React4.useState(() => ({
3734
- run: () => {
3735
- if (running.current) return void 0;
3736
- running.current = true;
3737
- let pending;
3738
- try {
3739
- pending = latest.current.onRun?.(latest.current.store.getSnapshot());
3740
- } catch (error) {
2090
+ const report = React6.useRef(onElementError);
2091
+ React6.useInsertionEffect(() => {
2092
+ report.current = onElementError;
2093
+ });
2094
+ const [made] = React6.useState(() => /* @__PURE__ */ new Map());
2095
+ const pageRegistry = React6.useMemo(
2096
+ () => reportingRegistry(registry, report, made),
2097
+ [registry, made]
2098
+ );
2099
+ const rootType = spec?.elements?.[spec.root]?.type ?? "root";
2100
+ const reporting = onElementError !== void 0;
2101
+ const validation = React6.useRef(null);
2102
+ const [checkedFields] = React6.useState(() => new CheckedFields());
2103
+ const running = React6.useRef(false);
2104
+ const [handlers] = React6.useState(() => {
2105
+ const release = () => {
2106
+ setTimeout(() => {
3741
2107
  running.current = false;
3742
- throw error;
2108
+ }, 0);
2109
+ };
2110
+ return {
2111
+ run: () => {
2112
+ if (running.current) return void 0;
2113
+ const checks = validation.current;
2114
+ if (checks) {
2115
+ const registered = checks.validateAll();
2116
+ const mounted = checkedFields.check(checks);
2117
+ if (!registered || !mounted) return void 0;
2118
+ }
2119
+ running.current = true;
2120
+ let pending;
2121
+ try {
2122
+ pending = latest.current.onRun?.(latest.current.store.getSnapshot());
2123
+ } catch (error) {
2124
+ release();
2125
+ throw error;
2126
+ }
2127
+ if (isThenable(pending)) {
2128
+ const settled = Promise.resolve(pending);
2129
+ settled.then(release, release);
2130
+ return settled;
2131
+ }
2132
+ release();
2133
+ return void 0;
3743
2134
  }
3744
- if (isThenable(pending)) {
3745
- return Promise.resolve(pending).finally(() => {
3746
- running.current = false;
3747
- });
2135
+ };
2136
+ });
2137
+ const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(JSONUIProvider, { registry: pageRegistry, store, handlers, children: /* @__PURE__ */ jsx(ReportingContext.Provider, { value: reporting, children: /* @__PURE__ */ jsxs(CheckedFieldsContext.Provider, { value: checkedFields, children: [
2138
+ /* @__PURE__ */ jsx(ValidationBridge, { into: validation, fields: checkedFields }),
2139
+ /* @__PURE__ */ jsx(
2140
+ RootBoundary,
2141
+ {
2142
+ type: rootType,
2143
+ report,
2144
+ reporting,
2145
+ loading: loading === true,
2146
+ spec,
2147
+ children: /* @__PURE__ */ jsx(Renderer, { spec, registry: pageRegistry, loading })
3748
2148
  }
3749
- running.current = false;
3750
- return void 0;
3751
- }
3752
- }));
3753
- const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(JSONUIProvider, { registry, store, handlers, children: /* @__PURE__ */ jsx(Renderer, { spec, registry, loading }) }) });
2149
+ )
2150
+ ] }) }) }) });
3754
2151
  return brand ? /* @__PURE__ */ jsx(BrandProvider, { manifest: brand, children: page }) : page;
3755
2152
  }
2153
+ function ValidationBridge({
2154
+ into,
2155
+ fields
2156
+ }) {
2157
+ const validation = useOptionalValidation();
2158
+ React6.useInsertionEffect(() => {
2159
+ into.current = validation;
2160
+ fields.attach(validation);
2161
+ });
2162
+ return null;
2163
+ }
3756
2164
  function useStoreSnapshot(store) {
3757
- return React4.useSyncExternalStore(
2165
+ return React6.useSyncExternalStore(
3758
2166
  store.subscribe,
3759
2167
  store.getSnapshot,
3760
2168
  store.getServerSnapshot ?? store.getSnapshot
3761
2169
  );
3762
2170
  }
3763
2171
 
3764
- // src/generative/state.ts
3765
- function isRecord(value) {
3766
- return typeof value === "object" && value !== null && !Array.isArray(value);
3767
- }
3768
- function payloadToState(field, payload) {
3769
- if (payload === void 0 || payload === null) return payload;
3770
- if (isNativeValueNode(field)) return payload;
3771
- switch (field.kind) {
3772
- case "date": {
3773
- const content = readDateContent(payload);
3774
- return content ? formatDateContent(content) : payload;
3775
- }
3776
- case "list": {
3777
- const items = Array.isArray(payload) ? payload : isRecord(payload) && Array.isArray(payload.items) ? payload.items : void 0;
3778
- if (!items) return payload;
3779
- return items.map((item) => payloadToState(field.item, item));
3780
- }
3781
- case "object": {
3782
- if (!isRecord(payload)) return payload;
3783
- const out = { ...payload };
3784
- for (const child of field.fields) {
3785
- if (hasOwnProp(payload, child.name)) {
3786
- out[child.name] = payloadToState(child, payload[child.name]);
3787
- }
3788
- }
3789
- return out;
3790
- }
3791
- case "document":
3792
- case "image":
3793
- case "text":
3794
- case "prose":
3795
- case "number":
3796
- case "boolean":
3797
- case "enum":
3798
- case "unknown":
3799
- return payload;
3800
- default:
3801
- return field;
3802
- }
3803
- }
3804
-
3805
- // src/generative/fixture.ts
3806
- function fixtureId(fixture) {
3807
- return [
3808
- fixture.producer,
3809
- fixture.model,
3810
- fixture.frame ?? null,
3811
- fixture.style ?? null,
3812
- fixture.seed ? "seeded" : null,
3813
- fixture.critic ? "critic" : null
3814
- ].filter((part) => part !== null).join("--");
3815
- }
3816
- var PRODUCER_LABEL = {
3817
- "pipelex-method": "Pipelex method",
3818
- "claude-code-subagent": "Claude Code subagent",
3819
- "claude-code-session": "Claude Code session, by hand"
3820
- };
3821
- function fixtureLabel(fixture) {
3822
- const parts = [PRODUCER_LABEL[fixture.producer], fixture.model];
3823
- if (fixture.frame) parts.push(`${fixture.frame} frame`);
3824
- if (fixture.style) parts.push(`${fixture.style} style`);
3825
- if (fixture.seed) parts.push("with a seed");
3826
- if (fixture.repairs) parts.push(`repaired \xD7 ${fixture.repairs}`);
3827
- if (fixture.critic) {
3828
- parts.push(
3829
- `${fixture.critic.model} critic \xD7 ${fixture.critic.rounds} round${fixture.critic.rounds === 1 ? "" : "s"}`
3830
- );
3831
- }
3832
- return parts.join(" \xB7 ");
3833
- }
3834
- function findFixture(fixtures, pipeRef, id) {
3835
- const found = fixtures.find(
3836
- (fixture) => fixture.pipeRef === pipeRef && fixtureId(fixture) === id
3837
- );
3838
- if (!found) {
3839
- const known = fixtures.filter((fixture) => fixture.pipeRef === pipeRef).map(fixtureId).join(", ");
3840
- throw new Error(`No spec fixture ${id} for ${pipeRef}. Known: ${known || "none"}.`);
3841
- }
3842
- return found;
3843
- }
3844
-
3845
- export { BrandProvider, COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, DescriptorProvider, FRAMES, FRAME_COMPONENTS, FrameHostProvider, GenerativePage, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, ResultSlotProvider, absoluteHatchPath, catalog, componentNamesFor, designerCatalog, domIdFor, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, generativeRegistry, generativeRenderers, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheckProblems, layoutFits, layoutProblems, pathFromDomId, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, useBrand, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot, validateAgainstCatalog };
2172
+ export { BrandBar, BrandProvider, DescriptorProvider, FrameHostProvider, GenerativePage, ResultSlotProvider, domIdFor, generativeRegistry, generativeRenderers, pathFromDomId, useBrand, useCheckedField, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot };
3846
2173
  //# sourceMappingURL=index.js.map
3847
2174
  //# sourceMappingURL=index.js.map