@pipelex/mthds-form 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/dist/chunk-AZZAAGNX.js +194 -0
- package/dist/chunk-AZZAAGNX.js.map +1 -0
- package/dist/{chunk-IIU3YK4O.js → chunk-DARLDVJM.js} +3 -3
- package/dist/{chunk-IIU3YK4O.js.map → chunk-DARLDVJM.js.map} +1 -1
- package/dist/{chunk-R7B7ID25.js → chunk-IUWLOSAM.js} +4 -4
- package/dist/{chunk-R7B7ID25.js.map → chunk-IUWLOSAM.js.map} +1 -1
- package/dist/chunk-KG4IYFWI.js +2721 -0
- package/dist/chunk-KG4IYFWI.js.map +1 -0
- package/dist/{chunk-J4L32LD6.js → chunk-KIEOLP2L.js} +3 -3
- package/dist/{chunk-J4L32LD6.js.map → chunk-KIEOLP2L.js.map} +1 -1
- package/dist/{chunk-4BSBBG2T.js → chunk-S4CB423P.js} +3 -3
- package/dist/{chunk-4BSBBG2T.js.map → chunk-S4CB423P.js.map} +1 -1
- package/dist/{chunk-XYQ7NXQZ.js → chunk-WYH56SOZ.js} +2 -2
- package/dist/chunk-WYH56SOZ.js.map +1 -0
- package/dist/{chunk-YEVVR6FE.js → chunk-Y2ND6W4W.js} +3 -3
- package/dist/{chunk-YEVVR6FE.js.map → chunk-Y2ND6W4W.js.map} +1 -1
- package/dist/{chunk-T4NSAGOS.js → chunk-YABRYTQ4.js} +4 -4
- package/dist/{chunk-T4NSAGOS.js.map → chunk-YABRYTQ4.js.map} +1 -1
- package/dist/core/contracts.d.ts +4 -4
- package/dist/core/contracts.js +1 -1
- package/dist/core/derive.js +2 -2
- package/dist/core/gate.js +3 -3
- package/dist/core/gating-fields.js +2 -2
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.js +8 -8
- package/dist/core/native-content.d.ts +56 -9
- package/dist/core/native-content.js +1 -1
- package/dist/core/output-form.js +2 -2
- package/dist/core/stuff-files.js +2 -2
- package/dist/core/values.js +3 -3
- package/dist/field-renderer-BSU2hMf6.d.ts +34 -0
- package/dist/generative/index.d.ts +2013 -0
- package/dist/generative/index.js +3171 -0
- package/dist/generative/index.js.map +1 -0
- package/dist/react/index.d.ts +42 -36
- package/dist/react/index.js +8 -2614
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/ui-designer.mthds +304 -0
- package/package.json +19 -7
- package/dist/chunk-45C2MKK6.js +0 -94
- package/dist/chunk-45C2MKK6.js.map +0 -1
- package/dist/chunk-XYQ7NXQZ.js.map +0 -1
|
@@ -0,0 +1,3171 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { cn, fieldControlClass, ResultField, FieldRenderer, ToggleGroup, ToggleGroupItem, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../chunk-KG4IYFWI.js';
|
|
3
|
+
import '../chunk-S4CB423P.js';
|
|
4
|
+
import { isNativeHtmlNode, isNativeDateNode, isNativeCompositeNode, readDateContent, formatDateContent } from '../chunk-AZZAAGNX.js';
|
|
5
|
+
import '../chunk-25I6JDIK.js';
|
|
6
|
+
import '../chunk-2JWRG53S.js';
|
|
7
|
+
import '../chunk-PM55AHRO.js';
|
|
8
|
+
import '../chunk-GKV77RAS.js';
|
|
9
|
+
import { hasOwnProp } from '../chunk-V3OV2XQT.js';
|
|
10
|
+
import { defineSchema, defineCatalog, validateSpec, compileSpecStream } from '@json-render/core';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import * as React3 from 'react';
|
|
13
|
+
import { defineRegistry, useStateValue, useStateStore, useBoundProp, useFieldValidation, useRepeatScope, JSONUIProvider, Renderer } from '@json-render/react';
|
|
14
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
15
|
+
import { 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, Clock, Calendar, Moon, Sun, Leaf, Mountain, Globe, Landmark, Building2, Home, Hotel, Ship, Car, Train, Plane, Route, MapPin, Map as Map$1, Compass, Rocket, Sparkles } from 'lucide-react';
|
|
16
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
17
|
+
|
|
18
|
+
var generativeSchema = defineSchema(
|
|
19
|
+
(s) => ({
|
|
20
|
+
spec: s.object({
|
|
21
|
+
root: s.string(),
|
|
22
|
+
elements: s.record(
|
|
23
|
+
s.object({
|
|
24
|
+
type: s.ref("catalog.components"),
|
|
25
|
+
props: s.propsOf("catalog.components"),
|
|
26
|
+
children: s.array(s.string()),
|
|
27
|
+
slots: { ...s.record(s.array(s.string())), ...s.optional() },
|
|
28
|
+
visible: { ...s.any(), ...s.optional() },
|
|
29
|
+
repeat: { ...s.any(), ...s.optional() }
|
|
30
|
+
})
|
|
31
|
+
)
|
|
32
|
+
}),
|
|
33
|
+
catalog: s.object({
|
|
34
|
+
components: s.map({
|
|
35
|
+
props: s.zod(),
|
|
36
|
+
slots: s.array(s.string()),
|
|
37
|
+
description: s.string(),
|
|
38
|
+
example: s.any()
|
|
39
|
+
}),
|
|
40
|
+
actions: s.map({
|
|
41
|
+
params: s.zod(),
|
|
42
|
+
description: s.string()
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
}),
|
|
46
|
+
{
|
|
47
|
+
// The runtime handles these four without a registry handler; the names and
|
|
48
|
+
// the wording are the React schema's, so a model that has seen json-render
|
|
49
|
+
// before recognises them. The validator reads them to know which actions
|
|
50
|
+
// need no handler, and `designerCatalog()` lists them first, marked
|
|
51
|
+
// built-in, so the method's prompt names them beside the catalog's own.
|
|
52
|
+
builtInActions: [
|
|
53
|
+
{
|
|
54
|
+
name: "setState",
|
|
55
|
+
description: "Update a value in the state model at the given statePath. Params: { statePath: string, value: any }"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "pushState",
|
|
59
|
+
description: "Append an item to an array in state. Params: { statePath: string, value: any, clearStatePath?: string }."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: "removeState",
|
|
63
|
+
description: "Remove an item from an array in state by index. Params: { statePath: string, index: number }"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: "validateForm",
|
|
67
|
+
description: "Validate all registered form fields and write the result to state. Params: { statePath?: string }. Defaults to /formValidation."
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
var validationCheckSchema = z.array(
|
|
73
|
+
z.object({
|
|
74
|
+
type: z.string(),
|
|
75
|
+
message: z.string(),
|
|
76
|
+
args: z.record(z.string(), z.unknown()).optional()
|
|
77
|
+
})
|
|
78
|
+
).nullable();
|
|
79
|
+
var validateOnSchema = z.enum(["change", "blur", "submit"]).nullable();
|
|
80
|
+
var shadcnComponentDefinitions = {
|
|
81
|
+
// ── Layout ────────────────────────────────────────────────────────────────
|
|
82
|
+
Card: {
|
|
83
|
+
props: z.object({
|
|
84
|
+
title: z.string().nullable(),
|
|
85
|
+
description: z.string().nullable(),
|
|
86
|
+
maxWidth: z.enum(["sm", "md", "lg", "full"]).nullable(),
|
|
87
|
+
centered: z.boolean().nullable(),
|
|
88
|
+
className: z.string().nullable().describe("Additional CSS classes")
|
|
89
|
+
}),
|
|
90
|
+
slots: ["default"],
|
|
91
|
+
description: "Container card for content sections. Use for forms/content boxes, NOT for page headers.",
|
|
92
|
+
example: { title: "Overview", description: "Your account summary" }
|
|
93
|
+
},
|
|
94
|
+
Stack: {
|
|
95
|
+
props: z.object({
|
|
96
|
+
direction: z.enum(["horizontal", "vertical"]).nullable(),
|
|
97
|
+
gap: z.enum(["none", "sm", "md", "lg", "xl"]).nullable(),
|
|
98
|
+
align: z.enum(["start", "center", "end", "stretch"]).nullable(),
|
|
99
|
+
justify: z.enum(["start", "center", "end", "between", "around"]).nullable(),
|
|
100
|
+
className: z.string().nullable().describe("Additional CSS classes")
|
|
101
|
+
}),
|
|
102
|
+
slots: ["default"],
|
|
103
|
+
description: "Flex container for layouts",
|
|
104
|
+
example: { direction: "vertical", gap: "md" }
|
|
105
|
+
},
|
|
106
|
+
Grid: {
|
|
107
|
+
props: z.object({
|
|
108
|
+
columns: z.number().nullable(),
|
|
109
|
+
gap: z.enum(["sm", "md", "lg", "xl"]).nullable(),
|
|
110
|
+
className: z.string().nullable().describe("Additional CSS classes")
|
|
111
|
+
}),
|
|
112
|
+
slots: ["default"],
|
|
113
|
+
description: "Grid layout (1-6 columns)",
|
|
114
|
+
example: { columns: 3, gap: "md" }
|
|
115
|
+
},
|
|
116
|
+
Separator: {
|
|
117
|
+
props: z.object({
|
|
118
|
+
orientation: z.enum(["horizontal", "vertical"]).nullable()
|
|
119
|
+
}),
|
|
120
|
+
description: "Visual separator line"
|
|
121
|
+
},
|
|
122
|
+
Collapsible: {
|
|
123
|
+
props: z.object({
|
|
124
|
+
title: z.string(),
|
|
125
|
+
defaultOpen: z.boolean().nullable()
|
|
126
|
+
}),
|
|
127
|
+
slots: ["default"],
|
|
128
|
+
description: "Collapsible section with trigger. Children render inside."
|
|
129
|
+
},
|
|
130
|
+
// ── Content ───────────────────────────────────────────────────────────────
|
|
131
|
+
Heading: {
|
|
132
|
+
props: z.object({
|
|
133
|
+
text: z.string(),
|
|
134
|
+
level: z.enum(["h1", "h2", "h3", "h4"]).nullable()
|
|
135
|
+
}),
|
|
136
|
+
description: "Heading text (h1-h4)",
|
|
137
|
+
example: { text: "Welcome", level: "h1" }
|
|
138
|
+
},
|
|
139
|
+
Text: {
|
|
140
|
+
props: z.object({
|
|
141
|
+
text: z.string(),
|
|
142
|
+
variant: z.enum(["body", "caption", "muted", "lead", "code"]).nullable()
|
|
143
|
+
}),
|
|
144
|
+
description: "Paragraph text",
|
|
145
|
+
example: { text: "Hello, world!" }
|
|
146
|
+
},
|
|
147
|
+
Avatar: {
|
|
148
|
+
props: z.object({
|
|
149
|
+
src: z.string().nullable(),
|
|
150
|
+
name: z.string(),
|
|
151
|
+
size: z.enum(["sm", "md", "lg"]).nullable()
|
|
152
|
+
}),
|
|
153
|
+
description: "User avatar with fallback initials",
|
|
154
|
+
example: { name: "Jane Doe", size: "md" }
|
|
155
|
+
},
|
|
156
|
+
Badge: {
|
|
157
|
+
props: z.object({
|
|
158
|
+
text: z.string(),
|
|
159
|
+
variant: z.enum(["default", "secondary", "destructive", "outline"]).nullable()
|
|
160
|
+
}),
|
|
161
|
+
description: "Status badge",
|
|
162
|
+
example: { text: "Active", variant: "default" }
|
|
163
|
+
},
|
|
164
|
+
Alert: {
|
|
165
|
+
props: z.object({
|
|
166
|
+
title: z.string(),
|
|
167
|
+
message: z.string().nullable(),
|
|
168
|
+
type: z.enum(["info", "success", "warning", "error"]).nullable()
|
|
169
|
+
}),
|
|
170
|
+
description: "Alert banner",
|
|
171
|
+
example: {
|
|
172
|
+
title: "Note",
|
|
173
|
+
message: "Your changes have been saved.",
|
|
174
|
+
type: "success"
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
Progress: {
|
|
178
|
+
props: z.object({
|
|
179
|
+
value: z.number(),
|
|
180
|
+
max: z.number().nullable(),
|
|
181
|
+
label: z.string().nullable()
|
|
182
|
+
}),
|
|
183
|
+
description: "Progress bar (value 0-100)",
|
|
184
|
+
example: { value: 65, max: 100, label: "Upload progress" }
|
|
185
|
+
},
|
|
186
|
+
// ── Inputs ────────────────────────────────────────────────────────────────
|
|
187
|
+
Input: {
|
|
188
|
+
props: z.object({
|
|
189
|
+
label: z.string(),
|
|
190
|
+
name: z.string(),
|
|
191
|
+
type: z.enum(["text", "email", "password", "number"]).nullable(),
|
|
192
|
+
placeholder: z.string().nullable(),
|
|
193
|
+
value: z.string().nullable(),
|
|
194
|
+
checks: validationCheckSchema,
|
|
195
|
+
validateOn: validateOnSchema
|
|
196
|
+
}),
|
|
197
|
+
events: ["submit", "focus", "blur"],
|
|
198
|
+
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).",
|
|
199
|
+
example: {
|
|
200
|
+
label: "Email",
|
|
201
|
+
name: "email",
|
|
202
|
+
type: "email",
|
|
203
|
+
placeholder: "you@example.com"
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
Textarea: {
|
|
207
|
+
props: z.object({
|
|
208
|
+
label: z.string(),
|
|
209
|
+
name: z.string(),
|
|
210
|
+
placeholder: z.string().nullable(),
|
|
211
|
+
rows: z.number().nullable(),
|
|
212
|
+
value: z.string().nullable(),
|
|
213
|
+
checks: validationCheckSchema,
|
|
214
|
+
validateOn: validateOnSchema
|
|
215
|
+
}),
|
|
216
|
+
description: "Multi-line text input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: blur)."
|
|
217
|
+
},
|
|
218
|
+
Select: {
|
|
219
|
+
props: z.object({
|
|
220
|
+
label: z.string(),
|
|
221
|
+
name: z.string(),
|
|
222
|
+
options: z.array(z.string().min(1)),
|
|
223
|
+
placeholder: z.string().nullable(),
|
|
224
|
+
value: z.string().nullable(),
|
|
225
|
+
checks: validationCheckSchema,
|
|
226
|
+
validateOn: validateOnSchema
|
|
227
|
+
}),
|
|
228
|
+
events: ["change"],
|
|
229
|
+
description: "Dropdown select input. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change)."
|
|
230
|
+
},
|
|
231
|
+
Radio: {
|
|
232
|
+
props: z.object({
|
|
233
|
+
label: z.string(),
|
|
234
|
+
name: z.string(),
|
|
235
|
+
options: z.array(z.string().min(1)),
|
|
236
|
+
value: z.string().nullable(),
|
|
237
|
+
checks: validationCheckSchema,
|
|
238
|
+
validateOn: validateOnSchema
|
|
239
|
+
}),
|
|
240
|
+
events: ["change"],
|
|
241
|
+
description: "Radio button group. Use { $bindState } on value for binding. Use checks for validation. validateOn controls timing (default: change)."
|
|
242
|
+
},
|
|
243
|
+
Checkbox: {
|
|
244
|
+
props: z.object({
|
|
245
|
+
label: z.string(),
|
|
246
|
+
name: z.string(),
|
|
247
|
+
checked: z.boolean().nullable(),
|
|
248
|
+
checks: validationCheckSchema,
|
|
249
|
+
validateOn: validateOnSchema
|
|
250
|
+
}),
|
|
251
|
+
events: ["change"],
|
|
252
|
+
description: "Checkbox input. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change)."
|
|
253
|
+
},
|
|
254
|
+
Switch: {
|
|
255
|
+
props: z.object({
|
|
256
|
+
label: z.string(),
|
|
257
|
+
name: z.string(),
|
|
258
|
+
checked: z.boolean().nullable(),
|
|
259
|
+
checks: validationCheckSchema,
|
|
260
|
+
validateOn: validateOnSchema
|
|
261
|
+
}),
|
|
262
|
+
events: ["change"],
|
|
263
|
+
description: "Toggle switch. Use { $bindState } on checked for binding. Use checks for validation. validateOn controls timing (default: change)."
|
|
264
|
+
},
|
|
265
|
+
// ── Actions ───────────────────────────────────────────────────────────────
|
|
266
|
+
Button: {
|
|
267
|
+
props: z.object({
|
|
268
|
+
label: z.string(),
|
|
269
|
+
variant: z.enum(["primary", "secondary", "danger"]).nullable(),
|
|
270
|
+
disabled: z.boolean().nullable()
|
|
271
|
+
}),
|
|
272
|
+
events: ["press"],
|
|
273
|
+
description: "Clickable button. Bind on.press for handler.",
|
|
274
|
+
example: { label: "Submit", variant: "primary" }
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
// src/generative/components.ts
|
|
279
|
+
var shadcn = shadcnComponentDefinitions;
|
|
280
|
+
var PICKED_SHADCN = [
|
|
281
|
+
"Card",
|
|
282
|
+
"Stack",
|
|
283
|
+
"Grid",
|
|
284
|
+
"Separator",
|
|
285
|
+
"Heading",
|
|
286
|
+
"Text",
|
|
287
|
+
"Badge",
|
|
288
|
+
"Alert",
|
|
289
|
+
"Collapsible",
|
|
290
|
+
"Progress",
|
|
291
|
+
"Avatar",
|
|
292
|
+
"Input",
|
|
293
|
+
"Textarea",
|
|
294
|
+
"Select",
|
|
295
|
+
"Radio",
|
|
296
|
+
"Checkbox",
|
|
297
|
+
"Switch",
|
|
298
|
+
"Button"
|
|
299
|
+
];
|
|
300
|
+
var CUSTOM_COMPONENTS = [
|
|
301
|
+
"Split",
|
|
302
|
+
"Tabs",
|
|
303
|
+
"Steps",
|
|
304
|
+
"Icon",
|
|
305
|
+
"Segmented",
|
|
306
|
+
"NumberInput",
|
|
307
|
+
"MthdsField",
|
|
308
|
+
"MthdsResult",
|
|
309
|
+
"DataTable",
|
|
310
|
+
"Metric"
|
|
311
|
+
];
|
|
312
|
+
var METRIC_FORMATS = ["plain", "integer", "decimal", "compact"];
|
|
313
|
+
var ICON_NAMES = [
|
|
314
|
+
"Sparkles",
|
|
315
|
+
"Rocket",
|
|
316
|
+
"Compass",
|
|
317
|
+
"Map",
|
|
318
|
+
"MapPin",
|
|
319
|
+
"Route",
|
|
320
|
+
"Plane",
|
|
321
|
+
"Train",
|
|
322
|
+
"Car",
|
|
323
|
+
"Ship",
|
|
324
|
+
"Hotel",
|
|
325
|
+
"Home",
|
|
326
|
+
"Building2",
|
|
327
|
+
"Landmark",
|
|
328
|
+
"Globe",
|
|
329
|
+
"Mountain",
|
|
330
|
+
"Leaf",
|
|
331
|
+
"Sun",
|
|
332
|
+
"Moon",
|
|
333
|
+
"Calendar",
|
|
334
|
+
"Clock",
|
|
335
|
+
"Hourglass",
|
|
336
|
+
"Users",
|
|
337
|
+
"User",
|
|
338
|
+
"Baby",
|
|
339
|
+
"Accessibility",
|
|
340
|
+
"Heart",
|
|
341
|
+
"Star",
|
|
342
|
+
"Utensils",
|
|
343
|
+
"Coffee",
|
|
344
|
+
"Music",
|
|
345
|
+
"Camera",
|
|
346
|
+
"Image",
|
|
347
|
+
"Ticket",
|
|
348
|
+
"Gift",
|
|
349
|
+
"Wallet",
|
|
350
|
+
"Banknote",
|
|
351
|
+
"Coins",
|
|
352
|
+
"Receipt",
|
|
353
|
+
"Percent",
|
|
354
|
+
"Hash",
|
|
355
|
+
"TrendingUp",
|
|
356
|
+
"BarChart3",
|
|
357
|
+
"FileText",
|
|
358
|
+
"Upload",
|
|
359
|
+
"Paperclip",
|
|
360
|
+
"Mail",
|
|
361
|
+
"Phone",
|
|
362
|
+
"Tag",
|
|
363
|
+
"Layers",
|
|
364
|
+
"ListChecks",
|
|
365
|
+
"CheckCircle2",
|
|
366
|
+
"AlertCircle",
|
|
367
|
+
"Info",
|
|
368
|
+
"Shield",
|
|
369
|
+
"Flag",
|
|
370
|
+
"Bookmark",
|
|
371
|
+
"Briefcase",
|
|
372
|
+
"Package",
|
|
373
|
+
"Truck",
|
|
374
|
+
"Lightbulb",
|
|
375
|
+
"Zap",
|
|
376
|
+
"Pencil",
|
|
377
|
+
"Send"
|
|
378
|
+
];
|
|
379
|
+
var components = {
|
|
380
|
+
// ── Layout (shadcn) ────────────────────────────────────────────────────
|
|
381
|
+
Card: {
|
|
382
|
+
...shadcn.Card,
|
|
383
|
+
props: shadcn.Card.props.omit({ className: true, maxWidth: true, centered: true }),
|
|
384
|
+
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.'
|
|
385
|
+
},
|
|
386
|
+
Stack: { ...shadcn.Stack, props: shadcn.Stack.props.omit({ className: true }) },
|
|
387
|
+
Grid: { ...shadcn.Grid, props: shadcn.Grid.props.omit({ className: true }) },
|
|
388
|
+
Separator: shadcn.Separator,
|
|
389
|
+
Collapsible: shadcn.Collapsible,
|
|
390
|
+
// ── Content (shadcn) ───────────────────────────────────────────────────
|
|
391
|
+
Heading: shadcn.Heading,
|
|
392
|
+
Text: shadcn.Text,
|
|
393
|
+
Badge: shadcn.Badge,
|
|
394
|
+
Alert: shadcn.Alert,
|
|
395
|
+
Progress: shadcn.Progress,
|
|
396
|
+
Avatar: {
|
|
397
|
+
...shadcn.Avatar,
|
|
398
|
+
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".`
|
|
399
|
+
},
|
|
400
|
+
// ── Inputs (shadcn) ────────────────────────────────────────────────────
|
|
401
|
+
Input: shadcn.Input,
|
|
402
|
+
Textarea: shadcn.Textarea,
|
|
403
|
+
Select: shadcn.Select,
|
|
404
|
+
Radio: shadcn.Radio,
|
|
405
|
+
Checkbox: shadcn.Checkbox,
|
|
406
|
+
Switch: shadcn.Switch,
|
|
407
|
+
Button: shadcn.Button,
|
|
408
|
+
// ── Layout (ours) ──────────────────────────────────────────────────────
|
|
409
|
+
Split: {
|
|
410
|
+
props: z.object({
|
|
411
|
+
ratio: z.enum(["1:1", "1:2", "2:1"]).nullable(),
|
|
412
|
+
gap: z.enum(["md", "lg", "xl"]).nullable()
|
|
413
|
+
}),
|
|
414
|
+
slots: ["default"],
|
|
415
|
+
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.',
|
|
416
|
+
example: { ratio: "1:2", gap: "lg" }
|
|
417
|
+
},
|
|
418
|
+
Tabs: {
|
|
419
|
+
props: z.object({
|
|
420
|
+
tabs: z.array(z.object({ label: z.string(), value: z.string() })).min(2)
|
|
421
|
+
}),
|
|
422
|
+
slots: ["default"],
|
|
423
|
+
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.",
|
|
424
|
+
example: {
|
|
425
|
+
tabs: [
|
|
426
|
+
{ label: "The trip", value: "trip" },
|
|
427
|
+
{ label: "Who is going", value: "people" }
|
|
428
|
+
]
|
|
429
|
+
}
|
|
430
|
+
},
|
|
431
|
+
Steps: {
|
|
432
|
+
props: z.object({
|
|
433
|
+
steps: z.array(z.string()).min(2),
|
|
434
|
+
nextLabel: z.string().nullable(),
|
|
435
|
+
backLabel: z.string().nullable()
|
|
436
|
+
}),
|
|
437
|
+
slots: ["default"],
|
|
438
|
+
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.",
|
|
439
|
+
example: { steps: ["Where", "Who", "How"], nextLabel: "Continue" }
|
|
440
|
+
},
|
|
441
|
+
Icon: {
|
|
442
|
+
props: z.object({
|
|
443
|
+
name: z.enum(ICON_NAMES),
|
|
444
|
+
size: z.enum(["sm", "md", "lg"]).nullable()
|
|
445
|
+
}),
|
|
446
|
+
slots: [],
|
|
447
|
+
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.",
|
|
448
|
+
example: { name: "Plane", size: "md" }
|
|
449
|
+
},
|
|
450
|
+
// ── Inputs (ours) ──────────────────────────────────────────────────────
|
|
451
|
+
Segmented: {
|
|
452
|
+
props: z.object({
|
|
453
|
+
label: z.string(),
|
|
454
|
+
name: z.string(),
|
|
455
|
+
options: z.array(z.string().min(1)).min(2).max(6),
|
|
456
|
+
value: z.string().nullable()
|
|
457
|
+
}),
|
|
458
|
+
slots: [],
|
|
459
|
+
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/..." }.`,
|
|
460
|
+
example: {
|
|
461
|
+
label: "Pace",
|
|
462
|
+
name: "pace",
|
|
463
|
+
options: ["slow", "balanced", "packed"],
|
|
464
|
+
value: { $bindState: "/inputs/request/pace" }
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
NumberInput: {
|
|
468
|
+
props: z.object({
|
|
469
|
+
label: z.string(),
|
|
470
|
+
name: z.string(),
|
|
471
|
+
value: z.number().nullable(),
|
|
472
|
+
placeholder: z.string().nullable(),
|
|
473
|
+
unit: z.string().nullable(),
|
|
474
|
+
min: z.number().nullable(),
|
|
475
|
+
max: z.number().nullable(),
|
|
476
|
+
step: z.number().nullable()
|
|
477
|
+
}),
|
|
478
|
+
slots: [],
|
|
479
|
+
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.',
|
|
480
|
+
example: {
|
|
481
|
+
label: "Budget",
|
|
482
|
+
name: "budget",
|
|
483
|
+
value: { $bindState: "/inputs/request/budget" },
|
|
484
|
+
placeholder: "2500"
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
// ── The escape hatches and the bound equivalents (ours) ────────────────
|
|
488
|
+
MthdsField: {
|
|
489
|
+
props: z.object({
|
|
490
|
+
path: z.string()
|
|
491
|
+
}),
|
|
492
|
+
slots: [],
|
|
493
|
+
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 its own label and description. Inside a repeat, "path" is the item's field name relative to the item ("lines"), never an $item expression.`,
|
|
494
|
+
example: { path: "/inputs/source" }
|
|
495
|
+
},
|
|
496
|
+
MthdsResult: {
|
|
497
|
+
props: z.object({
|
|
498
|
+
path: z.string(),
|
|
499
|
+
hideLabel: z.boolean().nullable()
|
|
500
|
+
}),
|
|
501
|
+
slots: [],
|
|
502
|
+
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.`,
|
|
503
|
+
example: { path: "/result/issued_on" }
|
|
504
|
+
},
|
|
505
|
+
DataTable: {
|
|
506
|
+
props: z.object({
|
|
507
|
+
rows: z.any(),
|
|
508
|
+
columns: z.array(z.object({ path: z.string(), label: z.string() })),
|
|
509
|
+
caption: z.string().nullable()
|
|
510
|
+
}),
|
|
511
|
+
slots: [],
|
|
512
|
+
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") and the header to show for it.',
|
|
513
|
+
example: {
|
|
514
|
+
rows: { $state: "/result/lines" },
|
|
515
|
+
columns: [
|
|
516
|
+
{ path: "label", label: "Item" },
|
|
517
|
+
{ path: "quantity", label: "Qty" }
|
|
518
|
+
]
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
Metric: {
|
|
522
|
+
props: z.object({
|
|
523
|
+
label: z.string(),
|
|
524
|
+
value: z.any(),
|
|
525
|
+
unit: z.string().nullable(),
|
|
526
|
+
format: z.enum(METRIC_FORMATS).nullable()
|
|
527
|
+
}),
|
|
528
|
+
slots: [],
|
|
529
|
+
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.',
|
|
530
|
+
example: { label: "Total", value: { $state: "/result/total" }, unit: "EUR", format: "decimal" }
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
var actions = {
|
|
534
|
+
run: {
|
|
535
|
+
params: z.object({}),
|
|
536
|
+
description: "Start the run with the current /inputs. The host handles it; an input page binds it to its one Button after validateForm."
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
defineCatalog(generativeSchema, { components, actions });
|
|
540
|
+
var generativeComponents = components;
|
|
541
|
+
var generativeActions = actions;
|
|
542
|
+
|
|
543
|
+
// src/generative/catalog.ts
|
|
544
|
+
var PRODUCT_COMPONENTS = [
|
|
545
|
+
"AppBar",
|
|
546
|
+
"Hero",
|
|
547
|
+
"Workspace",
|
|
548
|
+
"Section",
|
|
549
|
+
"Rail",
|
|
550
|
+
"SummaryRow",
|
|
551
|
+
"Cta",
|
|
552
|
+
"Footer"
|
|
553
|
+
];
|
|
554
|
+
var product = {
|
|
555
|
+
AppBar: {
|
|
556
|
+
props: z.object({
|
|
557
|
+
app: z.string(),
|
|
558
|
+
links: z.array(z.string()).nullable(),
|
|
559
|
+
tag: z.string().nullable()
|
|
560
|
+
}),
|
|
561
|
+
slots: [],
|
|
562
|
+
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.",
|
|
563
|
+
example: { app: "Trip planner", links: ["Methods", "Runs"], tag: "plan_trip" }
|
|
564
|
+
},
|
|
565
|
+
Hero: {
|
|
566
|
+
props: z.object({
|
|
567
|
+
headline: z.string(),
|
|
568
|
+
lede: z.string().nullable(),
|
|
569
|
+
eyebrow: z.string().nullable()
|
|
570
|
+
}),
|
|
571
|
+
slots: [],
|
|
572
|
+
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.",
|
|
573
|
+
example: { headline: "Plan a trip worth taking.", lede: "Tell us where and who is coming." }
|
|
574
|
+
},
|
|
575
|
+
Workspace: {
|
|
576
|
+
props: z.object({ rail: z.enum(["right", "left"]).nullable() }),
|
|
577
|
+
slots: ["default"],
|
|
578
|
+
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.',
|
|
579
|
+
example: { rail: "right" }
|
|
580
|
+
},
|
|
581
|
+
Section: {
|
|
582
|
+
props: z.object({
|
|
583
|
+
number: z.string().nullable(),
|
|
584
|
+
title: z.string(),
|
|
585
|
+
lede: z.string().nullable()
|
|
586
|
+
}),
|
|
587
|
+
slots: ["default"],
|
|
588
|
+
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.",
|
|
589
|
+
example: { number: "01", title: "Where and when" }
|
|
590
|
+
},
|
|
591
|
+
Rail: {
|
|
592
|
+
props: z.object({ title: z.string() }),
|
|
593
|
+
slots: ["default"],
|
|
594
|
+
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.",
|
|
595
|
+
example: { title: "Your trip" }
|
|
596
|
+
},
|
|
597
|
+
SummaryRow: {
|
|
598
|
+
props: z.object({
|
|
599
|
+
label: z.string(),
|
|
600
|
+
value: z.any().nullable(),
|
|
601
|
+
detail: z.any().nullable(),
|
|
602
|
+
separator: z.string().nullable(),
|
|
603
|
+
placeholder: z.string().nullable()
|
|
604
|
+
}),
|
|
605
|
+
slots: [],
|
|
606
|
+
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.',
|
|
607
|
+
example: {
|
|
608
|
+
label: "Budget",
|
|
609
|
+
value: { $state: "/inputs/request/budget" },
|
|
610
|
+
detail: { $state: "/inputs/request/currency" }
|
|
611
|
+
}
|
|
612
|
+
},
|
|
613
|
+
Cta: {
|
|
614
|
+
props: z.object({ label: z.string(), hint: z.string().nullable() }),
|
|
615
|
+
slots: [],
|
|
616
|
+
events: ["press"],
|
|
617
|
+
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.",
|
|
618
|
+
example: { label: "Plan my trip", hint: "Nothing runs until the request is complete." }
|
|
619
|
+
},
|
|
620
|
+
Footer: {
|
|
621
|
+
props: z.object({ text: z.string(), tag: z.string().nullable() }),
|
|
622
|
+
slots: [],
|
|
623
|
+
description: "A closing line, muted, with an optional mono tag at the right. It renders as the page's contentinfo, so at most one.",
|
|
624
|
+
example: { text: "Runs on Pipelex.", tag: "MTHDS" }
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
var COMPONENT_RENDERINGS = {
|
|
628
|
+
Hero: { heading: { level: 1 } },
|
|
629
|
+
Section: { heading: { level: 2 } },
|
|
630
|
+
Rail: { heading: { level: 2 } },
|
|
631
|
+
Card: { heading: { level: 3, when: "title" } },
|
|
632
|
+
AppBar: { once: "the page's banner" },
|
|
633
|
+
Footer: { once: "the page's contentinfo" },
|
|
634
|
+
Split: { children: { count: 2, roles: "left, right" } },
|
|
635
|
+
Workspace: { children: { count: 2, roles: "work, rail" } },
|
|
636
|
+
Tabs: { panels: { prop: "tabs", noun: "tab" } },
|
|
637
|
+
Steps: { panels: { prop: "steps", noun: "step" } }
|
|
638
|
+
};
|
|
639
|
+
var catalog = Object.assign(
|
|
640
|
+
defineCatalog(generativeSchema, {
|
|
641
|
+
components: { ...generativeComponents, ...product },
|
|
642
|
+
actions: generativeActions
|
|
643
|
+
}),
|
|
644
|
+
{ renders: COMPONENT_RENDERINGS }
|
|
645
|
+
);
|
|
646
|
+
var COMPONENT_NAMES = [
|
|
647
|
+
...PICKED_SHADCN,
|
|
648
|
+
...CUSTOM_COMPONENTS,
|
|
649
|
+
...PRODUCT_COMPONENTS
|
|
650
|
+
];
|
|
651
|
+
|
|
652
|
+
// src/generative/props-signature.ts
|
|
653
|
+
function definitionOf(schema) {
|
|
654
|
+
return schema._zod.def;
|
|
655
|
+
}
|
|
656
|
+
function propsSignature(schema) {
|
|
657
|
+
const def = definitionOf(schema);
|
|
658
|
+
switch (def.type) {
|
|
659
|
+
case "string":
|
|
660
|
+
return "string";
|
|
661
|
+
case "number":
|
|
662
|
+
return "number";
|
|
663
|
+
case "boolean":
|
|
664
|
+
return "boolean";
|
|
665
|
+
case "literal":
|
|
666
|
+
return JSON.stringify(def.values?.[0]);
|
|
667
|
+
case "enum":
|
|
668
|
+
return Object.values(def.entries ?? {}).map((value) => `"${String(value)}"`).join(" | ");
|
|
669
|
+
case "array":
|
|
670
|
+
return `Array<${def.element ? propsSignature(def.element) : "unknown"}>`;
|
|
671
|
+
case "object": {
|
|
672
|
+
const props = Object.entries(def.shape ?? {}).map(([key, value]) => {
|
|
673
|
+
const inner = definitionOf(value).type;
|
|
674
|
+
const optional = inner === "optional" || inner === "nullable";
|
|
675
|
+
return `${key}${optional ? "?" : ""}: ${propsSignature(value)}`;
|
|
676
|
+
});
|
|
677
|
+
return `{ ${props.join(", ")} }`;
|
|
678
|
+
}
|
|
679
|
+
case "optional":
|
|
680
|
+
case "nullable":
|
|
681
|
+
case "default":
|
|
682
|
+
return def.innerType ? propsSignature(def.innerType) : "unknown";
|
|
683
|
+
case "union":
|
|
684
|
+
return def.options ? def.options.map((option) => propsSignature(option)).join(" | ") : "unknown";
|
|
685
|
+
case "record": {
|
|
686
|
+
const key = def.keyType ? propsSignature(def.keyType) : "string";
|
|
687
|
+
const value = def.valueType ? propsSignature(def.valueType) : "unknown";
|
|
688
|
+
return `Record<${key}, ${value}>`;
|
|
689
|
+
}
|
|
690
|
+
default:
|
|
691
|
+
return "unknown";
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// src/generative/designer-catalog.ts
|
|
696
|
+
var DESIGNER_CATALOG_CONCEPT = "generative.Catalog";
|
|
697
|
+
function designerCatalog() {
|
|
698
|
+
const definitions = catalog.data.components;
|
|
699
|
+
const components3 = COMPONENT_NAMES.map((name) => {
|
|
700
|
+
const definition = definitions[name];
|
|
701
|
+
if (!definition) throw new Error(`the catalog defines no component named '${name}'`);
|
|
702
|
+
const slots = definition.slots ?? [];
|
|
703
|
+
return {
|
|
704
|
+
name,
|
|
705
|
+
props: propsSignature(definition.props),
|
|
706
|
+
description: definition.description,
|
|
707
|
+
accepts_children: slots.includes("default"),
|
|
708
|
+
slots: slots.filter((slot) => slot !== "default"),
|
|
709
|
+
events: [...definition.events ?? []]
|
|
710
|
+
};
|
|
711
|
+
});
|
|
712
|
+
const builtIn = (generativeSchema.builtInActions ?? []).map((action) => ({
|
|
713
|
+
name: action.name,
|
|
714
|
+
description: action.description,
|
|
715
|
+
built_in: true
|
|
716
|
+
}));
|
|
717
|
+
const own = Object.entries(catalog.data.actions).map(
|
|
718
|
+
([name, action]) => ({
|
|
719
|
+
name,
|
|
720
|
+
description: action.description,
|
|
721
|
+
built_in: false
|
|
722
|
+
})
|
|
723
|
+
);
|
|
724
|
+
return { components: components3, actions: [...builtIn, ...own] };
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// src/generative/prompt-hash.ts
|
|
728
|
+
var PROMPT_HASH = "15d195df65f3";
|
|
729
|
+
|
|
730
|
+
// src/generative/paths.ts
|
|
731
|
+
var INPUTS_ROOT = "/inputs";
|
|
732
|
+
var RESULT_ROOT = "/result";
|
|
733
|
+
function joinPath(root, ...segments) {
|
|
734
|
+
return segments.reduce((path, segment) => `${path}/${escapeSegment(segment)}`, root);
|
|
735
|
+
}
|
|
736
|
+
function escapeSegment(segment) {
|
|
737
|
+
return segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
738
|
+
}
|
|
739
|
+
function unescapeSegment(segment) {
|
|
740
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
741
|
+
}
|
|
742
|
+
function segmentsUnder(root, path) {
|
|
743
|
+
if (path === root) return [];
|
|
744
|
+
if (!path.startsWith(`${root}/`)) return void 0;
|
|
745
|
+
return path.slice(root.length + 1).split("/").map(unescapeSegment);
|
|
746
|
+
}
|
|
747
|
+
function fieldAtSegments(fields, segments) {
|
|
748
|
+
const [head, ...rest] = segments;
|
|
749
|
+
if (head === void 0) return void 0;
|
|
750
|
+
const field = fields.find((candidate) => candidate.name === head);
|
|
751
|
+
if (!field) return void 0;
|
|
752
|
+
return rest.length === 0 ? field : descend(field, rest);
|
|
753
|
+
}
|
|
754
|
+
function descend(field, segments) {
|
|
755
|
+
const [head, ...rest] = segments;
|
|
756
|
+
if (head === void 0) return field;
|
|
757
|
+
if (field.kind === "object") return fieldAtSegments(field.fields, segments);
|
|
758
|
+
if (field.kind === "list" && /^\d+$/.test(head)) return descend(field.item, rest);
|
|
759
|
+
return void 0;
|
|
760
|
+
}
|
|
761
|
+
function inputFieldAtPath(fields, path) {
|
|
762
|
+
const segments = segmentsUnder(INPUTS_ROOT, path);
|
|
763
|
+
return segments && segments.length > 0 ? fieldAtSegments(fields, segments) : void 0;
|
|
764
|
+
}
|
|
765
|
+
function resultFieldAtPath(field, path) {
|
|
766
|
+
const segments = segmentsUnder(RESULT_ROOT, path);
|
|
767
|
+
if (!segments) return void 0;
|
|
768
|
+
return segments.length === 0 ? field : descend(field, segments);
|
|
769
|
+
}
|
|
770
|
+
function parentMapOf(spec) {
|
|
771
|
+
const parents = /* @__PURE__ */ new Map();
|
|
772
|
+
for (const [parentKey, element] of Object.entries(spec.elements)) {
|
|
773
|
+
for (const child of element.children ?? []) parents.set(child, parentKey);
|
|
774
|
+
}
|
|
775
|
+
return parents;
|
|
776
|
+
}
|
|
777
|
+
function repeatBasePathOf(spec, key, parents = parentMapOf(spec)) {
|
|
778
|
+
const chain = [];
|
|
779
|
+
const seen = /* @__PURE__ */ new Set();
|
|
780
|
+
let current = key;
|
|
781
|
+
while (current !== void 0 && !seen.has(current)) {
|
|
782
|
+
seen.add(current);
|
|
783
|
+
chain.unshift(current);
|
|
784
|
+
current = parents.get(current);
|
|
785
|
+
}
|
|
786
|
+
let base;
|
|
787
|
+
for (const elementKey of chain) {
|
|
788
|
+
const repeat = spec.elements[elementKey]?.repeat;
|
|
789
|
+
if (!repeat) continue;
|
|
790
|
+
const statePath = repeat.statePath;
|
|
791
|
+
if (typeof statePath === "string") {
|
|
792
|
+
base = `${statePath}/0`;
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
const item = statePath?.$item;
|
|
796
|
+
if (base === void 0 || typeof item !== "string") return void 0;
|
|
797
|
+
base = `${joinPath(base, item)}/0`;
|
|
798
|
+
}
|
|
799
|
+
return base;
|
|
800
|
+
}
|
|
801
|
+
function absoluteHatchPath(spec, key, path, parents) {
|
|
802
|
+
if (path.startsWith("/")) return path;
|
|
803
|
+
const base = repeatBasePathOf(spec, key, parents);
|
|
804
|
+
return base ? `${base}/${path}` : void 0;
|
|
805
|
+
}
|
|
806
|
+
function repeatListPathOf(spec, key, parents = parentMapOf(spec)) {
|
|
807
|
+
const statePath = spec.elements[key]?.repeat?.statePath;
|
|
808
|
+
if (typeof statePath === "string") return statePath;
|
|
809
|
+
const item = statePath?.$item;
|
|
810
|
+
const parent = parents.get(key);
|
|
811
|
+
const outer = parent === void 0 ? void 0 : repeatBasePathOf(spec, parent, parents);
|
|
812
|
+
return outer !== void 0 && typeof item === "string" ? joinPath(outer, item) : void 0;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// src/generative/brief.ts
|
|
816
|
+
var RUN_COMPONENT = "Cta";
|
|
817
|
+
function isDelegatedInput(field) {
|
|
818
|
+
return field.kind === "document" || field.kind === "image" || field.kind === "date" || field.kind === "list" || field.kind === "unknown" || field.kind === "enum" && field.options.includes("");
|
|
819
|
+
}
|
|
820
|
+
function isDelegatedResult(field) {
|
|
821
|
+
if (isNativeHtmlNode(field) || isNativeDateNode(field) || isNativeCompositeNode(field)) {
|
|
822
|
+
return true;
|
|
823
|
+
}
|
|
824
|
+
return field.kind === "document" || field.kind === "image" || field.kind === "date" || field.kind === "prose" || field.kind === "unknown";
|
|
825
|
+
}
|
|
826
|
+
function kindLabel(field) {
|
|
827
|
+
if (isNativeHtmlNode(field)) return "markup (native.Html)";
|
|
828
|
+
if (isNativeDateNode(field)) return "date (native.Date)";
|
|
829
|
+
if (isNativeCompositeNode(field)) return "composite (native.Composite)";
|
|
830
|
+
switch (field.kind) {
|
|
831
|
+
case "text":
|
|
832
|
+
return "text";
|
|
833
|
+
case "prose":
|
|
834
|
+
return "prose (markdown)";
|
|
835
|
+
case "number":
|
|
836
|
+
return field.integer ? "integer" : "number";
|
|
837
|
+
case "boolean":
|
|
838
|
+
return "boolean";
|
|
839
|
+
case "enum":
|
|
840
|
+
return `choice of ${field.options.map((option) => JSON.stringify(option)).join(" | ")}`;
|
|
841
|
+
case "date":
|
|
842
|
+
return field.datetime ? "date with time" : "date";
|
|
843
|
+
case "document":
|
|
844
|
+
return "document (a file)";
|
|
845
|
+
case "image":
|
|
846
|
+
return "image (a file)";
|
|
847
|
+
case "object":
|
|
848
|
+
return `structure${field.conceptRef ? ` ${field.conceptRef}` : ""}`;
|
|
849
|
+
case "list":
|
|
850
|
+
return `list of ${kindLabel(field.item)}`;
|
|
851
|
+
case "unknown":
|
|
852
|
+
return "unknown";
|
|
853
|
+
default:
|
|
854
|
+
return field;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
function labelOf(field) {
|
|
858
|
+
return field.title && field.title !== field.name ? `${field.title} (\`${field.name}\`)` : `\`${field.name}\``;
|
|
859
|
+
}
|
|
860
|
+
function constraints(field) {
|
|
861
|
+
const notes = [];
|
|
862
|
+
if (field.kind === "number") {
|
|
863
|
+
if (field.min !== void 0) notes.push(`min ${field.min}`);
|
|
864
|
+
if (field.max !== void 0) notes.push(`max ${field.max}`);
|
|
865
|
+
}
|
|
866
|
+
if (field.kind === "text" || field.kind === "prose") {
|
|
867
|
+
if (field.minLength !== void 0) notes.push(`at least ${field.minLength} characters`);
|
|
868
|
+
if (field.maxLength !== void 0) notes.push(`at most ${field.maxLength} characters`);
|
|
869
|
+
if (field.format) notes.push(`format ${field.format}`);
|
|
870
|
+
}
|
|
871
|
+
if (field.kind === "list") {
|
|
872
|
+
if (field.itemCount !== void 0) notes.push(`at least ${field.itemCount} items`);
|
|
873
|
+
if (field.maxItemCount !== void 0) notes.push(`at most ${field.maxItemCount} items`);
|
|
874
|
+
}
|
|
875
|
+
if (field.defaultValue !== void 0) notes.push(`default ${JSON.stringify(field.defaultValue)}`);
|
|
876
|
+
if (field.examples && field.examples.length > 0) {
|
|
877
|
+
notes.push(`examples ${field.examples.map((example) => JSON.stringify(example)).join(", ")}`);
|
|
878
|
+
}
|
|
879
|
+
if (field.hints && Object.keys(field.hints).length > 0) {
|
|
880
|
+
notes.push(
|
|
881
|
+
`hints ${Object.entries(field.hints).map(([key, value]) => `${key}=${value}`).join(", ")}`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
return notes;
|
|
885
|
+
}
|
|
886
|
+
function describe(field, path, depth, side, lines) {
|
|
887
|
+
const delegated = side === "input" ? isDelegatedInput(field) : isDelegatedResult(field);
|
|
888
|
+
const parts = [`\`${path}\` \u2014 ${kindLabel(field)}`];
|
|
889
|
+
if (side === "input") {
|
|
890
|
+
parts.push(field.required ? "required" : "optional");
|
|
891
|
+
if (field.gating) parts.push("gates the run");
|
|
892
|
+
}
|
|
893
|
+
const head = parts.join(", ");
|
|
894
|
+
const tail = [labelOf(field), field.description ?? ""].filter(Boolean).join(": ");
|
|
895
|
+
const notes = constraints(field);
|
|
896
|
+
const delegation = delegated ? ` **[delegate: ${side === "input" ? "MthdsField" : "MthdsResult"}]**` : "";
|
|
897
|
+
lines.push({
|
|
898
|
+
depth,
|
|
899
|
+
text: `${head} \u2014 ${tail}${notes.length > 0 ? ` (${notes.join("; ")})` : ""}${delegation}`
|
|
900
|
+
});
|
|
901
|
+
if (delegated) return;
|
|
902
|
+
if (field.kind === "object") {
|
|
903
|
+
for (const child of field.fields) {
|
|
904
|
+
describe(child, joinPath(path, child.name), depth + 1, side, lines);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
if (field.kind === "list" && side === "result") {
|
|
908
|
+
const item = field.item;
|
|
909
|
+
if (item.kind === "object" && !isDelegatedResult(item)) {
|
|
910
|
+
lines.push({
|
|
911
|
+
depth: depth + 1,
|
|
912
|
+
text: `each item is a ${kindLabel(item)}${item.description ? ` (${item.description})` : ""}, with these members (paths relative to one item, for DataTable columns or $item):`
|
|
913
|
+
});
|
|
914
|
+
for (const child of item.fields) {
|
|
915
|
+
describeRelative(child, child.name, depth + 2, lines);
|
|
916
|
+
}
|
|
917
|
+
} else {
|
|
918
|
+
lines.push({ depth: depth + 1, text: `each item is a ${kindLabel(item)}` });
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
function describeRelative(field, relativePath, depth, lines) {
|
|
923
|
+
const notes = constraints(field);
|
|
924
|
+
lines.push({
|
|
925
|
+
depth,
|
|
926
|
+
text: `\`${relativePath}\` \u2014 ${kindLabel(field)} \u2014 ${[labelOf(field), field.description ?? ""].filter(Boolean).join(": ")}${notes.length > 0 ? ` (${notes.join("; ")})` : ""}${isDelegatedResult(field) ? " (a DataTable prints it as text; delegate the whole list to MthdsResult to render it properly)" : ""}`
|
|
927
|
+
});
|
|
928
|
+
if (field.kind === "object") {
|
|
929
|
+
for (const child of field.fields)
|
|
930
|
+
describeRelative(child, joinPath(relativePath, child.name), depth + 1, lines);
|
|
931
|
+
}
|
|
932
|
+
if (field.kind === "list" && field.item.kind === "object") {
|
|
933
|
+
lines.push({ depth: depth + 1, text: `each item is a ${kindLabel(field.item)}, with:` });
|
|
934
|
+
for (const child of field.item.fields) {
|
|
935
|
+
describeRelative(child, joinPath(relativePath, "<i>", child.name), depth + 2, lines);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
function render(lines) {
|
|
940
|
+
return lines.map((line) => `${" ".repeat(line.depth)}- ${line.text}`).join("\n");
|
|
941
|
+
}
|
|
942
|
+
function collectDelegated(fields, root, side) {
|
|
943
|
+
const out = [];
|
|
944
|
+
const walk = (field, path) => {
|
|
945
|
+
const delegated = side === "input" ? isDelegatedInput(field) : isDelegatedResult(field);
|
|
946
|
+
if (delegated) {
|
|
947
|
+
out.push(`\`${path}\` (${kindLabel(field)})`);
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (field.kind === "object") {
|
|
951
|
+
for (const child of field.fields) walk(child, joinPath(path, child.name));
|
|
952
|
+
}
|
|
953
|
+
};
|
|
954
|
+
for (const field of fields) walk(field, joinPath(root, field.name));
|
|
955
|
+
return out;
|
|
956
|
+
}
|
|
957
|
+
function collectDefaults(fields, root) {
|
|
958
|
+
const out = [];
|
|
959
|
+
const walk = (field, path) => {
|
|
960
|
+
if (field.defaultValue !== void 0) {
|
|
961
|
+
out.push(`\`${path}\` = ${JSON.stringify(field.defaultValue)}`);
|
|
962
|
+
return;
|
|
963
|
+
}
|
|
964
|
+
if (field.kind === "object") {
|
|
965
|
+
for (const child of field.fields) walk(child, joinPath(path, child.name));
|
|
966
|
+
}
|
|
967
|
+
};
|
|
968
|
+
for (const field of fields) walk(field, joinPath(root, field.name));
|
|
969
|
+
return out;
|
|
970
|
+
}
|
|
971
|
+
function renderInputBrief(subject, fields) {
|
|
972
|
+
const lines = [];
|
|
973
|
+
for (const field of fields) describe(field, joinPath(INPUTS_ROOT, field.name), 0, "input", lines);
|
|
974
|
+
const delegated = collectDelegated(fields, INPUTS_ROOT, "input");
|
|
975
|
+
const defaults = collectDefaults(fields, INPUTS_ROOT);
|
|
976
|
+
const gating = fields.filter((field) => field.gating).map((field) => `\`${field.name}\``);
|
|
977
|
+
return [
|
|
978
|
+
`# Input page: ${subject.pipeRef}`,
|
|
979
|
+
"",
|
|
980
|
+
subject.description ?? "A method with the inputs below.",
|
|
981
|
+
"",
|
|
982
|
+
...subject.name ? [
|
|
983
|
+
"## Name",
|
|
984
|
+
"",
|
|
985
|
+
`The method is called \xAB${subject.name}\xBB, as the host lists it. That is the app's name wherever the layout asks for one; invent no other. Links, if the layout carries any, name this page's own sections. The page states nothing the method does not: no promise about storage, privacy, speed, or what happens after the run.`,
|
|
986
|
+
""
|
|
987
|
+
] : [],
|
|
988
|
+
"## State",
|
|
989
|
+
"",
|
|
990
|
+
`The form's values live under \`${INPUTS_ROOT}\`, one member per input, exactly as listed. Bind each input with \`$bindState\` at its path; a structure's members bind at their own paths beneath it.`,
|
|
991
|
+
"",
|
|
992
|
+
render(lines),
|
|
993
|
+
"",
|
|
994
|
+
"## Delegated",
|
|
995
|
+
"",
|
|
996
|
+
delegated.length > 0 ? `Render these with \`MthdsField\` at the path, and nothing else:
|
|
997
|
+
|
|
998
|
+
${delegated.map((entry) => `- ${entry}`).join("\n")}` : "None: every input can be entered with the catalog's own inputs. `MthdsField` remains available for any you would rather not style.",
|
|
999
|
+
"",
|
|
1000
|
+
"## Defaults",
|
|
1001
|
+
"",
|
|
1002
|
+
defaults.length > 0 ? `Seed these, and only these, with \`/state\` patches (\`{"op":"add","path":"/state/inputs/...","value":...}\`):
|
|
1003
|
+
|
|
1004
|
+
${defaults.map((entry) => `- ${entry}`).join("\n")}` : "None. Emit no `/state` patches.",
|
|
1005
|
+
"",
|
|
1006
|
+
"## Run",
|
|
1007
|
+
"",
|
|
1008
|
+
gating.length > 0 ? `The run waits for ${gating.join(", ")}; say so near the ${RUN_COMPONENT}, briefly.` : "Nothing gates the run.",
|
|
1009
|
+
`The page has exactly one \`${RUN_COMPONENT}\`, \`on.press\` bound to \`validateForm\` then \`run\`; label it with what the method does, in a person's words.`,
|
|
1010
|
+
""
|
|
1011
|
+
].join("\n");
|
|
1012
|
+
}
|
|
1013
|
+
function renderResultBrief(subject, field, state) {
|
|
1014
|
+
const lines = [];
|
|
1015
|
+
const delegatedWhole = isDelegatedResult(field);
|
|
1016
|
+
if (field.kind === "object" && !delegatedWhole) {
|
|
1017
|
+
lines.push({
|
|
1018
|
+
depth: 0,
|
|
1019
|
+
text: `\`${RESULT_ROOT}\` \u2014 ${kindLabel(field)}${field.description ? `: ${field.description}` : ""}`
|
|
1020
|
+
});
|
|
1021
|
+
for (const child of field.fields) {
|
|
1022
|
+
describe(child, joinPath(RESULT_ROOT, child.name), 1, "result", lines);
|
|
1023
|
+
}
|
|
1024
|
+
} else {
|
|
1025
|
+
describe(field, RESULT_ROOT, 0, "result", lines);
|
|
1026
|
+
}
|
|
1027
|
+
const delegated = field.kind === "object" && !delegatedWhole ? collectDelegated(field.fields, RESULT_ROOT, "result") : delegatedWhole ? [`\`${RESULT_ROOT}\` (${kindLabel(field)})`] : [];
|
|
1028
|
+
return [
|
|
1029
|
+
`# Result page: ${subject.pipeRef}`,
|
|
1030
|
+
"",
|
|
1031
|
+
subject.description ?? "The result of a method, described below.",
|
|
1032
|
+
"",
|
|
1033
|
+
"## State",
|
|
1034
|
+
"",
|
|
1035
|
+
`The host loads the run's result under \`${RESULT_ROOT}\` before the page renders. Read every value with \`$state\`; emit no \`/state\` patches. Paths:`,
|
|
1036
|
+
"",
|
|
1037
|
+
render(lines),
|
|
1038
|
+
"",
|
|
1039
|
+
"## Delegated",
|
|
1040
|
+
"",
|
|
1041
|
+
delegated.length > 0 ? `Render these with \`MthdsResult\` at the path - they are kinds the catalog cannot show properly:
|
|
1042
|
+
|
|
1043
|
+
${delegated.map((entry) => `- ${entry}`).join("\n")}` : "None. `MthdsResult` remains available for any structure you choose not to lay out.",
|
|
1044
|
+
"",
|
|
1045
|
+
"## One real run, as loaded",
|
|
1046
|
+
"",
|
|
1047
|
+
"The state of one actual run, so you can judge magnitudes and lengths. Bind to the paths; do not copy these values into the spec.",
|
|
1048
|
+
"",
|
|
1049
|
+
"```json",
|
|
1050
|
+
JSON.stringify(state, null, 2),
|
|
1051
|
+
"```",
|
|
1052
|
+
""
|
|
1053
|
+
].join("\n");
|
|
1054
|
+
}
|
|
1055
|
+
var EXPRESSION_KEYS = [
|
|
1056
|
+
"$state",
|
|
1057
|
+
"$item",
|
|
1058
|
+
"$index",
|
|
1059
|
+
"$bindState",
|
|
1060
|
+
"$bindItem",
|
|
1061
|
+
"$cond",
|
|
1062
|
+
"$computed",
|
|
1063
|
+
"$template"
|
|
1064
|
+
];
|
|
1065
|
+
var HATCHES = /* @__PURE__ */ new Set(["MthdsField", "MthdsResult"]);
|
|
1066
|
+
function isExpression(value) {
|
|
1067
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.keys(value).some((key) => EXPRESSION_KEYS.includes(key));
|
|
1068
|
+
}
|
|
1069
|
+
function isClosedVocabulary(schema) {
|
|
1070
|
+
let current = schema;
|
|
1071
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
1072
|
+
const def = current.def;
|
|
1073
|
+
if (!def) return false;
|
|
1074
|
+
if (def.type === "enum" || def.type === "literal") return true;
|
|
1075
|
+
if (def.innerType === void 0) return false;
|
|
1076
|
+
current = def.innerType;
|
|
1077
|
+
}
|
|
1078
|
+
return false;
|
|
1079
|
+
}
|
|
1080
|
+
function definitionOf2(catalog2, type) {
|
|
1081
|
+
const components3 = catalog2.data.components;
|
|
1082
|
+
return hasOwnProp(components3, type) ? components3[type] : void 0;
|
|
1083
|
+
}
|
|
1084
|
+
function propsSchemaOf(catalog2, type) {
|
|
1085
|
+
const props = definitionOf2(catalog2, type)?.props;
|
|
1086
|
+
return props && typeof props === "object" && "shape" in props ? props : void 0;
|
|
1087
|
+
}
|
|
1088
|
+
function eventsOf(catalog2, type) {
|
|
1089
|
+
const definition = definitionOf2(catalog2, type);
|
|
1090
|
+
if (!definition) return void 0;
|
|
1091
|
+
return Array.isArray(definition.events) ? definition.events.filter((event) => typeof event === "string") : [];
|
|
1092
|
+
}
|
|
1093
|
+
var MAX_ELEMENT_DEPTH = 512;
|
|
1094
|
+
function childKeysOf(spec, key) {
|
|
1095
|
+
const element = spec.elements[key];
|
|
1096
|
+
if (!element) return [];
|
|
1097
|
+
const children = Array.isArray(element.children) ? element.children : [];
|
|
1098
|
+
const slots = typeof element.slots === "object" && element.slots !== null ? Object.values(element.slots).flatMap(
|
|
1099
|
+
(slot) => Array.isArray(slot) ? slot : []
|
|
1100
|
+
) : [];
|
|
1101
|
+
return [...children, ...slots].filter((child) => typeof child === "string");
|
|
1102
|
+
}
|
|
1103
|
+
function deepestChain(spec) {
|
|
1104
|
+
const below = /* @__PURE__ */ new Map();
|
|
1105
|
+
const onPath = /* @__PURE__ */ new Set();
|
|
1106
|
+
let deepest = 0;
|
|
1107
|
+
for (const start of Object.keys(spec.elements)) {
|
|
1108
|
+
if (below.has(start)) continue;
|
|
1109
|
+
const stack = [start];
|
|
1110
|
+
while (stack.length > 0) {
|
|
1111
|
+
const key = stack[stack.length - 1];
|
|
1112
|
+
if (below.has(key)) {
|
|
1113
|
+
onPath.delete(key);
|
|
1114
|
+
stack.pop();
|
|
1115
|
+
continue;
|
|
1116
|
+
}
|
|
1117
|
+
const children = childKeysOf(spec, key).filter((child) => !onPath.has(child));
|
|
1118
|
+
const pending = children.filter((child) => !below.has(child));
|
|
1119
|
+
if (pending.length > 0) {
|
|
1120
|
+
onPath.add(key);
|
|
1121
|
+
stack.push(...pending);
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
below.set(
|
|
1125
|
+
key,
|
|
1126
|
+
children.reduce((tallest, child) => Math.max(tallest, below.get(child) ?? 0), 0) + 1
|
|
1127
|
+
);
|
|
1128
|
+
onPath.delete(key);
|
|
1129
|
+
stack.pop();
|
|
1130
|
+
}
|
|
1131
|
+
deepest = Math.max(deepest, below.get(start) ?? 0);
|
|
1132
|
+
}
|
|
1133
|
+
return deepest;
|
|
1134
|
+
}
|
|
1135
|
+
function validateAgainstCatalog(spec, catalog2 = catalog) {
|
|
1136
|
+
try {
|
|
1137
|
+
return checkAgainstCatalog(spec, catalog2);
|
|
1138
|
+
} catch (error) {
|
|
1139
|
+
return {
|
|
1140
|
+
ok: false,
|
|
1141
|
+
problems: [{ message: `the layout is not a well-formed spec: ${String(error)}` }]
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
function checkAgainstCatalog(spec, catalog2) {
|
|
1146
|
+
const problems = [];
|
|
1147
|
+
const depth = deepestChain(spec);
|
|
1148
|
+
if (depth > MAX_ELEMENT_DEPTH) {
|
|
1149
|
+
return {
|
|
1150
|
+
ok: false,
|
|
1151
|
+
problems: [
|
|
1152
|
+
{
|
|
1153
|
+
message: `the element tree is ${depth} deep; this entry renders at most ${MAX_ELEMENT_DEPTH}.`
|
|
1154
|
+
}
|
|
1155
|
+
]
|
|
1156
|
+
};
|
|
1157
|
+
}
|
|
1158
|
+
const structure = validateSpec(spec, { checkOrphans: true });
|
|
1159
|
+
for (const issue of structure.issues) {
|
|
1160
|
+
if (issue.severity === "error" || issue.code === "orphaned_element") {
|
|
1161
|
+
problems.push({ elementKey: issue.elementKey, message: issue.message });
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
const cyclic = /* @__PURE__ */ new Set();
|
|
1165
|
+
const state = /* @__PURE__ */ new Map();
|
|
1166
|
+
const descend2 = (key) => {
|
|
1167
|
+
const mark = state.get(key);
|
|
1168
|
+
if (mark === "done") return;
|
|
1169
|
+
if (mark === "open") {
|
|
1170
|
+
cyclic.add(key);
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1173
|
+
state.set(key, "open");
|
|
1174
|
+
const element = spec.elements[key];
|
|
1175
|
+
for (const child of element?.children ?? []) descend2(child);
|
|
1176
|
+
for (const slot of Object.values(element?.slots ?? {}))
|
|
1177
|
+
for (const child of slot) descend2(child);
|
|
1178
|
+
state.set(key, "done");
|
|
1179
|
+
};
|
|
1180
|
+
for (const key of Object.keys(spec.elements)) descend2(key);
|
|
1181
|
+
for (const key of cyclic) {
|
|
1182
|
+
problems.push({
|
|
1183
|
+
elementKey: key,
|
|
1184
|
+
message: `"${key}" is its own descendant: an element tree may not contain a cycle.`
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
const known = new Set(catalog2.componentNames);
|
|
1188
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1189
|
+
if (!known.has(element.type)) {
|
|
1190
|
+
problems.push({ elementKey: key, message: `unknown component type "${element.type}"` });
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1194
|
+
const schema = propsSchemaOf(catalog2, element.type);
|
|
1195
|
+
if (!schema) continue;
|
|
1196
|
+
const shape = schema.shape;
|
|
1197
|
+
const props = element.props ?? {};
|
|
1198
|
+
for (const [name, value] of Object.entries(props)) {
|
|
1199
|
+
const propSchema = shape[name];
|
|
1200
|
+
if (!propSchema) {
|
|
1201
|
+
problems.push({
|
|
1202
|
+
elementKey: key,
|
|
1203
|
+
message: `${element.type} has no prop "${name}" (known: ${Object.keys(shape).join(", ")})`
|
|
1204
|
+
});
|
|
1205
|
+
continue;
|
|
1206
|
+
}
|
|
1207
|
+
if (isExpression(value)) {
|
|
1208
|
+
if (HATCHES.has(element.type) && name === "path") {
|
|
1209
|
+
problems.push({
|
|
1210
|
+
elementKey: key,
|
|
1211
|
+
message: `${element.type}.path must be a literal string - absolute, or the item's field name inside a repeat - never an expression.`
|
|
1212
|
+
});
|
|
1213
|
+
} else if (isClosedVocabulary(propSchema)) {
|
|
1214
|
+
problems.push({
|
|
1215
|
+
elementKey: key,
|
|
1216
|
+
message: `${element.type}.${name} is one of a fixed set of names, so it must be written as a literal, never as an expression.`
|
|
1217
|
+
});
|
|
1218
|
+
}
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
const parsed = propSchema.safeParse(value);
|
|
1222
|
+
if (!parsed.success) {
|
|
1223
|
+
problems.push({
|
|
1224
|
+
elementKey: key,
|
|
1225
|
+
message: `${element.type}.${name}: ${parsed.error.issues.map((issue) => issue.message).join("; ")}`
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
for (const [name, propSchema] of Object.entries(shape)) {
|
|
1230
|
+
if (name in props) continue;
|
|
1231
|
+
if (propSchema.safeParse(void 0).success || propSchema.safeParse(null).success) continue;
|
|
1232
|
+
problems.push({
|
|
1233
|
+
elementKey: key,
|
|
1234
|
+
message: `${element.type} is missing required prop "${name}"`
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
const renders = catalog2.renders ?? {};
|
|
1239
|
+
const renderingOf = (type) => hasOwnProp(renders, type) ? renders[type] : void 0;
|
|
1240
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1241
|
+
const panelled = renderingOf(element.type)?.panels;
|
|
1242
|
+
if (!panelled) continue;
|
|
1243
|
+
const props = element.props ?? {};
|
|
1244
|
+
const listed = props[panelled.prop];
|
|
1245
|
+
if (!Array.isArray(listed)) continue;
|
|
1246
|
+
const panels = listed.length;
|
|
1247
|
+
const children = element.children?.length ?? 0;
|
|
1248
|
+
if (children !== panels) {
|
|
1249
|
+
problems.push({
|
|
1250
|
+
elementKey: key,
|
|
1251
|
+
message: `${element.type} declares ${panels} panel${panels === 1 ? "" : "s"} but has ${children} child${children === 1 ? "" : "ren"}: exactly one child per ${panelled.noun}, in order.`
|
|
1252
|
+
});
|
|
1253
|
+
}
|
|
1254
|
+
const runs = (element.children ?? []).filter(
|
|
1255
|
+
(child) => spec.elements[child]?.type === "Button"
|
|
1256
|
+
);
|
|
1257
|
+
if (runs.length > 0) {
|
|
1258
|
+
problems.push({
|
|
1259
|
+
elementKey: key,
|
|
1260
|
+
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.`
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
const present2 = (value) => value !== void 0 && value !== null && value !== "";
|
|
1265
|
+
const levels = [];
|
|
1266
|
+
const onlyOnce = /* @__PURE__ */ new Map();
|
|
1267
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1268
|
+
const walk = (key) => {
|
|
1269
|
+
if (seen.has(key)) return;
|
|
1270
|
+
seen.add(key);
|
|
1271
|
+
const element = spec.elements[key];
|
|
1272
|
+
if (!element) return;
|
|
1273
|
+
const props = element.props ?? {};
|
|
1274
|
+
const rendering = renderingOf(element.type);
|
|
1275
|
+
if (element.type === "Heading") {
|
|
1276
|
+
const level = String(props.level ?? "h2");
|
|
1277
|
+
const match = /^h([1-4])$/.exec(level);
|
|
1278
|
+
if (match) levels.push({ key, type: "Heading", level: Number(match[1]) });
|
|
1279
|
+
} else if (rendering?.heading) {
|
|
1280
|
+
const rendered = rendering.heading;
|
|
1281
|
+
const has = rendered.when === void 0 || present2(props[rendered.when]);
|
|
1282
|
+
if (has) levels.push({ key, type: element.type, level: rendered.level });
|
|
1283
|
+
}
|
|
1284
|
+
if (rendering?.once !== void 0) {
|
|
1285
|
+
const entry = onlyOnce.get(element.type) ?? { reason: rendering.once, keys: [] };
|
|
1286
|
+
entry.keys.push(key);
|
|
1287
|
+
onlyOnce.set(element.type, entry);
|
|
1288
|
+
}
|
|
1289
|
+
for (const child of element.children ?? []) walk(child);
|
|
1290
|
+
for (const slot of Object.values(element.slots ?? {})) for (const child of slot) walk(child);
|
|
1291
|
+
};
|
|
1292
|
+
if (spec.root) walk(spec.root);
|
|
1293
|
+
const describe2 = (heading) => heading.type === "Heading" ? `h${heading.level}` : `${heading.type}'s h${heading.level}`;
|
|
1294
|
+
let previous;
|
|
1295
|
+
for (const heading of levels) {
|
|
1296
|
+
if (previous !== void 0 && heading.level > previous.level + 1) {
|
|
1297
|
+
problems.push({
|
|
1298
|
+
elementKey: heading.key,
|
|
1299
|
+
message: `${describe2(heading)} jumps after ${describe2(previous)}: heading levels increase by one, never by more.`
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
previous = heading;
|
|
1303
|
+
}
|
|
1304
|
+
const firsts = levels.filter((heading) => heading.level === 1);
|
|
1305
|
+
const h1Ways = [
|
|
1306
|
+
...known.has("Heading") ? ["as a Heading at level h1"] : [],
|
|
1307
|
+
...Object.entries(renders).filter(([, rendering]) => rendering.heading?.level === 1).map(([type]) => `as the heading a ${type} renders`)
|
|
1308
|
+
];
|
|
1309
|
+
if (firsts.length === 0 && spec.root && h1Ways.length > 0) {
|
|
1310
|
+
problems.push({
|
|
1311
|
+
elementKey: spec.root,
|
|
1312
|
+
message: `the page has no h1: exactly one, ${h1Ways.join(" or ")}.`
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
for (const extra of firsts.slice(1)) {
|
|
1316
|
+
problems.push({
|
|
1317
|
+
elementKey: extra.key,
|
|
1318
|
+
message: `a second h1 (${describe2(extra)}, after ${describe2(firsts[0])} at [${firsts[0].key}]): exactly one on a page.`
|
|
1319
|
+
});
|
|
1320
|
+
}
|
|
1321
|
+
for (const [type, { reason, keys }] of onlyOnce) {
|
|
1322
|
+
for (const extra of keys.slice(1)) {
|
|
1323
|
+
problems.push({
|
|
1324
|
+
elementKey: extra,
|
|
1325
|
+
message: `a second ${type} (after [${keys[0]}]): at most one on a page, since it renders as ${reason}.`
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1330
|
+
const rule = renderingOf(element.type)?.children;
|
|
1331
|
+
if (!rule) continue;
|
|
1332
|
+
const children = element.children?.length ?? 0;
|
|
1333
|
+
if (children !== rule.count) {
|
|
1334
|
+
problems.push({
|
|
1335
|
+
elementKey: key,
|
|
1336
|
+
message: `${element.type} takes exactly ${rule.count} children (${rule.roles}); this one has ${children}.`
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
const declared = [
|
|
1341
|
+
...catalog2.actionNames ?? [],
|
|
1342
|
+
...(catalog2.schema?.builtInActions ?? []).map((action) => action.name)
|
|
1343
|
+
];
|
|
1344
|
+
const knownActions = declared.length > 0 ? new Set(declared) : void 0;
|
|
1345
|
+
const STATE_WRITES = {
|
|
1346
|
+
setState: [{ param: "statePath" }],
|
|
1347
|
+
pushState: [{ param: "statePath" }, { param: "clearStatePath", optional: true }],
|
|
1348
|
+
removeState: [{ param: "statePath" }],
|
|
1349
|
+
validateForm: [{ param: "statePath", optional: true }]
|
|
1350
|
+
};
|
|
1351
|
+
const OWNED_ROOTS = [INPUTS_ROOT, RESULT_ROOT];
|
|
1352
|
+
const ownedRootOf = (written) => {
|
|
1353
|
+
const statePath = written.startsWith("/") ? written : `/${written}`;
|
|
1354
|
+
return OWNED_ROOTS.find((root) => statePath === root || statePath.startsWith(`${root}/`));
|
|
1355
|
+
};
|
|
1356
|
+
const bindingsOf = (binding) => Array.isArray(binding) ? binding : [binding];
|
|
1357
|
+
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.`;
|
|
1358
|
+
const checkBinding = (key, site, entry) => {
|
|
1359
|
+
const action = entry?.action;
|
|
1360
|
+
if (typeof action !== "string") {
|
|
1361
|
+
problems.push({ elementKey: key, message: `${site} has an entry with no "action" name.` });
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
if (knownActions && !knownActions.has(action)) {
|
|
1365
|
+
problems.push({
|
|
1366
|
+
elementKey: key,
|
|
1367
|
+
message: `${site} names the unknown action "${action}" (known: ${[...knownActions].join(", ")}); nothing would handle it.`
|
|
1368
|
+
});
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
const writes = hasOwnProp(STATE_WRITES, action) ? STATE_WRITES[action] : void 0;
|
|
1372
|
+
if (writes) {
|
|
1373
|
+
const params = entry.params;
|
|
1374
|
+
for (const { param, optional } of writes) {
|
|
1375
|
+
const written = typeof params === "object" && params !== null ? params[param] : void 0;
|
|
1376
|
+
if (written === void 0 || written === null) {
|
|
1377
|
+
if (!optional) {
|
|
1378
|
+
problems.push({
|
|
1379
|
+
elementKey: key,
|
|
1380
|
+
message: `${site} calls ${action} without a literal "${param}".`
|
|
1381
|
+
});
|
|
1382
|
+
}
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (typeof written !== "string") {
|
|
1386
|
+
problems.push({
|
|
1387
|
+
elementKey: key,
|
|
1388
|
+
message: `${site} calls ${action} with a "${param}" that is not a literal string; where an action writes must be named, never computed.`
|
|
1389
|
+
});
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
const owned = ownedRootOf(written);
|
|
1393
|
+
if (owned) {
|
|
1394
|
+
const where = param === "statePath" ? `on "${written}"` : `with ${param} "${written}"`;
|
|
1395
|
+
problems.push({
|
|
1396
|
+
elementKey: key,
|
|
1397
|
+
message: `${site} calls ${action} ${where}: ${refusal(owned, written.startsWith("/") ? written : `/${written}`)}`
|
|
1398
|
+
});
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
for (const hook of ["onSuccess", "onError"]) {
|
|
1403
|
+
const follow = entry[hook];
|
|
1404
|
+
if (follow === void 0 || follow === null) continue;
|
|
1405
|
+
const hookSite = `${site}.${hook}`;
|
|
1406
|
+
if (typeof follow !== "object" || Array.isArray(follow)) {
|
|
1407
|
+
problems.push({
|
|
1408
|
+
elementKey: key,
|
|
1409
|
+
message: `${hookSite} must be an object: a { "set" } of state paths to values, or an { "action" } binding.`
|
|
1410
|
+
});
|
|
1411
|
+
continue;
|
|
1412
|
+
}
|
|
1413
|
+
const record = follow;
|
|
1414
|
+
if (record.set !== void 0 && record.set !== null) {
|
|
1415
|
+
if (typeof record.set !== "object" || Array.isArray(record.set)) {
|
|
1416
|
+
problems.push({
|
|
1417
|
+
elementKey: key,
|
|
1418
|
+
message: `${hookSite}.set must be an object mapping a state path to its value.`
|
|
1419
|
+
});
|
|
1420
|
+
} else {
|
|
1421
|
+
for (const written of Object.keys(record.set)) {
|
|
1422
|
+
const owned = ownedRootOf(written);
|
|
1423
|
+
if (owned) {
|
|
1424
|
+
problems.push({
|
|
1425
|
+
elementKey: key,
|
|
1426
|
+
message: `${hookSite} sets "${written}": ${refusal(owned, written.startsWith("/") ? written : `/${written}`)}`
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (record.action !== void 0) checkBinding(key, hookSite, record);
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1436
|
+
const on = element.on;
|
|
1437
|
+
if (on !== void 0 && on !== null) {
|
|
1438
|
+
if (typeof on !== "object" || Array.isArray(on)) {
|
|
1439
|
+
problems.push({
|
|
1440
|
+
elementKey: key,
|
|
1441
|
+
message: '"on" must be an object mapping an event name to its actions.'
|
|
1442
|
+
});
|
|
1443
|
+
} else {
|
|
1444
|
+
const emitted = eventsOf(catalog2, element.type);
|
|
1445
|
+
for (const [event, binding] of Object.entries(on)) {
|
|
1446
|
+
if (emitted && !emitted.includes(event)) {
|
|
1447
|
+
problems.push({
|
|
1448
|
+
elementKey: key,
|
|
1449
|
+
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.`
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
for (const entry of bindingsOf(binding)) checkBinding(key, `on.${event}`, entry);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
const watch = element.watch;
|
|
1457
|
+
if (watch !== void 0 && watch !== null) {
|
|
1458
|
+
if (typeof watch !== "object" || Array.isArray(watch)) {
|
|
1459
|
+
problems.push({
|
|
1460
|
+
elementKey: key,
|
|
1461
|
+
message: '"watch" must be an object mapping a state path to the actions its change fires.'
|
|
1462
|
+
});
|
|
1463
|
+
} else {
|
|
1464
|
+
for (const [path, binding] of Object.entries(watch)) {
|
|
1465
|
+
for (const entry of bindingsOf(binding)) checkBinding(key, `watch.${path}`, entry);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
return { ok: problems.length === 0, problems };
|
|
1471
|
+
}
|
|
1472
|
+
function formatProblems(problems) {
|
|
1473
|
+
return problems.map(
|
|
1474
|
+
(problem) => problem.elementKey ? `[${problem.elementKey}] ${problem.message}` : problem.message
|
|
1475
|
+
).join("\n");
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
// src/generative/layout-fits.ts
|
|
1479
|
+
function isUnder(root, path) {
|
|
1480
|
+
return path === root || path.startsWith(`${root}/`);
|
|
1481
|
+
}
|
|
1482
|
+
function itemPathResolver(spec, key, parents) {
|
|
1483
|
+
let base;
|
|
1484
|
+
let resolvedBase = false;
|
|
1485
|
+
return (field) => {
|
|
1486
|
+
if (!resolvedBase) {
|
|
1487
|
+
base = repeatBasePathOf(spec, key, parents);
|
|
1488
|
+
resolvedBase = true;
|
|
1489
|
+
}
|
|
1490
|
+
if (base === void 0) return void 0;
|
|
1491
|
+
const under = field.startsWith("/") ? field.slice(1) : field;
|
|
1492
|
+
return under === "" ? base : `${base}/${under}`;
|
|
1493
|
+
};
|
|
1494
|
+
}
|
|
1495
|
+
function interpolatedPaths(template, itemPath) {
|
|
1496
|
+
return [...template.matchAll(/\$\{([^}]+)\}/g)].flatMap((match) => {
|
|
1497
|
+
const raw = match[1];
|
|
1498
|
+
if (raw === void 0) return [];
|
|
1499
|
+
if (raw.startsWith("/")) return [raw];
|
|
1500
|
+
return [itemPath(raw) ?? `/${raw}`];
|
|
1501
|
+
});
|
|
1502
|
+
}
|
|
1503
|
+
function mentionedPaths(spec, parents) {
|
|
1504
|
+
const found = { bound: [], read: [], adrift: [] };
|
|
1505
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1506
|
+
const itemPath = itemPathResolver(spec, key, parents);
|
|
1507
|
+
const walk = (value) => {
|
|
1508
|
+
if (Array.isArray(value)) {
|
|
1509
|
+
value.forEach(walk);
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
if (typeof value !== "object" || value === null) return;
|
|
1513
|
+
const record = value;
|
|
1514
|
+
if (typeof record.$bindState === "string") found.bound.push(record.$bindState);
|
|
1515
|
+
if (typeof record.$state === "string") found.read.push(record.$state);
|
|
1516
|
+
if (typeof record.$template === "string") {
|
|
1517
|
+
found.read.push(...interpolatedPaths(record.$template, itemPath));
|
|
1518
|
+
}
|
|
1519
|
+
if (typeof record.$bindItem === "string") {
|
|
1520
|
+
const path = itemPath(record.$bindItem);
|
|
1521
|
+
if (path === void 0) found.adrift.push(key);
|
|
1522
|
+
else found.bound.push(path);
|
|
1523
|
+
}
|
|
1524
|
+
if (typeof record.$item === "string") {
|
|
1525
|
+
const path = itemPath(record.$item);
|
|
1526
|
+
if (path === void 0) found.adrift.push(key);
|
|
1527
|
+
else found.read.push(path);
|
|
1528
|
+
}
|
|
1529
|
+
Object.values(record).forEach(walk);
|
|
1530
|
+
};
|
|
1531
|
+
walk(element.props);
|
|
1532
|
+
walk(element.visible);
|
|
1533
|
+
}
|
|
1534
|
+
return found;
|
|
1535
|
+
}
|
|
1536
|
+
function staleReads(read, root, resolves) {
|
|
1537
|
+
const under = read.filter((path) => isUnder(root, path));
|
|
1538
|
+
return [...new Set(under)].filter((path) => !resolves(path));
|
|
1539
|
+
}
|
|
1540
|
+
function staleRepeats(spec, parents, root, resolves, side) {
|
|
1541
|
+
const problems = [];
|
|
1542
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1543
|
+
if (!element.repeat) continue;
|
|
1544
|
+
const listPath = repeatListPathOf(spec, key, parents);
|
|
1545
|
+
if (listPath === void 0 || !isUnder(root, listPath)) continue;
|
|
1546
|
+
if (!resolves(listPath)) {
|
|
1547
|
+
problems.push(`${key}: repeats over ${listPath}, which no ${side} has`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
return problems;
|
|
1551
|
+
}
|
|
1552
|
+
var NOT_A_VALUE = {
|
|
1553
|
+
object: { what: "a structure", instead: "a structure is shown through its members" },
|
|
1554
|
+
list: { what: "a list", instead: "a list is laid out as a repeat or a DataTable" },
|
|
1555
|
+
document: { what: "a file", instead: "a file is delegated to the kernel's own control" },
|
|
1556
|
+
image: { what: "a file", instead: "a file is delegated to the kernel's own control" }
|
|
1557
|
+
};
|
|
1558
|
+
var NOT_VALUES = {
|
|
1559
|
+
object: "structures",
|
|
1560
|
+
list: "lists",
|
|
1561
|
+
document: "files",
|
|
1562
|
+
image: "files"
|
|
1563
|
+
};
|
|
1564
|
+
var listPropsCache;
|
|
1565
|
+
function listProps() {
|
|
1566
|
+
if (listPropsCache) return listPropsCache;
|
|
1567
|
+
const found = /* @__PURE__ */ new Map([
|
|
1568
|
+
["DataTable", /* @__PURE__ */ new Map([["rows", "anything"]])]
|
|
1569
|
+
]);
|
|
1570
|
+
const components3 = catalog.data.components;
|
|
1571
|
+
for (const [type, definition] of Object.entries(components3)) {
|
|
1572
|
+
const shape = definition.props?.shape;
|
|
1573
|
+
for (const [prop, schema] of Object.entries(shape ?? {})) {
|
|
1574
|
+
const items = listItemsOf(schema);
|
|
1575
|
+
if (items === void 0) continue;
|
|
1576
|
+
if (!found.has(type)) found.set(type, /* @__PURE__ */ new Map());
|
|
1577
|
+
found.get(type).set(prop, items);
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
listPropsCache = found;
|
|
1581
|
+
return found;
|
|
1582
|
+
}
|
|
1583
|
+
var VALUE_SCHEMAS = /* @__PURE__ */ new Set(["string", "number", "boolean", "enum", "literal"]);
|
|
1584
|
+
function unwrapped(schema) {
|
|
1585
|
+
let current = schema;
|
|
1586
|
+
for (let depth = 0; depth < 8; depth += 1) {
|
|
1587
|
+
const def = current.def;
|
|
1588
|
+
if (!def) return void 0;
|
|
1589
|
+
if (def.innerType === void 0) return def;
|
|
1590
|
+
current = def.innerType;
|
|
1591
|
+
}
|
|
1592
|
+
return void 0;
|
|
1593
|
+
}
|
|
1594
|
+
function listItemsOf(schema) {
|
|
1595
|
+
const def = unwrapped(schema);
|
|
1596
|
+
if (def?.type !== "array") return void 0;
|
|
1597
|
+
const element = unwrapped(def.element);
|
|
1598
|
+
return element?.type !== void 0 && VALUE_SCHEMAS.has(element.type) ? "values" : "anything";
|
|
1599
|
+
}
|
|
1600
|
+
function nonScalarReads(spec, parents, root, fieldAt) {
|
|
1601
|
+
const problems = [];
|
|
1602
|
+
const lists = listProps();
|
|
1603
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1604
|
+
const itemPath = itemPathResolver(spec, key, parents);
|
|
1605
|
+
const listy = lists.get(element.type);
|
|
1606
|
+
const check = (prop, path) => {
|
|
1607
|
+
if (!isUnder(root, path)) return;
|
|
1608
|
+
const field = fieldAt(path);
|
|
1609
|
+
if (!field) return;
|
|
1610
|
+
const wrong = hasOwnProp(NOT_A_VALUE, field.kind) ? NOT_A_VALUE[field.kind] : void 0;
|
|
1611
|
+
if (!wrong) return;
|
|
1612
|
+
problems.push(
|
|
1613
|
+
`${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}`
|
|
1614
|
+
);
|
|
1615
|
+
};
|
|
1616
|
+
const checkList = (prop, path, items) => {
|
|
1617
|
+
if (!isUnder(root, path)) return;
|
|
1618
|
+
const field = fieldAt(path);
|
|
1619
|
+
if (!field || field.kind === "unknown") return;
|
|
1620
|
+
if (field.kind !== "list") {
|
|
1621
|
+
const what = hasOwnProp(NOT_A_VALUE, field.kind) ? NOT_A_VALUE[field.kind].what : "a value";
|
|
1622
|
+
problems.push(
|
|
1623
|
+
`${key}: ${prop} takes a list, and ${path} is ${what}; a prop that takes a list reads a list path`
|
|
1624
|
+
);
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
if (items !== "values") return;
|
|
1628
|
+
const of = hasOwnProp(NOT_VALUES, field.item.kind) ? NOT_VALUES[field.item.kind] : void 0;
|
|
1629
|
+
if (!of) return;
|
|
1630
|
+
problems.push(
|
|
1631
|
+
`${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`
|
|
1632
|
+
);
|
|
1633
|
+
};
|
|
1634
|
+
const walk = (prop, value) => {
|
|
1635
|
+
if (Array.isArray(value)) {
|
|
1636
|
+
value.forEach((entry) => walk(prop, entry));
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
if (typeof value !== "object" || value === null) return;
|
|
1640
|
+
const record = value;
|
|
1641
|
+
if (typeof record.$state === "string") check(prop, record.$state);
|
|
1642
|
+
if (typeof record.$item === "string") {
|
|
1643
|
+
const path = itemPath(record.$item);
|
|
1644
|
+
if (path !== void 0) check(prop, path);
|
|
1645
|
+
}
|
|
1646
|
+
if (typeof record.$template === "string") {
|
|
1647
|
+
for (const path of interpolatedPaths(record.$template, itemPath)) check(prop, path);
|
|
1648
|
+
}
|
|
1649
|
+
for (const [name, entry] of Object.entries(record)) {
|
|
1650
|
+
if (name !== "$cond") walk(prop, entry);
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1653
|
+
for (const [prop, value] of Object.entries(element.props ?? {})) {
|
|
1654
|
+
const items = listy?.get(prop);
|
|
1655
|
+
if (items === void 0 || Array.isArray(value)) {
|
|
1656
|
+
walk(prop, value);
|
|
1657
|
+
continue;
|
|
1658
|
+
}
|
|
1659
|
+
if (typeof value !== "object" || value === null) continue;
|
|
1660
|
+
const record = value;
|
|
1661
|
+
if (typeof record.$state === "string") checkList(prop, record.$state, items);
|
|
1662
|
+
if (typeof record.$item === "string") {
|
|
1663
|
+
const path = itemPath(record.$item);
|
|
1664
|
+
if (path !== void 0) checkList(prop, path, items);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return problems;
|
|
1669
|
+
}
|
|
1670
|
+
function boundProblems(bound, inputs) {
|
|
1671
|
+
const problems = [];
|
|
1672
|
+
const underInputs = bound.filter((path) => isUnder(INPUTS_ROOT, path));
|
|
1673
|
+
if (!inputs) {
|
|
1674
|
+
if (underInputs.length > 0) {
|
|
1675
|
+
problems.push(`a result page binds ${underInputs.join(", ")}; a result page writes nothing`);
|
|
1676
|
+
}
|
|
1677
|
+
} else {
|
|
1678
|
+
for (const path of underInputs) {
|
|
1679
|
+
if (!inputFieldAtPath(inputs, path)) {
|
|
1680
|
+
problems.push(`${path} is bound, and no input has it`);
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
for (const path of bound) {
|
|
1685
|
+
if (isUnder(RESULT_ROOT, path)) {
|
|
1686
|
+
problems.push(
|
|
1687
|
+
`${path} is bound; a layout may not write into ${RESULT_ROOT}, which the run fills`
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return problems;
|
|
1692
|
+
}
|
|
1693
|
+
function repeatPaths(spec) {
|
|
1694
|
+
const found = [];
|
|
1695
|
+
for (const element of Object.values(spec.elements)) {
|
|
1696
|
+
const statePath = element.repeat?.statePath;
|
|
1697
|
+
if (typeof statePath === "string") found.push(statePath);
|
|
1698
|
+
}
|
|
1699
|
+
return found;
|
|
1700
|
+
}
|
|
1701
|
+
function isOffered(path, offered) {
|
|
1702
|
+
if (offered.bound.has(path) || offered.repeated.has(path)) return true;
|
|
1703
|
+
return offered.delegated.some((at) => path === at || path.startsWith(`${at}/`));
|
|
1704
|
+
}
|
|
1705
|
+
function uncoveredUnder(field, path, offered) {
|
|
1706
|
+
if (isOffered(path, offered)) return [];
|
|
1707
|
+
if (field.kind === "object") {
|
|
1708
|
+
return field.fields.filter((child) => child.required).flatMap((child) => uncoveredUnder(child, joinPath(path, child.name), offered));
|
|
1709
|
+
}
|
|
1710
|
+
return [path];
|
|
1711
|
+
}
|
|
1712
|
+
function layoutProblems(descriptor, spec) {
|
|
1713
|
+
try {
|
|
1714
|
+
return collectLayoutProblems(descriptor, spec);
|
|
1715
|
+
} catch (error) {
|
|
1716
|
+
return [`the layout is not a well-formed spec: ${String(error)}`];
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
function collectLayoutProblems(descriptor, spec) {
|
|
1720
|
+
const problems = [];
|
|
1721
|
+
const delegated = [];
|
|
1722
|
+
const parents = parentMapOf(spec);
|
|
1723
|
+
for (const [key, element] of Object.entries(spec.elements)) {
|
|
1724
|
+
const written = element.props?.path;
|
|
1725
|
+
const path = typeof written === "string" ? absoluteHatchPath(spec, key, written, parents) : void 0;
|
|
1726
|
+
if (element.type === "MthdsField") {
|
|
1727
|
+
if (!path || !inputFieldAtPath(descriptor.inputs ?? [], path)) {
|
|
1728
|
+
problems.push(`${key}: MthdsField delegates ${String(written)}, which no input has`);
|
|
1729
|
+
} else {
|
|
1730
|
+
delegated.push(path);
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (element.type === "MthdsResult") {
|
|
1734
|
+
if (!path || !descriptor.result || !resultFieldAtPath(descriptor.result, path)) {
|
|
1735
|
+
problems.push(`${key}: MthdsResult delegates ${String(written)}, which no result has`);
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
const { bound, read, adrift } = mentionedPaths(spec, parents);
|
|
1740
|
+
for (const key of new Set(adrift)) {
|
|
1741
|
+
problems.push(`${key}: reads the current item, and no repeat above it has one`);
|
|
1742
|
+
}
|
|
1743
|
+
problems.push(...boundProblems(bound, descriptor.inputs));
|
|
1744
|
+
if (!descriptor.inputs) {
|
|
1745
|
+
const result = descriptor.result;
|
|
1746
|
+
const fieldAt2 = (path) => result ? resultFieldAtPath(result, path) : void 0;
|
|
1747
|
+
const resolves2 = (path) => fieldAt2(path) !== void 0;
|
|
1748
|
+
problems.push(...staleRepeats(spec, parents, RESULT_ROOT, resolves2, "result"));
|
|
1749
|
+
for (const path of staleReads(read, RESULT_ROOT, resolves2)) {
|
|
1750
|
+
problems.push(`${path} is read, and no result has it`);
|
|
1751
|
+
}
|
|
1752
|
+
problems.push(...nonScalarReads(spec, parents, RESULT_ROOT, fieldAt2));
|
|
1753
|
+
return problems;
|
|
1754
|
+
}
|
|
1755
|
+
const inputs = descriptor.inputs;
|
|
1756
|
+
const fieldAt = (path) => inputFieldAtPath(inputs, path);
|
|
1757
|
+
const resolves = (path) => fieldAt(path) !== void 0;
|
|
1758
|
+
problems.push(...staleRepeats(spec, parents, INPUTS_ROOT, resolves, "input"));
|
|
1759
|
+
for (const path of staleReads(read, INPUTS_ROOT, resolves)) {
|
|
1760
|
+
problems.push(`${path} is read, and no input has it`);
|
|
1761
|
+
}
|
|
1762
|
+
problems.push(...nonScalarReads(spec, parents, INPUTS_ROOT, fieldAt));
|
|
1763
|
+
const offered = {
|
|
1764
|
+
bound: new Set(bound),
|
|
1765
|
+
repeated: new Set(repeatPaths(spec)),
|
|
1766
|
+
delegated
|
|
1767
|
+
};
|
|
1768
|
+
for (const field of inputs) {
|
|
1769
|
+
if (!field.required) continue;
|
|
1770
|
+
for (const path of uncoveredUnder(field, joinPath(INPUTS_ROOT, field.name), offered)) {
|
|
1771
|
+
problems.push(`${path} is required, and the layout offers nowhere to enter it`);
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
return problems;
|
|
1775
|
+
}
|
|
1776
|
+
function layoutFits(descriptor, spec) {
|
|
1777
|
+
return layoutProblems(descriptor, spec).length === 0;
|
|
1778
|
+
}
|
|
1779
|
+
function specToJsonl(spec) {
|
|
1780
|
+
const lines = [];
|
|
1781
|
+
const push = (op, path, value) => lines.push(JSON.stringify({ op, path, value }));
|
|
1782
|
+
push("add", "/root", spec.root);
|
|
1783
|
+
const emitted = /* @__PURE__ */ new Set();
|
|
1784
|
+
const queue = [spec.root];
|
|
1785
|
+
while (queue.length > 0) {
|
|
1786
|
+
const key = queue.shift();
|
|
1787
|
+
if (emitted.has(key)) continue;
|
|
1788
|
+
const element = spec.elements[key];
|
|
1789
|
+
if (!element) continue;
|
|
1790
|
+
emitted.add(key);
|
|
1791
|
+
push("add", `/elements/${escapeSegment(key)}`, element);
|
|
1792
|
+
for (const child of element.children ?? []) queue.push(child);
|
|
1793
|
+
for (const slotChildren of Object.values(element.slots ?? {})) queue.push(...slotChildren);
|
|
1794
|
+
}
|
|
1795
|
+
for (const key of Object.keys(spec.elements)) {
|
|
1796
|
+
if (!emitted.has(key)) push("add", `/elements/${escapeSegment(key)}`, spec.elements[key]);
|
|
1797
|
+
}
|
|
1798
|
+
for (const [key, value] of Object.entries(spec.state ?? {})) {
|
|
1799
|
+
push("add", `/state/${escapeSegment(key)}`, value);
|
|
1800
|
+
}
|
|
1801
|
+
return `${lines.join("\n")}
|
|
1802
|
+
`;
|
|
1803
|
+
}
|
|
1804
|
+
var FORBIDDEN_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1805
|
+
function pointerReachesPrototype(pointer) {
|
|
1806
|
+
return typeof pointer === "string" && pointer.split("/").some((segment) => FORBIDDEN_SEGMENTS.has(unescapeSegment(segment)));
|
|
1807
|
+
}
|
|
1808
|
+
function reachesPrototype(line) {
|
|
1809
|
+
let parsed;
|
|
1810
|
+
try {
|
|
1811
|
+
parsed = JSON.parse(line);
|
|
1812
|
+
} catch {
|
|
1813
|
+
return false;
|
|
1814
|
+
}
|
|
1815
|
+
const patch = parsed;
|
|
1816
|
+
return pointerReachesPrototype(patch?.path) || pointerReachesPrototype(patch?.from);
|
|
1817
|
+
}
|
|
1818
|
+
function specFromJsonl(jsonl) {
|
|
1819
|
+
let spec = { root: "", elements: {} };
|
|
1820
|
+
for (const line of jsonl.split("\n")) {
|
|
1821
|
+
if (reachesPrototype(line)) continue;
|
|
1822
|
+
try {
|
|
1823
|
+
spec = compileSpecStream(line, spec);
|
|
1824
|
+
} catch {
|
|
1825
|
+
continue;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
return spec;
|
|
1829
|
+
}
|
|
1830
|
+
function jsonlLines(jsonl) {
|
|
1831
|
+
return jsonl.split("\n").filter((line) => line.trim().length > 0);
|
|
1832
|
+
}
|
|
1833
|
+
var BrandContext = React3.createContext(null);
|
|
1834
|
+
function BrandProvider({
|
|
1835
|
+
manifest,
|
|
1836
|
+
children
|
|
1837
|
+
}) {
|
|
1838
|
+
useWebfont(manifest.webfont);
|
|
1839
|
+
return /* @__PURE__ */ jsx(BrandContext.Provider, { value: manifest, children });
|
|
1840
|
+
}
|
|
1841
|
+
function useBrand() {
|
|
1842
|
+
const manifest = React3.useContext(BrandContext);
|
|
1843
|
+
if (!manifest)
|
|
1844
|
+
throw new Error(
|
|
1845
|
+
"useBrand: no brand in scope. Pass `brand` to GenerativePage, or wrap the tree in BrandProvider."
|
|
1846
|
+
);
|
|
1847
|
+
return manifest;
|
|
1848
|
+
}
|
|
1849
|
+
var WEIGHTS = "400;500;600;700";
|
|
1850
|
+
function useWebfont(webfont) {
|
|
1851
|
+
const provider = webfont?.provider;
|
|
1852
|
+
const family = webfont?.family;
|
|
1853
|
+
React3.useEffect(() => {
|
|
1854
|
+
if (!provider || !family) return;
|
|
1855
|
+
const key = `${provider}:${family}`;
|
|
1856
|
+
if (document.head.querySelector(`link[data-brand-webfont="${CSS.escape(key)}"]`)) return;
|
|
1857
|
+
for (const origin of ["https://fonts.googleapis.com", "https://fonts.gstatic.com"]) {
|
|
1858
|
+
if (document.head.querySelector(`link[rel="preconnect"][href="${origin}"]`)) continue;
|
|
1859
|
+
const preconnect = document.createElement("link");
|
|
1860
|
+
preconnect.rel = "preconnect";
|
|
1861
|
+
preconnect.href = origin;
|
|
1862
|
+
if (origin.endsWith("gstatic.com")) preconnect.crossOrigin = "anonymous";
|
|
1863
|
+
document.head.appendChild(preconnect);
|
|
1864
|
+
}
|
|
1865
|
+
const link = document.createElement("link");
|
|
1866
|
+
link.rel = "stylesheet";
|
|
1867
|
+
const encoded = encodeURIComponent(family).replace(/%20/g, "+");
|
|
1868
|
+
link.href = `https://fonts.googleapis.com/css2?family=${encoded}:wght@${WEIGHTS}&display=swap`;
|
|
1869
|
+
link.dataset.brandWebfont = key;
|
|
1870
|
+
document.head.appendChild(link);
|
|
1871
|
+
}, [provider, family]);
|
|
1872
|
+
}
|
|
1873
|
+
function useBoundField(literal, binding, empty, { checks, validateOn }) {
|
|
1874
|
+
const [boundValue, setBoundValue] = useBoundProp(literal ?? void 0, binding);
|
|
1875
|
+
const [localValue, setLocalValue] = React3.useState(literal ?? empty);
|
|
1876
|
+
const isBound = Boolean(binding);
|
|
1877
|
+
const hasValidation = Boolean(binding && checks?.length);
|
|
1878
|
+
const when = validateOn ?? "change";
|
|
1879
|
+
const { errors, validate } = useFieldValidation(
|
|
1880
|
+
binding ?? "",
|
|
1881
|
+
hasValidation ? { checks: checks ?? [], validateOn: when } : void 0
|
|
1882
|
+
);
|
|
1883
|
+
return {
|
|
1884
|
+
value: isBound ? boundValue ?? empty : localValue,
|
|
1885
|
+
setValue: (next) => {
|
|
1886
|
+
if (isBound) setBoundValue(next);
|
|
1887
|
+
else setLocalValue(next);
|
|
1888
|
+
},
|
|
1889
|
+
error: errors[0],
|
|
1890
|
+
validateOnChange: () => {
|
|
1891
|
+
if (hasValidation && when === "change") validate();
|
|
1892
|
+
},
|
|
1893
|
+
validateOnBlur: () => {
|
|
1894
|
+
if (hasValidation && when === "blur") validate();
|
|
1895
|
+
}
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
var LABEL = "block text-sm leading-none font-medium";
|
|
1899
|
+
function FieldError({ message }) {
|
|
1900
|
+
return message ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: message }) : null;
|
|
1901
|
+
}
|
|
1902
|
+
function pickKey(table, key, fallback) {
|
|
1903
|
+
return key != null && hasOwnProp(table, key) ? key : fallback;
|
|
1904
|
+
}
|
|
1905
|
+
function pick(table, key, fallback) {
|
|
1906
|
+
return table[pickKey(table, key, fallback)];
|
|
1907
|
+
}
|
|
1908
|
+
function Card({
|
|
1909
|
+
props,
|
|
1910
|
+
children
|
|
1911
|
+
}) {
|
|
1912
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-border bg-card p-6 text-card-foreground", children: [
|
|
1913
|
+
props.title || props.description ? /* @__PURE__ */ jsxs("div", { className: "mb-4 space-y-1.5", children: [
|
|
1914
|
+
props.title ? /* @__PURE__ */ jsx("h3", { className: "text-lg leading-none font-semibold", children: props.title }) : null,
|
|
1915
|
+
props.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: props.description }) : null
|
|
1916
|
+
] }) : null,
|
|
1917
|
+
children
|
|
1918
|
+
] });
|
|
1919
|
+
}
|
|
1920
|
+
var GAP = {
|
|
1921
|
+
none: "gap-0",
|
|
1922
|
+
sm: "gap-2",
|
|
1923
|
+
md: "gap-4",
|
|
1924
|
+
lg: "gap-6",
|
|
1925
|
+
xl: "gap-8"
|
|
1926
|
+
};
|
|
1927
|
+
var ALIGN = {
|
|
1928
|
+
start: "items-start",
|
|
1929
|
+
center: "items-center",
|
|
1930
|
+
end: "items-end",
|
|
1931
|
+
stretch: "items-stretch"
|
|
1932
|
+
};
|
|
1933
|
+
var JUSTIFY = {
|
|
1934
|
+
start: "justify-start",
|
|
1935
|
+
center: "justify-center",
|
|
1936
|
+
end: "justify-end",
|
|
1937
|
+
between: "justify-between",
|
|
1938
|
+
around: "justify-around"
|
|
1939
|
+
};
|
|
1940
|
+
function Stack({
|
|
1941
|
+
props,
|
|
1942
|
+
children
|
|
1943
|
+
}) {
|
|
1944
|
+
const horizontal = props.direction === "horizontal";
|
|
1945
|
+
return /* @__PURE__ */ jsx(
|
|
1946
|
+
"div",
|
|
1947
|
+
{
|
|
1948
|
+
className: cn(
|
|
1949
|
+
"flex",
|
|
1950
|
+
horizontal ? "flex-row" : "flex-col",
|
|
1951
|
+
pick(GAP, props.gap, "md"),
|
|
1952
|
+
pick(ALIGN, props.align, horizontal ? "start" : "stretch"),
|
|
1953
|
+
props.justify ? pick(JUSTIFY, props.justify, "start") : void 0
|
|
1954
|
+
),
|
|
1955
|
+
children
|
|
1956
|
+
}
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
var COLUMNS = {
|
|
1960
|
+
1: "grid-cols-1",
|
|
1961
|
+
2: "sm:grid-cols-2",
|
|
1962
|
+
3: "sm:grid-cols-2 lg:grid-cols-3",
|
|
1963
|
+
4: "sm:grid-cols-2 lg:grid-cols-4",
|
|
1964
|
+
5: "sm:grid-cols-2 lg:grid-cols-5",
|
|
1965
|
+
6: "sm:grid-cols-2 lg:grid-cols-6"
|
|
1966
|
+
};
|
|
1967
|
+
function Grid({
|
|
1968
|
+
props,
|
|
1969
|
+
children
|
|
1970
|
+
}) {
|
|
1971
|
+
const columns = Math.min(Math.max(Math.round(props.columns ?? 2), 1), 6);
|
|
1972
|
+
return /* @__PURE__ */ jsx("div", { className: cn("grid grid-cols-1", COLUMNS[columns], pick(GAP, props.gap, "md")), children });
|
|
1973
|
+
}
|
|
1974
|
+
function Separator({
|
|
1975
|
+
props
|
|
1976
|
+
}) {
|
|
1977
|
+
const vertical = props.orientation === "vertical";
|
|
1978
|
+
return /* @__PURE__ */ jsx(
|
|
1979
|
+
"div",
|
|
1980
|
+
{
|
|
1981
|
+
role: "separator",
|
|
1982
|
+
"aria-orientation": vertical ? "vertical" : "horizontal",
|
|
1983
|
+
className: cn("shrink-0 bg-border", vertical ? "h-full w-px" : "h-px w-full")
|
|
1984
|
+
}
|
|
1985
|
+
);
|
|
1986
|
+
}
|
|
1987
|
+
function Collapsible({
|
|
1988
|
+
props,
|
|
1989
|
+
children
|
|
1990
|
+
}) {
|
|
1991
|
+
return /* @__PURE__ */ jsxs(
|
|
1992
|
+
"details",
|
|
1993
|
+
{
|
|
1994
|
+
className: "rounded-md border border-border",
|
|
1995
|
+
open: props.defaultOpen ?? false,
|
|
1996
|
+
"data-slot": "collapsible",
|
|
1997
|
+
children: [
|
|
1998
|
+
/* @__PURE__ */ jsx("summary", { className: "cursor-pointer list-none px-4 py-3 text-sm font-medium select-none", children: props.title }),
|
|
1999
|
+
/* @__PURE__ */ jsx("div", { className: "border-t border-border px-4 py-3", children })
|
|
2000
|
+
]
|
|
2001
|
+
}
|
|
2002
|
+
);
|
|
2003
|
+
}
|
|
2004
|
+
var HEADING = {
|
|
2005
|
+
h1: "text-3xl font-bold tracking-tight",
|
|
2006
|
+
h2: "text-2xl font-semibold tracking-tight",
|
|
2007
|
+
h3: "text-lg font-semibold",
|
|
2008
|
+
h4: "text-base font-semibold"
|
|
2009
|
+
};
|
|
2010
|
+
function Heading({
|
|
2011
|
+
props
|
|
2012
|
+
}) {
|
|
2013
|
+
const Tag2 = pickKey(HEADING, props.level, "h2");
|
|
2014
|
+
return /* @__PURE__ */ jsx(Tag2, { className: cn("text-foreground", HEADING[Tag2]), children: props.text });
|
|
2015
|
+
}
|
|
2016
|
+
var TEXT = {
|
|
2017
|
+
body: "text-sm text-foreground",
|
|
2018
|
+
caption: "text-xs text-muted-foreground",
|
|
2019
|
+
muted: "text-sm text-muted-foreground",
|
|
2020
|
+
lead: "text-lg text-muted-foreground",
|
|
2021
|
+
code: "font-mono text-[13px] text-foreground"
|
|
2022
|
+
};
|
|
2023
|
+
function Text({
|
|
2024
|
+
props
|
|
2025
|
+
}) {
|
|
2026
|
+
return /* @__PURE__ */ jsx("p", { className: pick(TEXT, props.variant, "body"), children: props.text });
|
|
2027
|
+
}
|
|
2028
|
+
var AVATAR_SIZE = {
|
|
2029
|
+
sm: "size-8 text-[11px]",
|
|
2030
|
+
md: "size-10 text-xs",
|
|
2031
|
+
lg: "size-12 text-sm"
|
|
2032
|
+
};
|
|
2033
|
+
function initialsOf(name) {
|
|
2034
|
+
const words = name.trim().split(/\s+/).filter(Boolean);
|
|
2035
|
+
if (words.length === 0) return "?";
|
|
2036
|
+
const first = words[0]?.[0] ?? "";
|
|
2037
|
+
const last = words.length > 1 ? words[words.length - 1]?.[0] ?? "" : "";
|
|
2038
|
+
return (first + last).toUpperCase();
|
|
2039
|
+
}
|
|
2040
|
+
function Avatar({
|
|
2041
|
+
props
|
|
2042
|
+
}) {
|
|
2043
|
+
const name = String(props.name ?? "");
|
|
2044
|
+
return /* @__PURE__ */ jsx(
|
|
2045
|
+
"span",
|
|
2046
|
+
{
|
|
2047
|
+
className: cn(
|
|
2048
|
+
"inline-flex shrink-0 items-center justify-center rounded-full bg-muted font-medium text-muted-foreground",
|
|
2049
|
+
pick(AVATAR_SIZE, props.size, "md")
|
|
2050
|
+
),
|
|
2051
|
+
"aria-label": name || void 0,
|
|
2052
|
+
role: "img",
|
|
2053
|
+
children: initialsOf(name)
|
|
2054
|
+
}
|
|
2055
|
+
);
|
|
2056
|
+
}
|
|
2057
|
+
var BADGE = {
|
|
2058
|
+
default: "bg-primary text-primary-foreground",
|
|
2059
|
+
secondary: "bg-secondary text-secondary-foreground",
|
|
2060
|
+
destructive: "bg-destructive text-white",
|
|
2061
|
+
outline: "border border-border text-foreground"
|
|
2062
|
+
};
|
|
2063
|
+
function Badge({
|
|
2064
|
+
props
|
|
2065
|
+
}) {
|
|
2066
|
+
return /* @__PURE__ */ jsx(
|
|
2067
|
+
"span",
|
|
2068
|
+
{
|
|
2069
|
+
className: cn(
|
|
2070
|
+
"inline-flex w-fit items-center rounded-md px-2 py-0.5 text-xs font-medium",
|
|
2071
|
+
pick(BADGE, props.variant, "default")
|
|
2072
|
+
),
|
|
2073
|
+
children: props.text
|
|
2074
|
+
}
|
|
2075
|
+
);
|
|
2076
|
+
}
|
|
2077
|
+
var ALERT = {
|
|
2078
|
+
info: "border-border bg-muted text-foreground",
|
|
2079
|
+
success: "border-border bg-muted text-foreground",
|
|
2080
|
+
warning: "border-border bg-muted text-foreground",
|
|
2081
|
+
error: "border-destructive/50 bg-destructive/10 text-foreground"
|
|
2082
|
+
};
|
|
2083
|
+
function Alert({
|
|
2084
|
+
props
|
|
2085
|
+
}) {
|
|
2086
|
+
const type = pickKey(ALERT, props.type, "info");
|
|
2087
|
+
return /* @__PURE__ */ jsxs(
|
|
2088
|
+
"div",
|
|
2089
|
+
{
|
|
2090
|
+
role: type === "error" ? "alert" : "status",
|
|
2091
|
+
className: cn("rounded-md border px-4 py-3 text-sm", ALERT[type]),
|
|
2092
|
+
children: [
|
|
2093
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: props.title }),
|
|
2094
|
+
props.message ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-muted-foreground", children: props.message }) : null
|
|
2095
|
+
]
|
|
2096
|
+
}
|
|
2097
|
+
);
|
|
2098
|
+
}
|
|
2099
|
+
function Progress({
|
|
2100
|
+
props
|
|
2101
|
+
}) {
|
|
2102
|
+
const max = props.max ?? 100;
|
|
2103
|
+
const value = Math.min(Math.max(Number(props.value) || 0, 0), max);
|
|
2104
|
+
const percent = max > 0 ? value / max * 100 : 0;
|
|
2105
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
2106
|
+
props.label ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: props.label }) : null,
|
|
2107
|
+
/* @__PURE__ */ jsx(
|
|
2108
|
+
"div",
|
|
2109
|
+
{
|
|
2110
|
+
role: "progressbar",
|
|
2111
|
+
"aria-valuenow": value,
|
|
2112
|
+
"aria-valuemin": 0,
|
|
2113
|
+
"aria-valuemax": max,
|
|
2114
|
+
"aria-label": props.label ?? void 0,
|
|
2115
|
+
className: "h-2 w-full overflow-hidden rounded-full bg-muted",
|
|
2116
|
+
children: /* @__PURE__ */ jsx("div", { className: "h-full bg-primary transition-all", style: { width: `${percent}%` } })
|
|
2117
|
+
}
|
|
2118
|
+
)
|
|
2119
|
+
] });
|
|
2120
|
+
}
|
|
2121
|
+
function useControlId(name) {
|
|
2122
|
+
return `jr-${name}-${React3.useId()}`;
|
|
2123
|
+
}
|
|
2124
|
+
function Input({
|
|
2125
|
+
props,
|
|
2126
|
+
bindings,
|
|
2127
|
+
emit
|
|
2128
|
+
}) {
|
|
2129
|
+
const id = useControlId(props.name);
|
|
2130
|
+
const field = useBoundField(props.value, bindings?.value, "", props);
|
|
2131
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2132
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
|
|
2133
|
+
/* @__PURE__ */ jsx(
|
|
2134
|
+
"input",
|
|
2135
|
+
{
|
|
2136
|
+
id,
|
|
2137
|
+
name: props.name,
|
|
2138
|
+
type: props.type ?? "text",
|
|
2139
|
+
className: cn(fieldControlClass, "min-h-11 px-3 py-2"),
|
|
2140
|
+
placeholder: props.placeholder ?? void 0,
|
|
2141
|
+
value: field.value,
|
|
2142
|
+
"aria-invalid": field.error ? true : void 0,
|
|
2143
|
+
onChange: (event) => {
|
|
2144
|
+
field.setValue(event.target.value);
|
|
2145
|
+
field.validateOnChange();
|
|
2146
|
+
},
|
|
2147
|
+
onFocus: () => emit("focus"),
|
|
2148
|
+
onBlur: () => {
|
|
2149
|
+
field.validateOnBlur();
|
|
2150
|
+
emit("blur");
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
),
|
|
2154
|
+
/* @__PURE__ */ jsx(FieldError, { message: field.error })
|
|
2155
|
+
] });
|
|
2156
|
+
}
|
|
2157
|
+
function Textarea({
|
|
2158
|
+
props,
|
|
2159
|
+
bindings
|
|
2160
|
+
}) {
|
|
2161
|
+
const id = useControlId(props.name);
|
|
2162
|
+
const field = useBoundField(props.value, bindings?.value, "", props);
|
|
2163
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2164
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
|
|
2165
|
+
/* @__PURE__ */ jsx(
|
|
2166
|
+
"textarea",
|
|
2167
|
+
{
|
|
2168
|
+
id,
|
|
2169
|
+
name: props.name,
|
|
2170
|
+
rows: props.rows ?? 4,
|
|
2171
|
+
className: cn(fieldControlClass, "px-3 py-2"),
|
|
2172
|
+
placeholder: props.placeholder ?? void 0,
|
|
2173
|
+
value: field.value,
|
|
2174
|
+
"aria-invalid": field.error ? true : void 0,
|
|
2175
|
+
onChange: (event) => {
|
|
2176
|
+
field.setValue(event.target.value);
|
|
2177
|
+
field.validateOnChange();
|
|
2178
|
+
},
|
|
2179
|
+
onBlur: field.validateOnBlur
|
|
2180
|
+
}
|
|
2181
|
+
),
|
|
2182
|
+
/* @__PURE__ */ jsx(FieldError, { message: field.error })
|
|
2183
|
+
] });
|
|
2184
|
+
}
|
|
2185
|
+
function Radio({
|
|
2186
|
+
props,
|
|
2187
|
+
bindings,
|
|
2188
|
+
emit
|
|
2189
|
+
}) {
|
|
2190
|
+
const id = useControlId(props.name);
|
|
2191
|
+
const field = useBoundField(props.value, bindings?.value, "", props);
|
|
2192
|
+
return /* @__PURE__ */ jsxs("fieldset", { className: "space-y-2", "aria-invalid": field.error ? true : void 0, children: [
|
|
2193
|
+
/* @__PURE__ */ jsx("legend", { className: LABEL, children: props.label }),
|
|
2194
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
2195
|
+
/* @__PURE__ */ jsx(
|
|
2196
|
+
"input",
|
|
2197
|
+
{
|
|
2198
|
+
type: "radio",
|
|
2199
|
+
name: id,
|
|
2200
|
+
value: option,
|
|
2201
|
+
checked: field.value === option,
|
|
2202
|
+
className: "size-4 accent-primary",
|
|
2203
|
+
onChange: () => {
|
|
2204
|
+
field.setValue(option);
|
|
2205
|
+
field.validateOnChange();
|
|
2206
|
+
emit("change");
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
),
|
|
2210
|
+
/* @__PURE__ */ jsx("span", { children: option })
|
|
2211
|
+
] }, `${index}-${option}`)) }),
|
|
2212
|
+
/* @__PURE__ */ jsx(FieldError, { message: field.error })
|
|
2213
|
+
] });
|
|
2214
|
+
}
|
|
2215
|
+
function Checkbox({
|
|
2216
|
+
props,
|
|
2217
|
+
bindings,
|
|
2218
|
+
emit
|
|
2219
|
+
}) {
|
|
2220
|
+
const id = useControlId(props.name);
|
|
2221
|
+
const field = useBoundField(props.checked, bindings?.checked, false, props);
|
|
2222
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2223
|
+
/* @__PURE__ */ jsxs("label", { htmlFor: id, className: "flex items-center gap-2 text-sm", children: [
|
|
2224
|
+
/* @__PURE__ */ jsx(
|
|
2225
|
+
"input",
|
|
2226
|
+
{
|
|
2227
|
+
id,
|
|
2228
|
+
name: props.name,
|
|
2229
|
+
type: "checkbox",
|
|
2230
|
+
className: "size-4 accent-primary",
|
|
2231
|
+
checked: field.value,
|
|
2232
|
+
"aria-invalid": field.error ? true : void 0,
|
|
2233
|
+
onChange: (event) => {
|
|
2234
|
+
field.setValue(event.target.checked);
|
|
2235
|
+
field.validateOnChange();
|
|
2236
|
+
emit("change");
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
),
|
|
2240
|
+
/* @__PURE__ */ jsx("span", { children: props.label })
|
|
2241
|
+
] }),
|
|
2242
|
+
/* @__PURE__ */ jsx(FieldError, { message: field.error })
|
|
2243
|
+
] });
|
|
2244
|
+
}
|
|
2245
|
+
function Switch2({
|
|
2246
|
+
props,
|
|
2247
|
+
bindings,
|
|
2248
|
+
emit
|
|
2249
|
+
}) {
|
|
2250
|
+
const id = useControlId(props.name);
|
|
2251
|
+
const field = useBoundField(props.checked, bindings?.checked, false, props);
|
|
2252
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2253
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2254
|
+
/* @__PURE__ */ jsx(
|
|
2255
|
+
Switch,
|
|
2256
|
+
{
|
|
2257
|
+
id,
|
|
2258
|
+
name: props.name,
|
|
2259
|
+
checked: field.value,
|
|
2260
|
+
"aria-invalid": field.error ? true : void 0,
|
|
2261
|
+
onCheckedChange: (next) => {
|
|
2262
|
+
field.setValue(next);
|
|
2263
|
+
field.validateOnChange();
|
|
2264
|
+
emit("change");
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
),
|
|
2268
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm", children: props.label })
|
|
2269
|
+
] }),
|
|
2270
|
+
/* @__PURE__ */ jsx(FieldError, { message: field.error })
|
|
2271
|
+
] });
|
|
2272
|
+
}
|
|
2273
|
+
var BUTTON = {
|
|
2274
|
+
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
|
|
2275
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
2276
|
+
danger: "bg-destructive text-white hover:bg-destructive/90"
|
|
2277
|
+
};
|
|
2278
|
+
function Button({
|
|
2279
|
+
props,
|
|
2280
|
+
emit
|
|
2281
|
+
}) {
|
|
2282
|
+
return /* @__PURE__ */ jsx(
|
|
2283
|
+
"button",
|
|
2284
|
+
{
|
|
2285
|
+
type: "button",
|
|
2286
|
+
disabled: props.disabled ?? false,
|
|
2287
|
+
className: cn(
|
|
2288
|
+
"inline-flex min-h-11 w-fit items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors",
|
|
2289
|
+
"focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
|
|
2290
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
2291
|
+
pick(BUTTON, props.variant, "primary")
|
|
2292
|
+
),
|
|
2293
|
+
onClick: () => emit("press"),
|
|
2294
|
+
children: props.label
|
|
2295
|
+
}
|
|
2296
|
+
);
|
|
2297
|
+
}
|
|
2298
|
+
var DescriptorContext = React3.createContext({});
|
|
2299
|
+
function useDescriptorScope() {
|
|
2300
|
+
return React3.useContext(DescriptorContext);
|
|
2301
|
+
}
|
|
2302
|
+
function DescriptorProvider({
|
|
2303
|
+
scope,
|
|
2304
|
+
children
|
|
2305
|
+
}) {
|
|
2306
|
+
return /* @__PURE__ */ jsx(DescriptorContext.Provider, { value: scope, children });
|
|
2307
|
+
}
|
|
2308
|
+
function Unresolved({ path, side }) {
|
|
2309
|
+
return /* @__PURE__ */ jsxs(
|
|
2310
|
+
"p",
|
|
2311
|
+
{
|
|
2312
|
+
role: "alert",
|
|
2313
|
+
className: "rounded-md border border-destructive/50 px-2.5 py-1.5 text-[12px] text-destructive",
|
|
2314
|
+
children: [
|
|
2315
|
+
side === "input" ? "No input" : "No result",
|
|
2316
|
+
" at ",
|
|
2317
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: path })
|
|
2318
|
+
]
|
|
2319
|
+
}
|
|
2320
|
+
);
|
|
2321
|
+
}
|
|
2322
|
+
function encodeSegment(segment) {
|
|
2323
|
+
return segment.replace(/_/g, "_u").replace(/-/g, "_d").replace(/~/g, "_t");
|
|
2324
|
+
}
|
|
2325
|
+
function decodeSegment(segment) {
|
|
2326
|
+
return segment.replace(
|
|
2327
|
+
/_([udt])/g,
|
|
2328
|
+
(_match, code) => code === "u" ? "_" : code === "d" ? "-" : "~"
|
|
2329
|
+
);
|
|
2330
|
+
}
|
|
2331
|
+
function domIdFor(prefix, path) {
|
|
2332
|
+
return `${prefix ?? "gen"}${path.split("/").map(encodeSegment).join("-")}`;
|
|
2333
|
+
}
|
|
2334
|
+
function pathFromDomId(prefix, id) {
|
|
2335
|
+
const head = prefix ?? "gen";
|
|
2336
|
+
if (!id.startsWith(head)) return void 0;
|
|
2337
|
+
const [own = "", ...children] = id.slice(head.length).split(".");
|
|
2338
|
+
if (!own.startsWith("-")) return void 0;
|
|
2339
|
+
const path = own.split("-").map(decodeSegment).join("/");
|
|
2340
|
+
return `${path}${children.map((child) => `/${child}`).join("")}`;
|
|
2341
|
+
}
|
|
2342
|
+
function useAbsolutePath(path) {
|
|
2343
|
+
const repeat = useRepeatScope();
|
|
2344
|
+
if (path.startsWith("/")) return path;
|
|
2345
|
+
return repeat ? `${repeat.basePath}/${path}` : void 0;
|
|
2346
|
+
}
|
|
2347
|
+
function MthdsField({ props }) {
|
|
2348
|
+
const scope = React3.useContext(DescriptorContext);
|
|
2349
|
+
const path = useAbsolutePath(props.path) ?? "";
|
|
2350
|
+
const field = scope.inputs && path ? inputFieldAtPath(scope.inputs, path) : void 0;
|
|
2351
|
+
const value = useStateValue(path);
|
|
2352
|
+
const { set } = useStateStore();
|
|
2353
|
+
const onChange = React3.useCallback((next) => set(path, next), [set, path]);
|
|
2354
|
+
if (!field) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "input" });
|
|
2355
|
+
return /* @__PURE__ */ jsx(
|
|
2356
|
+
FieldRenderer,
|
|
2357
|
+
{
|
|
2358
|
+
field,
|
|
2359
|
+
value,
|
|
2360
|
+
onChange,
|
|
2361
|
+
id: domIdFor(scope.idPrefix, path),
|
|
2362
|
+
env: scope.env
|
|
2363
|
+
}
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
function MthdsResult({ props }) {
|
|
2367
|
+
const scope = React3.useContext(DescriptorContext);
|
|
2368
|
+
const path = useAbsolutePath(props.path) ?? "";
|
|
2369
|
+
const field = scope.result && path ? resultFieldAtPath(scope.result, path) : void 0;
|
|
2370
|
+
const value = useStateValue(path);
|
|
2371
|
+
if (!field) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "result" });
|
|
2372
|
+
return /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: props.hideLabel ?? false });
|
|
2373
|
+
}
|
|
2374
|
+
function AccessibleSelect({ props, bindings, emit }) {
|
|
2375
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
2376
|
+
props.value ?? void 0,
|
|
2377
|
+
bindings?.value
|
|
2378
|
+
);
|
|
2379
|
+
const [localValue, setLocalValue] = React3.useState("");
|
|
2380
|
+
const isBound = Boolean(bindings?.value);
|
|
2381
|
+
const value = isBound ? boundValue ?? "" : localValue;
|
|
2382
|
+
const setValue = isBound ? setBoundValue : setLocalValue;
|
|
2383
|
+
const validateOn = props.validateOn ?? "change";
|
|
2384
|
+
const hasValidation = Boolean(bindings?.value && props.checks?.length);
|
|
2385
|
+
const { errors, validate } = useFieldValidation(
|
|
2386
|
+
bindings?.value ?? "",
|
|
2387
|
+
hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
|
|
2388
|
+
);
|
|
2389
|
+
const id = useControlId(props.name);
|
|
2390
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2391
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm leading-none font-medium", children: props.label }),
|
|
2392
|
+
/* @__PURE__ */ jsxs(
|
|
2393
|
+
Select,
|
|
2394
|
+
{
|
|
2395
|
+
value,
|
|
2396
|
+
onValueChange: (next) => {
|
|
2397
|
+
setValue(next);
|
|
2398
|
+
if (hasValidation && validateOn === "change") validate();
|
|
2399
|
+
emit("change");
|
|
2400
|
+
},
|
|
2401
|
+
children: [
|
|
2402
|
+
/* @__PURE__ */ jsx(SelectTrigger, { id, className: "w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: props.placeholder ?? "Select..." }) }),
|
|
2403
|
+
/* @__PURE__ */ jsx(SelectContent, { children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option }, `${index}-${option}`)) })
|
|
2404
|
+
]
|
|
2405
|
+
}
|
|
2406
|
+
),
|
|
2407
|
+
errors.length > 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errors[0] }) : null
|
|
2408
|
+
] });
|
|
2409
|
+
}
|
|
2410
|
+
function valueAt(row, relativePath) {
|
|
2411
|
+
let current = row;
|
|
2412
|
+
for (const segment of relativePath.split("/").filter((part) => part.length > 0)) {
|
|
2413
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
2414
|
+
current = current[segment];
|
|
2415
|
+
}
|
|
2416
|
+
return current;
|
|
2417
|
+
}
|
|
2418
|
+
var CELL_NUMBER = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
|
|
2419
|
+
function formatCell(value) {
|
|
2420
|
+
if (value === null || value === void 0 || value === "") return "\u2014";
|
|
2421
|
+
if (typeof value === "boolean") return value ? "yes" : "no";
|
|
2422
|
+
if (typeof value === "number") return CELL_NUMBER.format(value);
|
|
2423
|
+
if (typeof value === "string") return value;
|
|
2424
|
+
if (Array.isArray(value)) return value.map(formatCell).join(", ");
|
|
2425
|
+
return JSON.stringify(value);
|
|
2426
|
+
}
|
|
2427
|
+
function DataTable({ props }) {
|
|
2428
|
+
const rows = Array.isArray(props.rows) ? props.rows : [];
|
|
2429
|
+
const columns = props.columns ?? [];
|
|
2430
|
+
return /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-md border border-border", children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-full border-collapse text-[13px]", children: [
|
|
2431
|
+
props.caption ? /* @__PURE__ */ jsx("caption", { className: "px-2.5 py-1.5 text-start text-[12px] text-muted-foreground", children: props.caption }) : null,
|
|
2432
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-muted", children: columns.map((column) => /* @__PURE__ */ jsx(
|
|
2433
|
+
"th",
|
|
2434
|
+
{
|
|
2435
|
+
scope: "col",
|
|
2436
|
+
className: "whitespace-nowrap px-2.5 py-1.5 text-start font-semibold text-foreground",
|
|
2437
|
+
children: column.label
|
|
2438
|
+
},
|
|
2439
|
+
column.path
|
|
2440
|
+
)) }) }),
|
|
2441
|
+
/* @__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: formatCell(valueAt(row, column.path)) }, column.path)) }, index)) })
|
|
2442
|
+
] }) });
|
|
2443
|
+
}
|
|
2444
|
+
function numberFormat(format) {
|
|
2445
|
+
switch (format) {
|
|
2446
|
+
case "integer":
|
|
2447
|
+
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
|
|
2448
|
+
case "decimal":
|
|
2449
|
+
return new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
2450
|
+
case "compact":
|
|
2451
|
+
return new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
|
|
2452
|
+
case "plain":
|
|
2453
|
+
return new Intl.NumberFormat("en-US", { maximumFractionDigits: 20, useGrouping: false });
|
|
2454
|
+
default:
|
|
2455
|
+
return format;
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
function Metric({ props }) {
|
|
2459
|
+
const value = props.value;
|
|
2460
|
+
const text = typeof value === "number" ? numberFormat(props.format ?? "plain").format(value) : value === null || value === void 0 || value === "" ? "\u2014" : String(value);
|
|
2461
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
2462
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: props.label }),
|
|
2463
|
+
/* @__PURE__ */ jsxs("span", { className: "text-2xl font-semibold tabular-nums text-foreground", children: [
|
|
2464
|
+
text,
|
|
2465
|
+
props.unit ? /* @__PURE__ */ jsx("span", { className: "ml-1 text-sm font-normal text-muted-foreground", children: props.unit }) : null
|
|
2466
|
+
] })
|
|
2467
|
+
] });
|
|
2468
|
+
}
|
|
2469
|
+
var SPLIT_COLUMNS = {
|
|
2470
|
+
"1:1": "sm:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]",
|
|
2471
|
+
"1:2": "sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)]",
|
|
2472
|
+
"2:1": "sm:grid-cols-[minmax(0,2fr)_minmax(0,1fr)]"
|
|
2473
|
+
};
|
|
2474
|
+
var SPLIT_GAP = { md: "gap-4", lg: "gap-6", xl: "gap-8" };
|
|
2475
|
+
function Split({
|
|
2476
|
+
props,
|
|
2477
|
+
children
|
|
2478
|
+
}) {
|
|
2479
|
+
return /* @__PURE__ */ jsx(
|
|
2480
|
+
"div",
|
|
2481
|
+
{
|
|
2482
|
+
className: cn(
|
|
2483
|
+
"grid grid-cols-1 items-start",
|
|
2484
|
+
pick(SPLIT_COLUMNS, props.ratio, "1:2"),
|
|
2485
|
+
pick(SPLIT_GAP, props.gap, "lg")
|
|
2486
|
+
),
|
|
2487
|
+
children
|
|
2488
|
+
}
|
|
2489
|
+
);
|
|
2490
|
+
}
|
|
2491
|
+
function Tabs({ props, children }) {
|
|
2492
|
+
const tabs = props.tabs ?? [];
|
|
2493
|
+
const panels = React3.Children.toArray(children);
|
|
2494
|
+
const [value, setValue] = React3.useState(tabs[0]?.value ?? "");
|
|
2495
|
+
return /* @__PURE__ */ jsxs(TabsPrimitive.Root, { value, onValueChange: setValue, className: "w-full", children: [
|
|
2496
|
+
/* @__PURE__ */ jsx(TabsPrimitive.List, { className: "inline-flex h-10 items-center justify-start rounded-md bg-muted p-1 text-muted-foreground", children: tabs.map((tab) => /* @__PURE__ */ jsx(
|
|
2497
|
+
TabsPrimitive.Trigger,
|
|
2498
|
+
{
|
|
2499
|
+
value: tab.value,
|
|
2500
|
+
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",
|
|
2501
|
+
children: tab.label
|
|
2502
|
+
},
|
|
2503
|
+
tab.value
|
|
2504
|
+
)) }),
|
|
2505
|
+
tabs.map((tab, index) => /* @__PURE__ */ jsx(
|
|
2506
|
+
TabsPrimitive.Content,
|
|
2507
|
+
{
|
|
2508
|
+
value: tab.value,
|
|
2509
|
+
className: "mt-4 focus-visible:outline-hidden",
|
|
2510
|
+
children: panels[index] ?? null
|
|
2511
|
+
},
|
|
2512
|
+
tab.value
|
|
2513
|
+
))
|
|
2514
|
+
] });
|
|
2515
|
+
}
|
|
2516
|
+
var BUTTON_BASE = "inline-flex h-10 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";
|
|
2517
|
+
var PRIMARY_BUTTON = cn(BUTTON_BASE, "bg-primary text-primary-foreground hover:bg-primary/90");
|
|
2518
|
+
var SECONDARY_BUTTON = cn(
|
|
2519
|
+
BUTTON_BASE,
|
|
2520
|
+
"border border-border bg-background text-foreground hover:bg-accent hover:text-accent-foreground"
|
|
2521
|
+
);
|
|
2522
|
+
function Steps({ props, children }) {
|
|
2523
|
+
const steps = props.steps ?? [];
|
|
2524
|
+
const panels = React3.Children.toArray(children);
|
|
2525
|
+
const [index, setIndex] = React3.useState(0);
|
|
2526
|
+
const last = steps.length - 1;
|
|
2527
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
|
|
2528
|
+
/* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: steps.map((label, position) => /* @__PURE__ */ jsxs(
|
|
2529
|
+
"li",
|
|
2530
|
+
{
|
|
2531
|
+
"aria-current": position === index ? "step" : void 0,
|
|
2532
|
+
className: cn(
|
|
2533
|
+
"flex items-center gap-2 text-sm",
|
|
2534
|
+
position === index ? "font-medium text-foreground" : "text-muted-foreground"
|
|
2535
|
+
),
|
|
2536
|
+
children: [
|
|
2537
|
+
/* @__PURE__ */ jsx(
|
|
2538
|
+
"span",
|
|
2539
|
+
{
|
|
2540
|
+
className: cn(
|
|
2541
|
+
"flex h-6 w-6 items-center justify-center rounded-full text-[12px] font-semibold tabular-nums",
|
|
2542
|
+
position < index ? "bg-primary/15 text-primary" : position === index ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
|
2543
|
+
),
|
|
2544
|
+
children: position + 1
|
|
2545
|
+
}
|
|
2546
|
+
),
|
|
2547
|
+
label
|
|
2548
|
+
]
|
|
2549
|
+
},
|
|
2550
|
+
`${position}-${label}`
|
|
2551
|
+
)) }),
|
|
2552
|
+
/* @__PURE__ */ jsx("div", { children: panels[index] ?? null }),
|
|
2553
|
+
index > 0 || index < last ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
|
|
2554
|
+
index > 0 ? /* @__PURE__ */ jsx(
|
|
2555
|
+
"button",
|
|
2556
|
+
{
|
|
2557
|
+
type: "button",
|
|
2558
|
+
"data-testid": "steps-back",
|
|
2559
|
+
onClick: () => setIndex((current) => Math.max(0, current - 1)),
|
|
2560
|
+
className: SECONDARY_BUTTON,
|
|
2561
|
+
children: props.backLabel ?? "Back"
|
|
2562
|
+
}
|
|
2563
|
+
) : /* @__PURE__ */ jsx("span", {}),
|
|
2564
|
+
index < last ? /* @__PURE__ */ jsx(
|
|
2565
|
+
"button",
|
|
2566
|
+
{
|
|
2567
|
+
type: "button",
|
|
2568
|
+
"data-testid": "steps-next",
|
|
2569
|
+
onClick: () => setIndex((current) => Math.min(last, current + 1)),
|
|
2570
|
+
className: PRIMARY_BUTTON,
|
|
2571
|
+
children: props.nextLabel ?? "Next"
|
|
2572
|
+
}
|
|
2573
|
+
) : null
|
|
2574
|
+
] }) : null
|
|
2575
|
+
] });
|
|
2576
|
+
}
|
|
2577
|
+
var ICONS = {
|
|
2578
|
+
Sparkles,
|
|
2579
|
+
Rocket,
|
|
2580
|
+
Compass,
|
|
2581
|
+
Map: Map$1,
|
|
2582
|
+
MapPin,
|
|
2583
|
+
Route,
|
|
2584
|
+
Plane,
|
|
2585
|
+
Train,
|
|
2586
|
+
Car,
|
|
2587
|
+
Ship,
|
|
2588
|
+
Hotel,
|
|
2589
|
+
Home,
|
|
2590
|
+
Building2,
|
|
2591
|
+
Landmark,
|
|
2592
|
+
Globe,
|
|
2593
|
+
Mountain,
|
|
2594
|
+
Leaf,
|
|
2595
|
+
Sun,
|
|
2596
|
+
Moon,
|
|
2597
|
+
Calendar,
|
|
2598
|
+
Clock,
|
|
2599
|
+
Hourglass,
|
|
2600
|
+
Users,
|
|
2601
|
+
User,
|
|
2602
|
+
Baby,
|
|
2603
|
+
Accessibility,
|
|
2604
|
+
Heart,
|
|
2605
|
+
Star,
|
|
2606
|
+
Utensils,
|
|
2607
|
+
Coffee,
|
|
2608
|
+
Music,
|
|
2609
|
+
Camera,
|
|
2610
|
+
Image,
|
|
2611
|
+
Ticket,
|
|
2612
|
+
Gift,
|
|
2613
|
+
Wallet,
|
|
2614
|
+
Banknote,
|
|
2615
|
+
Coins,
|
|
2616
|
+
Receipt,
|
|
2617
|
+
Percent,
|
|
2618
|
+
Hash,
|
|
2619
|
+
TrendingUp,
|
|
2620
|
+
BarChart3,
|
|
2621
|
+
FileText,
|
|
2622
|
+
Upload,
|
|
2623
|
+
Paperclip,
|
|
2624
|
+
Mail,
|
|
2625
|
+
Phone,
|
|
2626
|
+
Tag,
|
|
2627
|
+
Layers,
|
|
2628
|
+
ListChecks,
|
|
2629
|
+
CheckCircle2,
|
|
2630
|
+
AlertCircle,
|
|
2631
|
+
Info,
|
|
2632
|
+
Shield,
|
|
2633
|
+
Flag,
|
|
2634
|
+
Bookmark,
|
|
2635
|
+
Briefcase,
|
|
2636
|
+
Package,
|
|
2637
|
+
Truck,
|
|
2638
|
+
Lightbulb,
|
|
2639
|
+
Zap,
|
|
2640
|
+
Pencil,
|
|
2641
|
+
Send
|
|
2642
|
+
};
|
|
2643
|
+
function Icon({ props }) {
|
|
2644
|
+
const Glyph = ICONS[props.name];
|
|
2645
|
+
if (!Glyph) return null;
|
|
2646
|
+
const size = props.size === "lg" ? "h-7 w-7" : props.size === "sm" ? "h-4 w-4" : "h-5 w-5";
|
|
2647
|
+
return /* @__PURE__ */ jsx(Glyph, { "aria-hidden": "true", className: cn(size, "shrink-0 text-muted-foreground") });
|
|
2648
|
+
}
|
|
2649
|
+
function Segmented({ props, bindings, emit }) {
|
|
2650
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
2651
|
+
props.value ?? void 0,
|
|
2652
|
+
bindings?.value
|
|
2653
|
+
);
|
|
2654
|
+
const [localValue, setLocalValue] = React3.useState("");
|
|
2655
|
+
const isBound = Boolean(bindings?.value);
|
|
2656
|
+
const value = isBound ? boundValue ?? "" : localValue;
|
|
2657
|
+
const setValue = isBound ? setBoundValue : setLocalValue;
|
|
2658
|
+
const labelId = useControlId(props.name);
|
|
2659
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2660
|
+
/* @__PURE__ */ jsx("span", { id: labelId, className: "block text-sm leading-none font-medium", children: props.label }),
|
|
2661
|
+
/* @__PURE__ */ jsx(
|
|
2662
|
+
ToggleGroup,
|
|
2663
|
+
{
|
|
2664
|
+
type: "single",
|
|
2665
|
+
"aria-labelledby": labelId,
|
|
2666
|
+
value,
|
|
2667
|
+
onValueChange: (next) => {
|
|
2668
|
+
if (!next) return;
|
|
2669
|
+
setValue(next);
|
|
2670
|
+
emit("change");
|
|
2671
|
+
},
|
|
2672
|
+
className: "flex-wrap justify-start gap-1.5",
|
|
2673
|
+
children: (props.options ?? []).map((option) => /* @__PURE__ */ jsx(
|
|
2674
|
+
ToggleGroupItem,
|
|
2675
|
+
{
|
|
2676
|
+
value: option,
|
|
2677
|
+
variant: "outline",
|
|
2678
|
+
size: "sm",
|
|
2679
|
+
className: "rounded-full px-3.5 data-[state=on]:border-primary data-[state=on]:bg-primary data-[state=on]:text-primary-foreground",
|
|
2680
|
+
children: option
|
|
2681
|
+
},
|
|
2682
|
+
option
|
|
2683
|
+
))
|
|
2684
|
+
}
|
|
2685
|
+
)
|
|
2686
|
+
] });
|
|
2687
|
+
}
|
|
2688
|
+
function NumberInput({ props, bindings, emit }) {
|
|
2689
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
2690
|
+
props.value ?? void 0,
|
|
2691
|
+
bindings?.value
|
|
2692
|
+
);
|
|
2693
|
+
const [localValue, setLocalValue] = React3.useState(void 0);
|
|
2694
|
+
const isBound = Boolean(bindings?.value);
|
|
2695
|
+
const value = isBound ? boundValue : localValue;
|
|
2696
|
+
const setValue = isBound ? setBoundValue : setLocalValue;
|
|
2697
|
+
const id = useControlId(props.name);
|
|
2698
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2699
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm leading-none font-medium", children: props.label }),
|
|
2700
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2701
|
+
/* @__PURE__ */ jsx(
|
|
2702
|
+
"input",
|
|
2703
|
+
{
|
|
2704
|
+
id,
|
|
2705
|
+
type: "number",
|
|
2706
|
+
inputMode: "decimal",
|
|
2707
|
+
value: typeof value === "number" ? value : "",
|
|
2708
|
+
min: props.min ?? void 0,
|
|
2709
|
+
max: props.max ?? void 0,
|
|
2710
|
+
step: props.step ?? "any",
|
|
2711
|
+
placeholder: props.placeholder ?? void 0,
|
|
2712
|
+
onChange: (event) => {
|
|
2713
|
+
const raw = event.target.value;
|
|
2714
|
+
setValue(raw === "" ? void 0 : Number(raw));
|
|
2715
|
+
emit("change");
|
|
2716
|
+
},
|
|
2717
|
+
className: cn(
|
|
2718
|
+
fieldControlClass,
|
|
2719
|
+
"h-10 px-3 tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
|
|
2720
|
+
)
|
|
2721
|
+
}
|
|
2722
|
+
),
|
|
2723
|
+
props.unit ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: props.unit }) : null
|
|
2724
|
+
] })
|
|
2725
|
+
] });
|
|
2726
|
+
}
|
|
2727
|
+
var components2 = {
|
|
2728
|
+
Card: (ctx) => /* @__PURE__ */ jsx(Card, { ...ctx }),
|
|
2729
|
+
// A vertical Stack stretches its children unless the spec says otherwise:
|
|
2730
|
+
// hugging the content is the one layout a result page never wants, since a
|
|
2731
|
+
// table or a grid inside it would stop short of the page's width.
|
|
2732
|
+
Stack: (ctx) => /* @__PURE__ */ jsx(Stack, { ...ctx }),
|
|
2733
|
+
Grid: (ctx) => /* @__PURE__ */ jsx(Grid, { ...ctx }),
|
|
2734
|
+
Separator: (ctx) => /* @__PURE__ */ jsx(Separator, { ...ctx }),
|
|
2735
|
+
Collapsible: (ctx) => /* @__PURE__ */ jsx(Collapsible, { ...ctx }),
|
|
2736
|
+
Heading: (ctx) => /* @__PURE__ */ jsx(Heading, { ...ctx }),
|
|
2737
|
+
Text: (ctx) => /* @__PURE__ */ jsx(Text, { ...ctx }),
|
|
2738
|
+
Badge: (ctx) => /* @__PURE__ */ jsx(Badge, { ...ctx }),
|
|
2739
|
+
Alert: (ctx) => /* @__PURE__ */ jsx(Alert, { ...ctx }),
|
|
2740
|
+
Progress: (ctx) => /* @__PURE__ */ jsx(Progress, { ...ctx }),
|
|
2741
|
+
Avatar: (ctx) => /* @__PURE__ */ jsx(Avatar, { ...ctx }),
|
|
2742
|
+
Input: (ctx) => /* @__PURE__ */ jsx(Input, { ...ctx }),
|
|
2743
|
+
Textarea: (ctx) => /* @__PURE__ */ jsx(Textarea, { ...ctx }),
|
|
2744
|
+
// A plain label beside a trigger leaves the trigger with no accessible name,
|
|
2745
|
+
// which axe reports as a button without one (`a11y` runs at error). This one
|
|
2746
|
+
// is the package's own vendored select under a label that names the trigger.
|
|
2747
|
+
Select: (ctx) => /* @__PURE__ */ jsx(AccessibleSelect, { ...ctx }),
|
|
2748
|
+
Radio: (ctx) => /* @__PURE__ */ jsx(Radio, { ...ctx }),
|
|
2749
|
+
Checkbox: (ctx) => /* @__PURE__ */ jsx(Checkbox, { ...ctx }),
|
|
2750
|
+
Switch: (ctx) => /* @__PURE__ */ jsx(Switch2, { ...ctx }),
|
|
2751
|
+
Button: (ctx) => /* @__PURE__ */ jsx(Button, { ...ctx }),
|
|
2752
|
+
Split: (ctx) => /* @__PURE__ */ jsx(Split, { ...ctx }),
|
|
2753
|
+
Tabs: (ctx) => /* @__PURE__ */ jsx(Tabs, { ...ctx }),
|
|
2754
|
+
Steps: (ctx) => /* @__PURE__ */ jsx(Steps, { ...ctx }),
|
|
2755
|
+
Icon: (ctx) => /* @__PURE__ */ jsx(Icon, { ...ctx }),
|
|
2756
|
+
Segmented: (ctx) => /* @__PURE__ */ jsx(Segmented, { ...ctx }),
|
|
2757
|
+
NumberInput: (ctx) => /* @__PURE__ */ jsx(NumberInput, { ...ctx }),
|
|
2758
|
+
// Ours are React components (they use hooks), so each is MOUNTED rather than
|
|
2759
|
+
// called: a render function that called hooks itself would run them inside
|
|
2760
|
+
// the registry's wrapper and break the moment the wrapper re-ordered them.
|
|
2761
|
+
MthdsField: (ctx) => /* @__PURE__ */ jsx(MthdsField, { ...ctx }),
|
|
2762
|
+
MthdsResult: (ctx) => /* @__PURE__ */ jsx(MthdsResult, { ...ctx }),
|
|
2763
|
+
DataTable: (ctx) => /* @__PURE__ */ jsx(DataTable, { ...ctx }),
|
|
2764
|
+
Metric: (ctx) => /* @__PURE__ */ jsx(Metric, { ...ctx })
|
|
2765
|
+
};
|
|
2766
|
+
var generativeRenderers = components2;
|
|
2767
|
+
var ResultSlotContext = React3.createContext(null);
|
|
2768
|
+
function ResultSlotProvider({
|
|
2769
|
+
slot,
|
|
2770
|
+
children
|
|
2771
|
+
}) {
|
|
2772
|
+
return /* @__PURE__ */ jsx(ResultSlotContext.Provider, { value: slot, children });
|
|
2773
|
+
}
|
|
2774
|
+
function useResultSlot() {
|
|
2775
|
+
return React3.useContext(ResultSlotContext);
|
|
2776
|
+
}
|
|
2777
|
+
var CONTAINER = "mx-auto w-full max-w-6xl px-6 sm:px-8";
|
|
2778
|
+
var LABEL2 = "block text-[13.5px] leading-none font-medium text-foreground";
|
|
2779
|
+
function AppBar({ props }) {
|
|
2780
|
+
const brand = useBrand();
|
|
2781
|
+
return /* @__PURE__ */ jsx("header", { className: "border-b border-border", children: /* @__PURE__ */ jsxs("div", { className: cn(CONTAINER, "flex h-16 items-center gap-5"), children: [
|
|
2782
|
+
/* @__PURE__ */ jsx(
|
|
2783
|
+
"img",
|
|
2784
|
+
{
|
|
2785
|
+
src: brand.logo.onLight,
|
|
2786
|
+
alt: brand.name,
|
|
2787
|
+
referrerPolicy: "no-referrer",
|
|
2788
|
+
className: "h-7 w-auto dark:hidden"
|
|
2789
|
+
}
|
|
2790
|
+
),
|
|
2791
|
+
/* @__PURE__ */ jsx(
|
|
2792
|
+
"img",
|
|
2793
|
+
{
|
|
2794
|
+
src: brand.logo.onDark,
|
|
2795
|
+
alt: brand.name,
|
|
2796
|
+
referrerPolicy: "no-referrer",
|
|
2797
|
+
className: "hidden h-7 w-auto dark:block"
|
|
2798
|
+
}
|
|
2799
|
+
),
|
|
2800
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "h-5 w-px bg-border" }),
|
|
2801
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground/80", children: props.app }),
|
|
2802
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex min-w-0 items-center gap-6", children: [
|
|
2803
|
+
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(
|
|
2804
|
+
"a",
|
|
2805
|
+
{
|
|
2806
|
+
href: "#",
|
|
2807
|
+
onClick: (event) => event.preventDefault(),
|
|
2808
|
+
className: "text-sm text-muted-foreground transition-colors hover:text-foreground",
|
|
2809
|
+
children: link
|
|
2810
|
+
},
|
|
2811
|
+
link
|
|
2812
|
+
)) }) : null,
|
|
2813
|
+
props.tag ? /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate rounded-lg border border-primary/40 bg-primary/10 px-2.5 py-1 font-mono text-[11px] text-primary", children: props.tag }) : null
|
|
2814
|
+
] })
|
|
2815
|
+
] }) });
|
|
2816
|
+
}
|
|
2817
|
+
function Hero({ props }) {
|
|
2818
|
+
return /* @__PURE__ */ jsxs("section", { className: "pt-12 pb-10 sm:pt-14 sm:pb-12", children: [
|
|
2819
|
+
props.eyebrow ? /* @__PURE__ */ jsx("p", { className: "mb-4 text-[11px] font-medium tracking-[0.15em] text-primary uppercase", children: props.eyebrow }) : null,
|
|
2820
|
+
/* @__PURE__ */ jsx("h1", { className: "max-w-2xl text-4xl font-bold tracking-tight text-foreground sm:text-[44px] sm:leading-[1.15]", children: props.headline }),
|
|
2821
|
+
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-4 max-w-2xl text-[17px] leading-relaxed text-muted-foreground", children: props.lede }) : null
|
|
2822
|
+
] });
|
|
2823
|
+
}
|
|
2824
|
+
function Workspace({ props, children }) {
|
|
2825
|
+
const [work, rail] = React3.Children.toArray(children);
|
|
2826
|
+
const railLeft = props.rail === "left";
|
|
2827
|
+
return /* @__PURE__ */ jsxs(
|
|
2828
|
+
"div",
|
|
2829
|
+
{
|
|
2830
|
+
className: cn(
|
|
2831
|
+
CONTAINER,
|
|
2832
|
+
"grid grid-cols-1 gap-10 pb-16 lg:gap-14",
|
|
2833
|
+
railLeft ? "lg:grid-cols-[360px_minmax(0,1fr)]" : "lg:grid-cols-[minmax(0,1fr)_360px]"
|
|
2834
|
+
),
|
|
2835
|
+
children: [
|
|
2836
|
+
/* @__PURE__ */ jsxs("div", { className: cn("min-w-0", railLeft && "lg:order-2"), children: [
|
|
2837
|
+
work,
|
|
2838
|
+
/* @__PURE__ */ jsx(ResultPanel, {})
|
|
2839
|
+
] }),
|
|
2840
|
+
/* @__PURE__ */ jsx(
|
|
2841
|
+
"aside",
|
|
2842
|
+
{
|
|
2843
|
+
className: cn(
|
|
2844
|
+
"lg:sticky lg:top-6 lg:self-start lg:pt-12 sm:lg:pt-14",
|
|
2845
|
+
railLeft && "lg:order-1"
|
|
2846
|
+
),
|
|
2847
|
+
children: rail
|
|
2848
|
+
}
|
|
2849
|
+
)
|
|
2850
|
+
]
|
|
2851
|
+
}
|
|
2852
|
+
);
|
|
2853
|
+
}
|
|
2854
|
+
function ResultPanel() {
|
|
2855
|
+
const slot = useResultSlot();
|
|
2856
|
+
if (!slot?.content) return null;
|
|
2857
|
+
return /* @__PURE__ */ jsxs("section", { className: "border-t border-border py-9", "data-testid": "result-panel", "aria-live": "polite", children: [
|
|
2858
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-6 text-xl font-semibold tracking-tight text-foreground", children: slot.title ?? "Result" }),
|
|
2859
|
+
slot.content
|
|
2860
|
+
] });
|
|
2861
|
+
}
|
|
2862
|
+
function Section({ props, children }) {
|
|
2863
|
+
return /* @__PURE__ */ jsxs("section", { className: "border-t border-border py-9 first:border-t-0 first:pt-0", children: [
|
|
2864
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
2865
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-3", children: [
|
|
2866
|
+
props.number ? /* @__PURE__ */ jsx("span", { className: "font-mono text-[12px] text-primary", children: props.number }) : null,
|
|
2867
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold tracking-tight text-foreground", children: props.title })
|
|
2868
|
+
] }),
|
|
2869
|
+
props.lede ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: props.lede }) : null
|
|
2870
|
+
] }),
|
|
2871
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children })
|
|
2872
|
+
] });
|
|
2873
|
+
}
|
|
2874
|
+
function Rail({ props, children }) {
|
|
2875
|
+
return (
|
|
2876
|
+
// An arbitrary value reads the token directly, so it does NOT go through
|
|
2877
|
+
// the `@theme inline` mapping and does not inherit its fallback - it has to
|
|
2878
|
+
// carry its own, or a host that defines no tokens loses the declaration
|
|
2879
|
+
// outright. See src/styles/tailwind-entry.css.
|
|
2880
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-[calc(var(--radius,0.5rem)*1.5)] border border-border bg-card p-7 text-card-foreground shadow-xl", children: [
|
|
2881
|
+
/* @__PURE__ */ jsx("h2", { className: "text-[11px] font-semibold tracking-[0.15em] text-muted-foreground uppercase", children: props.title }),
|
|
2882
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex flex-col", children })
|
|
2883
|
+
] })
|
|
2884
|
+
);
|
|
2885
|
+
}
|
|
2886
|
+
function present(value) {
|
|
2887
|
+
return value !== void 0 && value !== null && value !== "";
|
|
2888
|
+
}
|
|
2889
|
+
function SummaryRow({ props }) {
|
|
2890
|
+
const text = [props.value, props.detail].filter(present).map(String).join(props.separator ?? " ");
|
|
2891
|
+
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: [
|
|
2892
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-[13px] text-muted-foreground", children: props.label }),
|
|
2893
|
+
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" })
|
|
2894
|
+
] });
|
|
2895
|
+
}
|
|
2896
|
+
function Cta({ props, emit }) {
|
|
2897
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-6", children: [
|
|
2898
|
+
/* @__PURE__ */ jsx(
|
|
2899
|
+
"button",
|
|
2900
|
+
{
|
|
2901
|
+
type: "button",
|
|
2902
|
+
onClick: () => emit("press"),
|
|
2903
|
+
className: cn(
|
|
2904
|
+
"inline-flex h-12 w-full items-center justify-center rounded-lg bg-primary px-6 text-sm font-semibold text-primary-foreground",
|
|
2905
|
+
// The fallback is not decoration here either - see Rail above.
|
|
2906
|
+
"shadow-[0_0_28px_color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_35%,transparent)] transition-all hover:bg-primary/90 hover:shadow-[0_0_44px_color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_50%,transparent)]",
|
|
2907
|
+
"focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-hidden"
|
|
2908
|
+
),
|
|
2909
|
+
children: props.label
|
|
2910
|
+
}
|
|
2911
|
+
),
|
|
2912
|
+
props.hint ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-center text-[12px] leading-relaxed text-muted-foreground", children: props.hint }) : null
|
|
2913
|
+
] });
|
|
2914
|
+
}
|
|
2915
|
+
function Footer({ props }) {
|
|
2916
|
+
return /* @__PURE__ */ jsx("footer", { className: "border-t border-border", children: /* @__PURE__ */ jsxs(
|
|
2917
|
+
"div",
|
|
2918
|
+
{
|
|
2919
|
+
className: cn(
|
|
2920
|
+
CONTAINER,
|
|
2921
|
+
// Wraps, and the tag may break anywhere: a pipe's code is one long
|
|
2922
|
+
// word, and at phone width an unbreakable one widened the document
|
|
2923
|
+
// past the viewport.
|
|
2924
|
+
"flex flex-wrap items-center justify-between gap-x-6 gap-y-2 py-6 text-[12px] text-muted-foreground"
|
|
2925
|
+
),
|
|
2926
|
+
children: [
|
|
2927
|
+
/* @__PURE__ */ jsx("span", { children: props.text }),
|
|
2928
|
+
props.tag ? /* @__PURE__ */ jsx("span", { className: "min-w-0 font-mono text-[11px] tracking-wider break-all text-muted-foreground/70 uppercase", children: props.tag }) : null
|
|
2929
|
+
]
|
|
2930
|
+
}
|
|
2931
|
+
) });
|
|
2932
|
+
}
|
|
2933
|
+
function useTextField(props, bindings) {
|
|
2934
|
+
const [boundValue, setBoundValue] = useBoundProp(
|
|
2935
|
+
props.value ?? void 0,
|
|
2936
|
+
bindings?.value
|
|
2937
|
+
);
|
|
2938
|
+
const [localValue, setLocalValue] = React3.useState("");
|
|
2939
|
+
const isBound = Boolean(bindings?.value);
|
|
2940
|
+
const value = isBound ? boundValue ?? "" : localValue;
|
|
2941
|
+
const setValue = isBound ? setBoundValue : setLocalValue;
|
|
2942
|
+
const validateOn = props.validateOn ?? "blur";
|
|
2943
|
+
const hasValidation = Boolean(bindings?.value && props.checks?.length);
|
|
2944
|
+
const { errors, validate } = useFieldValidation(
|
|
2945
|
+
bindings?.value ?? "",
|
|
2946
|
+
hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
|
|
2947
|
+
);
|
|
2948
|
+
const onChange = (next) => {
|
|
2949
|
+
setValue(next);
|
|
2950
|
+
if (hasValidation && validateOn === "change") validate();
|
|
2951
|
+
};
|
|
2952
|
+
const onBlur = () => {
|
|
2953
|
+
if (hasValidation && validateOn === "blur") validate();
|
|
2954
|
+
};
|
|
2955
|
+
return { value, errors, onChange, onBlur };
|
|
2956
|
+
}
|
|
2957
|
+
function BrandInput({
|
|
2958
|
+
props,
|
|
2959
|
+
bindings,
|
|
2960
|
+
emit
|
|
2961
|
+
}) {
|
|
2962
|
+
const field = useTextField(props, bindings);
|
|
2963
|
+
const id = useControlId(props.name);
|
|
2964
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2965
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL2, children: props.label }),
|
|
2966
|
+
/* @__PURE__ */ jsx(
|
|
2967
|
+
"input",
|
|
2968
|
+
{
|
|
2969
|
+
id,
|
|
2970
|
+
name: props.name,
|
|
2971
|
+
type: props.type ?? "text",
|
|
2972
|
+
value: field.value,
|
|
2973
|
+
placeholder: props.placeholder ?? "",
|
|
2974
|
+
"aria-invalid": field.errors.length > 0 || void 0,
|
|
2975
|
+
onChange: (event) => field.onChange(event.target.value),
|
|
2976
|
+
onBlur: () => {
|
|
2977
|
+
field.onBlur();
|
|
2978
|
+
emit("blur");
|
|
2979
|
+
},
|
|
2980
|
+
onFocus: () => emit("focus"),
|
|
2981
|
+
onKeyDown: (event) => {
|
|
2982
|
+
if (event.key === "Enter") emit("submit");
|
|
2983
|
+
},
|
|
2984
|
+
className: cn(fieldControlClass, "h-11 px-3.5")
|
|
2985
|
+
}
|
|
2986
|
+
),
|
|
2987
|
+
field.errors[0] ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
|
|
2988
|
+
] });
|
|
2989
|
+
}
|
|
2990
|
+
function BrandTextarea({
|
|
2991
|
+
props,
|
|
2992
|
+
bindings
|
|
2993
|
+
}) {
|
|
2994
|
+
const field = useTextField(props, bindings);
|
|
2995
|
+
const id = useControlId(props.name);
|
|
2996
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2997
|
+
/* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL2, children: props.label }),
|
|
2998
|
+
/* @__PURE__ */ jsx(
|
|
2999
|
+
"textarea",
|
|
3000
|
+
{
|
|
3001
|
+
id,
|
|
3002
|
+
name: props.name,
|
|
3003
|
+
rows: props.rows ?? 3,
|
|
3004
|
+
value: field.value,
|
|
3005
|
+
placeholder: props.placeholder ?? "",
|
|
3006
|
+
"aria-invalid": field.errors.length > 0 || void 0,
|
|
3007
|
+
onChange: (event) => field.onChange(event.target.value),
|
|
3008
|
+
onBlur: field.onBlur,
|
|
3009
|
+
className: cn(fieldControlClass, "px-3.5 py-2.5 leading-relaxed")
|
|
3010
|
+
}
|
|
3011
|
+
),
|
|
3012
|
+
field.errors[0] ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
|
|
3013
|
+
] });
|
|
3014
|
+
}
|
|
3015
|
+
var { registry: generativeRegistry } = defineRegistry(catalog, {
|
|
3016
|
+
components: {
|
|
3017
|
+
...generativeRenderers,
|
|
3018
|
+
Input: (ctx) => /* @__PURE__ */ jsx(BrandInput, { ...ctx }),
|
|
3019
|
+
Textarea: (ctx) => /* @__PURE__ */ jsx(BrandTextarea, { ...ctx }),
|
|
3020
|
+
AppBar: (ctx) => /* @__PURE__ */ jsx(AppBar, { ...ctx }),
|
|
3021
|
+
Hero: (ctx) => /* @__PURE__ */ jsx(Hero, { ...ctx }),
|
|
3022
|
+
Workspace: (ctx) => /* @__PURE__ */ jsx(Workspace, { ...ctx }),
|
|
3023
|
+
Section: (ctx) => /* @__PURE__ */ jsx(Section, { ...ctx }),
|
|
3024
|
+
Rail: (ctx) => /* @__PURE__ */ jsx(Rail, { ...ctx }),
|
|
3025
|
+
SummaryRow: (ctx) => /* @__PURE__ */ jsx(SummaryRow, { ...ctx }),
|
|
3026
|
+
Cta: (ctx) => /* @__PURE__ */ jsx(Cta, { ...ctx }),
|
|
3027
|
+
Footer: (ctx) => /* @__PURE__ */ jsx(Footer, { ...ctx })
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
function GenerativePage({
|
|
3031
|
+
spec,
|
|
3032
|
+
store,
|
|
3033
|
+
scope,
|
|
3034
|
+
onRun,
|
|
3035
|
+
loading,
|
|
3036
|
+
registry = generativeRegistry,
|
|
3037
|
+
brand
|
|
3038
|
+
}) {
|
|
3039
|
+
const { inputs, result, env, idPrefix } = scope;
|
|
3040
|
+
const stableScope = React3.useMemo(
|
|
3041
|
+
() => ({ inputs, result, env, idPrefix }),
|
|
3042
|
+
[inputs, result, env, idPrefix]
|
|
3043
|
+
);
|
|
3044
|
+
const latest = React3.useRef({ onRun, store });
|
|
3045
|
+
React3.useEffect(() => {
|
|
3046
|
+
latest.current = { onRun, store };
|
|
3047
|
+
});
|
|
3048
|
+
const [handlers] = React3.useState(() => ({
|
|
3049
|
+
run: () => {
|
|
3050
|
+
latest.current.onRun?.(latest.current.store.getSnapshot());
|
|
3051
|
+
}
|
|
3052
|
+
}));
|
|
3053
|
+
const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(JSONUIProvider, { registry, store, handlers, children: /* @__PURE__ */ jsx(Renderer, { spec, registry, loading }) }) });
|
|
3054
|
+
return brand ? /* @__PURE__ */ jsx(BrandProvider, { manifest: brand, children: page }) : page;
|
|
3055
|
+
}
|
|
3056
|
+
function useStoreSnapshot(store) {
|
|
3057
|
+
return React3.useSyncExternalStore(
|
|
3058
|
+
store.subscribe,
|
|
3059
|
+
store.getSnapshot,
|
|
3060
|
+
store.getServerSnapshot ?? store.getSnapshot
|
|
3061
|
+
);
|
|
3062
|
+
}
|
|
3063
|
+
var httpUrl = z.url({ protocol: /^https?$/ });
|
|
3064
|
+
var brandManifestSchema = z.strictObject({
|
|
3065
|
+
name: z.string().min(1),
|
|
3066
|
+
website: httpUrl,
|
|
3067
|
+
logo: z.strictObject({
|
|
3068
|
+
/** The mark drawn on the light canvas: usually the dark version of the logo. */
|
|
3069
|
+
onLight: httpUrl,
|
|
3070
|
+
/** The mark drawn on the dark canvas: usually the light version of the logo. */
|
|
3071
|
+
onDark: httpUrl
|
|
3072
|
+
}),
|
|
3073
|
+
webfont: z.strictObject({
|
|
3074
|
+
provider: z.literal("google-fonts"),
|
|
3075
|
+
family: z.string().min(1)
|
|
3076
|
+
}).nullable()
|
|
3077
|
+
});
|
|
3078
|
+
|
|
3079
|
+
// src/generative/state.ts
|
|
3080
|
+
function isRecord(value) {
|
|
3081
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3082
|
+
}
|
|
3083
|
+
function payloadToState(field, payload) {
|
|
3084
|
+
if (payload === void 0 || payload === null) return payload;
|
|
3085
|
+
if (isNativeHtmlNode(field) || isNativeDateNode(field) || isNativeCompositeNode(field)) {
|
|
3086
|
+
return payload;
|
|
3087
|
+
}
|
|
3088
|
+
switch (field.kind) {
|
|
3089
|
+
case "date": {
|
|
3090
|
+
const content = readDateContent(payload);
|
|
3091
|
+
return content ? formatDateContent(content) : payload;
|
|
3092
|
+
}
|
|
3093
|
+
case "list": {
|
|
3094
|
+
const items = Array.isArray(payload) ? payload : isRecord(payload) && Array.isArray(payload.items) ? payload.items : void 0;
|
|
3095
|
+
if (!items) return payload;
|
|
3096
|
+
return items.map((item) => payloadToState(field.item, item));
|
|
3097
|
+
}
|
|
3098
|
+
case "object": {
|
|
3099
|
+
if (!isRecord(payload)) return payload;
|
|
3100
|
+
const out = { ...payload };
|
|
3101
|
+
for (const child of field.fields) {
|
|
3102
|
+
if (hasOwnProp(payload, child.name)) {
|
|
3103
|
+
out[child.name] = payloadToState(child, payload[child.name]);
|
|
3104
|
+
}
|
|
3105
|
+
}
|
|
3106
|
+
return out;
|
|
3107
|
+
}
|
|
3108
|
+
case "document":
|
|
3109
|
+
case "image":
|
|
3110
|
+
case "text":
|
|
3111
|
+
case "prose":
|
|
3112
|
+
case "number":
|
|
3113
|
+
case "boolean":
|
|
3114
|
+
case "enum":
|
|
3115
|
+
case "unknown":
|
|
3116
|
+
return payload;
|
|
3117
|
+
default:
|
|
3118
|
+
return field;
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
function seedInputs(fields) {
|
|
3122
|
+
const seed = {};
|
|
3123
|
+
for (const field of fields) {
|
|
3124
|
+
if (field.defaultValue !== void 0) {
|
|
3125
|
+
seed[field.name] = field.defaultValue;
|
|
3126
|
+
} else if (field.kind === "object") {
|
|
3127
|
+
const nested = seedInputs(field.fields);
|
|
3128
|
+
if (Object.keys(nested).length > 0) seed[field.name] = nested;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
return seed;
|
|
3132
|
+
}
|
|
3133
|
+
|
|
3134
|
+
// src/generative/fixture.ts
|
|
3135
|
+
function fixtureId(fixture) {
|
|
3136
|
+
return [
|
|
3137
|
+
fixture.producer,
|
|
3138
|
+
fixture.model,
|
|
3139
|
+
fixture.seed ? "seeded" : null,
|
|
3140
|
+
fixture.critic ? "critic" : null
|
|
3141
|
+
].filter((part) => part !== null).join("--");
|
|
3142
|
+
}
|
|
3143
|
+
var PRODUCER_LABEL = {
|
|
3144
|
+
"pipelex-method": "Pipelex method",
|
|
3145
|
+
"claude-code-subagent": "Claude Code subagent",
|
|
3146
|
+
"claude-code-session": "Claude Code session, by hand"
|
|
3147
|
+
};
|
|
3148
|
+
function fixtureLabel(fixture) {
|
|
3149
|
+
const parts = [PRODUCER_LABEL[fixture.producer], fixture.model];
|
|
3150
|
+
if (fixture.seed) parts.push("with a seed");
|
|
3151
|
+
if (fixture.critic) {
|
|
3152
|
+
parts.push(
|
|
3153
|
+
`${fixture.critic.model} critic \xD7 ${fixture.critic.rounds} round${fixture.critic.rounds === 1 ? "" : "s"}`
|
|
3154
|
+
);
|
|
3155
|
+
}
|
|
3156
|
+
return parts.join(" \xB7 ");
|
|
3157
|
+
}
|
|
3158
|
+
function findFixture(fixtures, pipeRef, id) {
|
|
3159
|
+
const found = fixtures.find(
|
|
3160
|
+
(fixture) => fixture.pipeRef === pipeRef && fixtureId(fixture) === id
|
|
3161
|
+
);
|
|
3162
|
+
if (!found) {
|
|
3163
|
+
const known = fixtures.filter((fixture) => fixture.pipeRef === pipeRef).map(fixtureId).join(", ");
|
|
3164
|
+
throw new Error(`No spec fixture ${id} for ${pipeRef}. Known: ${known || "none"}.`);
|
|
3165
|
+
}
|
|
3166
|
+
return found;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
export { BrandProvider, COMPONENT_NAMES, COMPONENT_RENDERINGS, CUSTOM_COMPONENTS, DESIGNER_CATALOG_CONCEPT, DescriptorProvider, GenerativePage, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, RESULT_ROOT, ResultSlotProvider, absoluteHatchPath, brandManifestSchema, catalog, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, generativeRegistry, generativeRenderers, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutFits, layoutProblems, pathFromDomId, payloadToState, renderInputBrief, renderResultBrief, repeatBasePathOf, resultFieldAtPath, seedInputs, segmentsUnder, specFromJsonl, specToJsonl, useBrand, useDescriptorScope, useResultSlot, useStoreSnapshot, validateAgainstCatalog };
|
|
3170
|
+
//# sourceMappingURL=index.js.map
|
|
3171
|
+
//# sourceMappingURL=index.js.map
|