@pipelex/mthds-form 0.32.0 → 0.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/brand/index.js +2 -2
  3. package/dist/{chunk-PCNFELNT.js → chunk-5RV3ASMO.js} +141 -69
  4. package/dist/chunk-5RV3ASMO.js.map +1 -0
  5. package/dist/{chunk-VH5CDQDR.js → chunk-AURDKAIC.js} +3 -3
  6. package/dist/{chunk-VH5CDQDR.js.map → chunk-AURDKAIC.js.map} +1 -1
  7. package/dist/{chunk-ZX52CQJ4.js → chunk-CFBK7NO7.js} +3 -3
  8. package/dist/{chunk-ZX52CQJ4.js.map → chunk-CFBK7NO7.js.map} +1 -1
  9. package/dist/{chunk-3GO65HWP.js → chunk-PNNWNRPG.js} +11 -7
  10. package/dist/chunk-PNNWNRPG.js.map +1 -0
  11. package/dist/{chunk-XO5OENNF.js → chunk-TF4RTKLH.js} +32 -5
  12. package/dist/chunk-TF4RTKLH.js.map +1 -0
  13. package/dist/{chunk-IBBAJXIO.js → chunk-XIWBKV7T.js} +3 -3
  14. package/dist/{chunk-IBBAJXIO.js.map → chunk-XIWBKV7T.js.map} +1 -1
  15. package/dist/{chunk-VBXFGUCG.js → chunk-XYS5KCXB.js} +465 -112
  16. package/dist/chunk-XYS5KCXB.js.map +1 -0
  17. package/dist/core/gate.js +2 -2
  18. package/dist/core/index.js +4 -4
  19. package/dist/core/readiness.d.ts +34 -1
  20. package/dist/core/readiness.js +1 -1
  21. package/dist/core/seed.js +2 -2
  22. package/dist/core/values.js +2 -2
  23. package/dist/{field-renderer-Dz03dpig.d.ts → field-renderer-BYN6YrU-.d.ts} +6 -6
  24. package/dist/generative/headless/index.d.ts +53 -16
  25. package/dist/generative/headless/index.js +3 -2
  26. package/dist/generative/index.d.ts +79 -25
  27. package/dist/generative/index.js +942 -275
  28. package/dist/generative/index.js.map +1 -1
  29. package/dist/react/index.d.ts +56 -29
  30. package/dist/react/index.js +4 -4
  31. package/dist/styles.css +1 -1
  32. package/package.json +1 -1
  33. package/dist/chunk-3GO65HWP.js.map +0 -1
  34. package/dist/chunk-PCNFELNT.js.map +0 -1
  35. package/dist/chunk-VBXFGUCG.js.map +0 -1
  36. package/dist/chunk-XO5OENNF.js.map +0 -1
@@ -1,33 +1,34 @@
1
1
  "use client";
2
- import { cn, headingClass, checkedAccentClass, focusOutlineClass, useResolveUrl, panelSurfaceClass, panelFlatShadowClass, linkTextClass, actionShadowClass, panelRaisedShadowClass, fieldControlClass, ResultRoot, ResultField, FieldRenderer, enumLabeler, useFieldPresentation, ToggleGroup, ToggleGroupItem, selectedFillStrongClass, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, selectedBorderClass, selectedFillClass, isThenable } from '../chunk-PCNFELNT.js';
3
- import { baseCatalog, catalog, frameComponents, resultFieldAtPath, segmentsUnder, RESULT_ROOT, inputFieldAtPath } from '../chunk-VBXFGUCG.js';
4
- export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, catalog, componentNamesFor, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, validateAgainstCatalog } from '../chunk-VBXFGUCG.js';
2
+ import { cn, headingClass, checkedAccentClass, focusOutlineClass, useResolveUrl, panelSurfaceClass, panelFlatShadowClass, linkTextClass, actionShadowClass, panelRaisedShadowClass, fieldControlClass, useFieldPresentation, useFieldStrings, fieldLabel, ResultRoot, ResultField, FieldRenderer, enumLabeler, ToggleGroup, ToggleGroupItem, selectedFillStrongClass, Switch, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, selectedBorderClass, selectedFillClass, isThenable } from '../chunk-5RV3ASMO.js';
3
+ import { baseCatalog, catalog, frameComponents, shareAmount, INPUTS_ROOT, joinPath, resultFieldAtPath, segmentsUnder, RESULT_ROOT, inputFieldAtPath, definitionChain, unwrapped } from '../chunk-XYS5KCXB.js';
4
+ export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, catalog, componentNamesFor, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheck, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, validateAgainstCatalog } from '../chunk-XYS5KCXB.js';
5
5
  import '../chunk-4VAO6BDE.js';
6
- export { seedInputs } from '../chunk-VH5CDQDR.js';
6
+ export { seedInputs } from '../chunk-AURDKAIC.js';
7
7
  import '../chunk-L23R3JNQ.js';
8
8
  import '../chunk-56WECFBD.js';
9
- import '../chunk-ZX52CQJ4.js';
9
+ import '../chunk-CFBK7NO7.js';
10
10
  import '../chunk-GHVVULHY.js';
11
11
  import { readImageContent, viewableUrl, readDateContent } from '../chunk-QQK6Y55E.js';
12
12
  export { brandManifestSchema } from '../chunk-QGFR57OH.js';
13
- import '../chunk-3GO65HWP.js';
13
+ import '../chunk-PNNWNRPG.js';
14
14
  import '../chunk-ZB4RYUKV.js';
15
15
  import '../chunk-FABY3DEY.js';
16
16
  import '../chunk-XCO6GTEC.js';
17
17
  import '../chunk-E6BIXJ6C.js';
18
- import '../chunk-XO5OENNF.js';
18
+ import { waitingFields } from '../chunk-TF4RTKLH.js';
19
19
  import '../chunk-OD3VYXUY.js';
20
20
  import '../chunk-WYH56SOZ.js';
21
- import { hasOwnProp } from '../chunk-V3OV2XQT.js';
21
+ import { hasOwnProp, ownProp } from '../chunk-V3OV2XQT.js';
22
22
  import '../chunk-GKV77RAS.js';
23
- import * as React5 from 'react';
24
- import { defineRegistry, useStateValue, useRepeatScope, useStateStore, useBoundProp, useFieldValidation, JSONUIProvider, Renderer } from '@json-render/react';
23
+ import * as React2 from 'react';
24
+ import { defineRegistry, useStateValue, useRepeatScope, useStateStore, useBoundProp, useFieldValidation, useOptionalValidation, JSONUIProvider, Renderer, useVisibility } from '@json-render/react';
25
25
  import { jsx, jsxs } from 'react/jsx-runtime';
26
+ import { builtInValidationFunctions, resolveRepeatStatePath, getByPath, evaluateVisibility, resolveElementProps, resolveRepeatItemStatePath } from '@json-render/core';
27
+ export { createStateStore } from '@json-render/core';
26
28
  import { Clock, ArrowRight, Send, Pencil, Zap, Lightbulb, Truck, Package, Briefcase, Bookmark, Flag, Shield, Info, AlertCircle, CheckCircle2, ListChecks, Layers, Tag, Phone, Mail, Paperclip, Upload, FileText, BarChart3, TrendingUp, Hash, Percent, Receipt, Coins, Banknote, Wallet, Gift, Ticket, Image, Camera, Music, Coffee, Utensils, Star, Heart, Accessibility, Baby, User, Users, Hourglass, Calendar, Moon, Sun, Leaf, Mountain, Globe, Landmark, Building2, Home, Hotel, Ship, Car, Train, Plane, Route, MapPin, Map as Map$1, Compass, Rocket, Sparkles, ChevronDown } from 'lucide-react';
27
29
  import * as TabsPrimitive from '@radix-ui/react-tabs';
28
- export { createStateStore } from '@json-render/core';
29
30
 
30
- var BrandContext = React5.createContext(null);
31
+ var BrandContext = React2.createContext(null);
31
32
  function BrandProvider({
32
33
  manifest,
33
34
  children
@@ -36,7 +37,7 @@ function BrandProvider({
36
37
  return /* @__PURE__ */ jsx(BrandContext.Provider, { value: manifest, children });
37
38
  }
38
39
  function useBrand() {
39
- const manifest = React5.useContext(BrandContext);
40
+ const manifest = React2.useContext(BrandContext);
40
41
  if (!manifest)
41
42
  throw new Error(
42
43
  "useBrand: no brand in scope. Pass `brand` to GenerativePage, or wrap the tree in BrandProvider."
@@ -47,7 +48,7 @@ var WEIGHTS = "400;500;600;700";
47
48
  function useWebfont(webfont) {
48
49
  const provider = webfont?.provider;
49
50
  const family = webfont?.family;
50
- React5.useEffect(() => {
51
+ React2.useEffect(() => {
51
52
  if (!provider || !family) return;
52
53
  const key = `${provider}:${family}`;
53
54
  if (document.head.querySelector(`link[data-brand-webfont="${CSS.escape(key)}"]`)) return;
@@ -67,40 +68,414 @@ function useWebfont(webfont) {
67
68
  document.head.appendChild(link);
68
69
  }, [provider, family]);
69
70
  }
70
- var ReportingContext = React5.createContext(false);
71
- var ElementBoundary = class extends React5.Component {
71
+ var CHECK_WHEN = "checkedFieldWhen";
72
+ var WHEN_ENABLED = "checkedFieldEnabled";
73
+ var WHEN_TYPE = "checkedFieldType";
74
+ var CHECKED_FIELD_FUNCTIONS = {
75
+ [CHECK_WHEN]: (value, args) => {
76
+ if (args?.[WHEN_ENABLED] !== true) return true;
77
+ const type = args[WHEN_TYPE];
78
+ if (typeof type !== "string" || !hasOwnProp(builtInValidationFunctions, type)) return true;
79
+ return builtInValidationFunctions[type](value, args);
80
+ }
81
+ };
82
+ function heldTo(check, enabled) {
83
+ return {
84
+ type: CHECK_WHEN,
85
+ message: check.message,
86
+ args: {
87
+ ...check.args,
88
+ [WHEN_ENABLED]: { $cond: enabled, $then: true, $else: false },
89
+ [WHEN_TYPE]: check.type
90
+ }
91
+ };
92
+ }
93
+ var CheckedFields = class {
94
+ constructor() {
95
+ // One entry per mounted field, so two fields handed one config object are
96
+ // two entries, and one leaving does not take the other's checks with it.
97
+ this.byPath = /* @__PURE__ */ new Map();
98
+ // Every path the record speaks for: those it carries now, and those it has
99
+ // just emptied, whose old checks json-render may still read until it has
100
+ // drawn once with the emptied ones. The verdict on these is the record's.
101
+ this.told = /* @__PURE__ */ new Set();
102
+ // The emptied paths, each with the validation json-render handed over when
103
+ // the record emptied it. A later one has drawn with the emptied checks, so
104
+ // the path goes back to json-render: a host's control that registers its
105
+ // checks there directly is then judged by them.
106
+ this.emptied = /* @__PURE__ */ new Map();
107
+ this.validation = null;
108
+ }
109
+ /** Hands the record json-render's validation, and tells it what is already mounted. */
110
+ attach(validation) {
111
+ if (validation === this.validation) return;
112
+ const first = this.validation === null;
113
+ this.validation = validation;
114
+ for (const [path, at] of this.emptied) {
115
+ if (at === validation) continue;
116
+ this.emptied.delete(path);
117
+ this.told.delete(path);
118
+ }
119
+ if (first) for (const path of this.byPath.keys()) this.publish(path);
120
+ }
121
+ /** Enters one mounted field; what it returns leaves it again. */
122
+ enter(path, config, field = {}) {
123
+ const entries = this.byPath.get(path) ?? /* @__PURE__ */ new Set();
124
+ this.byPath.set(path, entries);
125
+ const entry = {
126
+ config,
127
+ reveal: field.reveal ?? (() => {
128
+ }),
129
+ focus: field.focus ?? (() => {
130
+ }),
131
+ element: field.element ?? (() => null)
132
+ };
133
+ entries.add(entry);
134
+ this.publish(path);
135
+ return () => {
136
+ entries.delete(entry);
137
+ if (entries.size === 0) this.byPath.delete(path);
138
+ this.publish(path);
139
+ };
140
+ }
141
+ /**
142
+ * The checks of every field mounted at `path`, combined, or none when no
143
+ * field is. A field's `enabled` condition holds its own checks and no
144
+ * other field's: json-render reads `enabled` once for a whole config, and
145
+ * compares two configs without it, so each check of a conditional config
146
+ * carries its condition itself (`CHECK_WHEN`).
147
+ */
148
+ configFor(path) {
149
+ const entries = this.byPath.get(path);
150
+ if (!entries || entries.size === 0) return void 0;
151
+ const [first] = entries;
152
+ return {
153
+ checks: [...entries].flatMap(({ config }) => {
154
+ const checks = config.checks ?? [];
155
+ return config.enabled === void 0 ? checks : checks.map((check) => heldTo(check, config.enabled));
156
+ }),
157
+ ...first?.config.validateOn ? { validateOn: first.config.validateOn } : {}
158
+ };
159
+ }
160
+ /**
161
+ * Runs the checks of the page and answers whether they all pass; every
162
+ * check runs, so each failing field shows its message. When one fails, the
163
+ * person is taken to the first failing field in the page's order: the
164
+ * panels around it opened and the focus put on it, since a tab, a step or a
165
+ * section that is not open keeps its fields mounted and hidden.
166
+ *
167
+ * Each path is judged once, by one side. A path the record has carried is
168
+ * judged by the record's checks as it holds them now: json-render's own
169
+ * `validateAll` reads what it was told as of its provider's last render,
170
+ * which a press can beat by one task, so a field hidden or mounted the
171
+ * task before would be judged by checks it no longer carries, or not yet
172
+ * by those it does. A path the record does not carry, and has not just
173
+ * emptied, is a host's control that registered its checks with json-render
174
+ * directly (`useFieldValidation`), and json-render's verdict on it stands,
175
+ * a path the record once carried included.
176
+ */
177
+ check(validation) {
178
+ const before = validation.fieldStates;
179
+ validation.validateAll();
180
+ const after = validation.fieldStates;
181
+ let pass = true;
182
+ for (const [path, state] of Object.entries(after)) {
183
+ if (state === before[path] || this.told.has(path)) continue;
184
+ if (state.result?.valid === false) pass = false;
185
+ }
186
+ const failing = [];
187
+ for (const path of this.told) {
188
+ const combined = this.configFor(path);
189
+ if (combined) {
190
+ if (!validation.validate(path, combined).valid) {
191
+ pass = false;
192
+ failing.push(...this.byPath.get(path) ?? []);
193
+ }
194
+ } else if (after[path] !== before[path]) {
195
+ validation.clear(path);
196
+ }
197
+ }
198
+ const first = firstInPage(failing);
199
+ if (first) {
200
+ first.reveal();
201
+ first.focus();
202
+ }
203
+ return pass;
204
+ }
205
+ publish(path) {
206
+ if (!this.validation) return;
207
+ const config = this.configFor(path);
208
+ this.told.add(path);
209
+ if (config) this.emptied.delete(path);
210
+ else this.emptied.set(path, this.validation);
211
+ this.validation.registerField(path, config ?? { checks: [] });
212
+ }
213
+ };
214
+ var FOLLOWING = 4;
215
+ function firstInPage(entries) {
216
+ let first;
217
+ let firstElement = null;
218
+ for (const entry of entries) {
219
+ const element = entry.element();
220
+ if (!first) {
221
+ first = entry;
222
+ firstElement = element;
223
+ continue;
224
+ }
225
+ if (!element) continue;
226
+ if (!firstElement || element.compareDocumentPosition(firstElement) & FOLLOWING) {
227
+ first = entry;
228
+ firstElement = element;
229
+ }
230
+ }
231
+ return first;
232
+ }
233
+ var CheckedFieldsContext = React2.createContext(null);
234
+ var RevealContext = React2.createContext(null);
235
+ function RevealPanel({ open, children }) {
236
+ const outer = React2.useContext(RevealContext);
237
+ const latest = React2.useRef({ open, outer });
238
+ React2.useInsertionEffect(() => {
239
+ latest.current = { open, outer };
240
+ });
241
+ const reveal = React2.useCallback(() => {
242
+ latest.current.outer?.();
243
+ latest.current.open();
244
+ }, []);
245
+ return /* @__PURE__ */ jsx(RevealContext.Provider, { value: reveal, children });
246
+ }
247
+ function useCheckedField(path, config) {
248
+ const fields = React2.useContext(CheckedFieldsContext);
249
+ const field = useFieldValidation(path, fields ? void 0 : config);
250
+ const validation = useOptionalValidation();
251
+ const reveal = React2.useContext(RevealContext);
252
+ const element = React2.useRef(null);
253
+ const focusRef = React2.useCallback((node) => {
254
+ element.current = node;
255
+ }, []);
256
+ const [focusAsked, askFocus] = React2.useState(0);
257
+ React2.useEffect(() => {
258
+ if (focusAsked === 0) return;
259
+ const target = element.current;
260
+ if (!target) return;
261
+ target.focus({ preventScroll: true });
262
+ target.scrollIntoView?.({ block: "center" });
263
+ }, [focusAsked]);
264
+ const signature = config ? JSON.stringify(config) : "";
265
+ const latest = React2.useRef({ config, reveal });
266
+ React2.useInsertionEffect(() => {
267
+ latest.current = { config, reveal };
268
+ });
269
+ React2.useEffect(() => {
270
+ const entered = latest.current.config;
271
+ if (!fields || !path || !entered) return void 0;
272
+ return fields.enter(path, entered, {
273
+ reveal: () => latest.current.reveal?.(),
274
+ focus: () => askFocus((asked) => asked + 1),
275
+ element: () => element.current
276
+ });
277
+ }, [fields, path, signature]);
278
+ const fallback = field.validate;
279
+ const validate = React2.useCallback(() => {
280
+ const combined = fields?.configFor(path);
281
+ return validation && combined ? validation.validate(path, combined) : fallback();
282
+ }, [fields, path, validation, fallback]);
283
+ return { ...field, validate, focusRef };
284
+ }
285
+ var ReportingContext = React2.createContext(false);
286
+ function said(error) {
287
+ try {
288
+ return String(error);
289
+ } catch {
290
+ return "an error that cannot be written out";
291
+ }
292
+ }
293
+ function contentOf(value) {
294
+ const open = [];
295
+ let entries = 0;
296
+ try {
297
+ return JSON.stringify(value, function(_key, entry) {
298
+ if (++entries > MAX_CONTENT_ENTRIES) throw new RangeError("too large to compare");
299
+ while (open.length > 0 && open[open.length - 1] !== this) open.pop();
300
+ if (typeof entry === "bigint") return `${entry}n`;
301
+ if (typeof entry === "object" && entry !== null) {
302
+ if (open.includes(entry)) return "[cycle]";
303
+ open.push(entry);
304
+ }
305
+ return entry;
306
+ }) ?? "undefined";
307
+ } catch {
308
+ return "an input that cannot be written out";
309
+ }
310
+ }
311
+ var MAX_CONTENT_ENTRIES = 5e6;
312
+ function sameCue(a, b) {
313
+ if (Array.isArray(a) && Array.isArray(b)) {
314
+ return a.length === b.length && a.every((entry, index) => Object.is(entry, b[index]));
315
+ }
316
+ return Object.is(a, b);
317
+ }
318
+ var ElementBoundary = class extends React2.Component {
72
319
  constructor(props) {
73
320
  super(props);
74
- this.state = { failed: false, error: void 0, resetOn: props.resetOn };
321
+ this.state = { failed: false, error: void 0 };
75
322
  }
76
323
  static getDerivedStateFromError(error) {
77
324
  return { failed: true, error };
78
325
  }
79
- static getDerivedStateFromProps(props, state) {
80
- if (Object.is(props.resetOn, state.resetOn)) return null;
81
- return { failed: false, error: void 0, resetOn: props.resetOn };
326
+ /** What the element renders from, as text; total, since it reads a model's layout and the store. */
327
+ input() {
328
+ try {
329
+ return contentOf(this.props.inputOf ? this.props.inputOf() : this.props.resetOn);
330
+ } catch (error) {
331
+ return `the input could not be read: ${said(error)}`;
332
+ }
82
333
  }
83
334
  componentDidCatch(error, info) {
84
- if (!this.props.reporting) return;
335
+ if (this.props.loading) {
336
+ this.failedOn = void 0;
337
+ return;
338
+ }
339
+ this.failedOn = this.input();
85
340
  const componentStack = info.componentStack ?? void 0;
86
- this.props.report.current?.(error, {
87
- type: this.props.type,
88
- ...componentStack ? { componentStack } : {}
89
- });
341
+ if (this.props.reporting) {
342
+ this.props.report.current?.(error, {
343
+ type: this.props.type,
344
+ ...componentStack ? { componentStack } : {}
345
+ });
346
+ return;
347
+ }
348
+ if (!this.props.root) {
349
+ console.error(
350
+ `[json-render] Rendering error in <${this.props.type}>:`,
351
+ error,
352
+ componentStack
353
+ );
354
+ }
355
+ }
356
+ componentDidUpdate(previous, previousState) {
357
+ if (!this.state.failed || !previousState.failed) return;
358
+ const streamEnded = previous.loading === true && !this.props.loading;
359
+ if (streamEnded || this.inputChanged(previous)) {
360
+ this.failedOn = void 0;
361
+ this.setState({ failed: false, error: void 0 });
362
+ }
363
+ }
364
+ /** Whether what the element renders from is no longer what it failed on. */
365
+ inputChanged(previous) {
366
+ if (sameCue(this.props.resetOn, previous.resetOn)) return false;
367
+ if (this.props.loading || this.failedOn === void 0) return true;
368
+ return !Object.is(this.input(), this.failedOn);
90
369
  }
91
- // Whether to report or to throw the error on is decided as the boundary
92
- // renders its failure, from whether the host passes a handler in this very
93
- // render: decided later, it cost a blank commit and a second record of the
94
- // same error, and decided from the ref, it read the previous render's
95
- // callback. No host to tell: the error goes on, in the same render, to
96
- // json-render's boundary or the host's around the page, which record it
97
- // once, as if this one were not here.
370
+ // Whether the root throws the error on is decided as it renders its
371
+ // failure, from whether the host passes a handler in this very render:
372
+ // decided later, it cost a blank commit and a second record of the same
373
+ // error, and decided from the ref, it read the previous render's callback.
374
+ // No host to tell: the root's error goes on, in the same render, to the
375
+ // host's boundary around the page, which records it once.
98
376
  render() {
99
377
  if (!this.state.failed) return this.props.children;
100
- if (!this.props.reporting) throw this.state.error;
378
+ if (this.props.root && !this.props.reporting && !this.props.loading) throw this.state.error;
101
379
  return null;
102
380
  }
103
381
  };
382
+ function resolvedOf(element, ctx) {
383
+ if (!element) return void 0;
384
+ try {
385
+ return {
386
+ props: resolveElementProps(element.props ?? {}, ctx),
387
+ visible: element.visible === void 0 ? true : evaluateVisibility(element.visible, ctx)
388
+ };
389
+ } catch (error) {
390
+ return `throws: ${said(error)}`;
391
+ }
392
+ }
393
+ function childElementOf(node) {
394
+ if (!React2.isValidElement(node)) return void 0;
395
+ const { element, elementKey } = node.props;
396
+ if (typeof elementKey !== "string") return void 0;
397
+ return typeof element === "object" && element !== null ? element : void 0;
398
+ }
399
+ function repeatOf(node) {
400
+ if (!React2.isValidElement(node)) return void 0;
401
+ const { element, spec, elementKey, itemFilter } = node.props;
402
+ if (elementKey !== void 0 || element?.repeat === void 0) return void 0;
403
+ const elements = spec?.elements;
404
+ if (typeof elements !== "object" || elements === null || !Array.isArray(element.children)) {
405
+ return void 0;
406
+ }
407
+ return { keys: element.children, elements, itemFilter };
408
+ }
409
+ function inputOfElement(props, visibility, scope) {
410
+ const element = props.element;
411
+ const state = visibility.stateModel;
412
+ const ctx = {
413
+ ...visibility,
414
+ ...scope ? { repeatItem: scope.item, repeatIndex: scope.index, repeatBasePath: scope.basePath } : {}
415
+ };
416
+ const statePath = element?.repeat?.statePath;
417
+ let list;
418
+ let listPath;
419
+ if (statePath !== void 0) {
420
+ try {
421
+ listPath = resolveRepeatStatePath(statePath, scope?.basePath);
422
+ list = listPath === void 0 ? void 0 : getByPath(state, listPath);
423
+ } catch (error) {
424
+ list = `throws: ${said(error)}`;
425
+ }
426
+ }
427
+ const repeated = (repeat) => {
428
+ if (listPath === void 0 || !Array.isArray(list)) return null;
429
+ const path = listPath;
430
+ return list.map((item, index) => {
431
+ const itemCtx = {
432
+ ...visibility,
433
+ repeatItem: item,
434
+ repeatIndex: index,
435
+ repeatBasePath: resolveRepeatItemStatePath(path, index)
436
+ };
437
+ if (repeat.itemFilter !== void 0) {
438
+ try {
439
+ if (!evaluateVisibility(repeat.itemFilter, itemCtx)) return null;
440
+ } catch (error) {
441
+ return `throws: ${said(error)}`;
442
+ }
443
+ }
444
+ return repeat.keys.map((key) => {
445
+ const child = typeof key === "string" ? ownProp(repeat.elements, key) : void 0;
446
+ return child ? { element: child, resolved: resolvedOf(child, itemCtx) } : null;
447
+ });
448
+ });
449
+ };
450
+ const under = (nodes) => React2.Children.toArray(nodes).map((node) => {
451
+ const repeat = repeatOf(node);
452
+ if (repeat) return { repeated: repeated(repeat) };
453
+ const child = childElementOf(node);
454
+ return child ? { element: child, resolved: resolvedOf(child, ctx) } : null;
455
+ });
456
+ const slots = typeof props.slots === "object" && props.slots !== null ? Object.fromEntries(Object.entries(props.slots).map(([name, nodes]) => [name, under(nodes)])) : void 0;
457
+ return { props: element?.props, list, children: under(props.children), slots };
458
+ }
459
+ function inputOfRoot(spec, visibility) {
460
+ const root = spec?.elements?.[spec.root];
461
+ return { spec, root: resolvedOf(root, visibility) };
462
+ }
463
+ function RootBoundary(props) {
464
+ const { ctx } = useVisibility();
465
+ return /* @__PURE__ */ jsx(
466
+ ElementBoundary,
467
+ {
468
+ type: props.type,
469
+ report: props.report,
470
+ reporting: props.reporting,
471
+ loading: props.loading,
472
+ resetOn: [props.spec, ctx],
473
+ inputOf: () => inputOfRoot(props.spec, ctx),
474
+ root: true,
475
+ children: props.children
476
+ }
477
+ );
478
+ }
104
479
  function reportingRegistry(registry, report, made) {
105
480
  const wrapped = {};
106
481
  for (const [type, Renderer2] of Object.entries(registry)) {
@@ -110,13 +485,90 @@ function reportingRegistry(registry, report, made) {
110
485
  continue;
111
486
  }
112
487
  const Component2 = Renderer2;
113
- const Reported = (props) => /* @__PURE__ */ jsx(ElementBoundary, { type, report, reporting: React5.useContext(ReportingContext), children: /* @__PURE__ */ jsx(Component2, { ...props }) });
488
+ const Reported = (props) => {
489
+ const reporting = React2.useContext(ReportingContext);
490
+ const { ctx } = useVisibility();
491
+ const scope = useRepeatScope();
492
+ return /* @__PURE__ */ jsx(
493
+ ElementBoundary,
494
+ {
495
+ type,
496
+ report,
497
+ reporting,
498
+ loading: props.loading === true,
499
+ resetOn: [props.element, props.children, props.slots, ctx, scope],
500
+ inputOf: () => inputOfElement(props, ctx, scope),
501
+ children: /* @__PURE__ */ jsx(Component2, { ...props })
502
+ }
503
+ );
504
+ };
114
505
  Reported.displayName = `Reported(${type})`;
115
506
  made.set(type, { renderer: Renderer2, wrapper: Reported });
116
507
  wrapped[type] = Reported;
117
508
  }
118
509
  return wrapped;
119
510
  }
511
+ function placedInputs(spec) {
512
+ const placed = /* @__PURE__ */ new Map();
513
+ const elements = spec?.elements;
514
+ if (!isRecord(elements) || typeof spec?.root !== "string") return placed;
515
+ const seen = /* @__PURE__ */ new Set();
516
+ const pending = [spec.root];
517
+ while (pending.length > 0) {
518
+ const key = pending.pop();
519
+ if (seen.has(key)) continue;
520
+ seen.add(key);
521
+ const element = ownProp(elements, key);
522
+ if (!isRecord(element)) continue;
523
+ const props = isRecord(element.props) ? element.props : {};
524
+ place(placed, [...repeatedList(element.repeat), ...inputPathsOf(element.type, props)], props);
525
+ pending.push(...[...keysOf(element.children), ...slotKeys(element.slots)].reverse());
526
+ }
527
+ return placed;
528
+ }
529
+ function place(placed, paths, props) {
530
+ const label = typeof props.label === "string" ? props.label.trim() : "";
531
+ for (const path of paths) {
532
+ const known = placed.get(path);
533
+ if (!known) placed.set(path, { order: placed.size, ...label ? { label } : {} });
534
+ else if (known.label === void 0 && label) known.label = label;
535
+ }
536
+ }
537
+ function inputPathsOf(type, props) {
538
+ const paths = [];
539
+ if (type === "MthdsField" && typeof props.path === "string") paths.push(props.path);
540
+ for (const value of Object.values(props)) {
541
+ if (isRecord(value) && typeof value.$bindState === "string") paths.push(value.$bindState);
542
+ }
543
+ return paths.flatMap(inputPath);
544
+ }
545
+ function repeatedList(repeat) {
546
+ return isRecord(repeat) && typeof repeat.statePath === "string" ? inputPath(repeat.statePath) : [];
547
+ }
548
+ function inputPath(path) {
549
+ const segments = segmentsUnder(INPUTS_ROOT, path);
550
+ return segments && segments.length > 0 ? [joinPath(INPUTS_ROOT, ...segments)] : [];
551
+ }
552
+ function keysOf(value) {
553
+ return Array.isArray(value) ? value.filter((key) => typeof key === "string") : [];
554
+ }
555
+ function slotKeys(slots) {
556
+ return isRecord(slots) ? Object.values(slots).flatMap(keysOf) : [];
557
+ }
558
+ function isRecord(value) {
559
+ return typeof value === "object" && value !== null && !Array.isArray(value);
560
+ }
561
+ var PlacedInputsContext = React2.createContext(/* @__PURE__ */ new Map());
562
+ function PlacedInputsProvider({
563
+ spec,
564
+ children
565
+ }) {
566
+ const placed = React2.useMemo(() => placedInputs(spec), [spec]);
567
+ return /* @__PURE__ */ jsx(PlacedInputsContext.Provider, { value: placed, children });
568
+ }
569
+ function usePlacedInputs() {
570
+ return React2.useContext(PlacedInputsContext);
571
+ }
120
572
  var CONTAINER = "mx-auto w-full max-w-6xl px-6 sm:px-8";
121
573
  function BrandBar({ app, links, tag }) {
122
574
  const brand = useBrand();
@@ -147,7 +599,10 @@ function BrandBar({ app, links, tag }) {
147
599
  {
148
600
  href: "#",
149
601
  onClick: (event) => event.preventDefault(),
150
- className: "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-hidden",
602
+ className: cn(
603
+ "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground",
604
+ focusOutlineClass
605
+ ),
151
606
  children: link
152
607
  },
153
608
  `${index}-${link}`
@@ -169,39 +624,72 @@ function BrandBar({ app, links, tag }) {
169
624
  }
170
625
 
171
626
  // src/generative/declared-props.ts
172
- function definitionOf(schema) {
173
- return schema._zod?.def;
174
- }
175
- function unwrapped(schema) {
176
- let def = definitionOf(schema);
177
- for (let depth = 0; depth < 8 && def; depth += 1) {
178
- if (def.type !== "nullable" && def.type !== "optional" && def.type !== "default") return def;
179
- def = def.innerType ? definitionOf(def.innerType) : void 0;
180
- }
181
- return def;
182
- }
183
- function absentValueOf(schema) {
184
- if (schema.safeParse(null).success) return null;
185
- if (schema.safeParse(void 0).success) return void 0;
186
- const type = unwrapped(schema)?.type;
187
- if (type === "string") return "";
188
- if (type === "array") return [];
189
- return void 0;
627
+ var factsBySchema = /* @__PURE__ */ new WeakMap();
628
+ function factsOf(schema) {
629
+ const known = factsBySchema.get(schema);
630
+ if (known) return known;
631
+ const chain = definitionChain(schema);
632
+ const def = chain[chain.length - 1];
633
+ const acceptsNull = schema.safeParse(null).success;
634
+ const acceptsUndefined = schema.safeParse(void 0).success;
635
+ const absent = acceptsNull ? null : acceptsUndefined ? void 0 : def?.type === "string" ? "" : def?.type === "array" ? [] : void 0;
636
+ const facts = { def, plain: plainType(chain), acceptsNull, acceptsUndefined, absent };
637
+ factsBySchema.set(schema, facts);
638
+ return facts;
639
+ }
640
+ var PASSING_WRAPPERS = /* @__PURE__ */ new Set([
641
+ "optional",
642
+ "nullable",
643
+ "default",
644
+ "prefault",
645
+ "readonly",
646
+ "catch",
647
+ "nonoptional"
648
+ ]);
649
+ function plainType(chain) {
650
+ const def = chain[chain.length - 1];
651
+ if (!def || def.format !== void 0) return void 0;
652
+ if (chain.some((link) => link.checks !== void 0 && link.checks.length > 0)) return void 0;
653
+ if (!chain.slice(0, -1).every((link) => PASSING_WRAPPERS.has(link.type))) return void 0;
654
+ return def.type === "string" || def.type === "boolean" || def.type === "number" ? def.type : void 0;
655
+ }
656
+ function plainlyConforms(plain, value) {
657
+ if (plain === "number") return typeof value === "number" && Number.isFinite(value);
658
+ return plain !== void 0 && typeof value === plain;
659
+ }
660
+ function writtenOut(schema, value) {
661
+ if (typeof value !== "number" && typeof value !== "boolean") return void 0;
662
+ const text = String(value);
663
+ return schema.safeParse(text).success ? text : void 0;
664
+ }
665
+ function keptEntries(schema, element, entries, absent) {
666
+ const textItems = unwrapped(element)?.type === "string";
667
+ const kept = entries.flatMap((entry) => {
668
+ if (element.safeParse(entry).success) return [entry];
669
+ const text = textItems ? writtenOut(element, entry) : void 0;
670
+ return text === void 0 ? [] : [text];
671
+ });
672
+ return schema.safeParse(kept).success ? kept : absent;
190
673
  }
674
+ var conformedBySchema = /* @__PURE__ */ new WeakMap();
191
675
  function conformValue(schema, value) {
192
- if (value === void 0 || value === null) {
193
- return schema.safeParse(value).success ? value : absentValueOf(schema);
676
+ const facts = factsOf(schema);
677
+ if (value === void 0) return facts.acceptsUndefined ? value : facts.absent;
678
+ if (value === null) return facts.acceptsNull ? value : facts.absent;
679
+ if (typeof value !== "object") {
680
+ if (plainlyConforms(facts.plain, value) || schema.safeParse(value).success) return value;
681
+ if (facts.def?.type === "string") return writtenOut(schema, value) ?? facts.absent;
682
+ return facts.absent;
194
683
  }
195
- if (schema.safeParse(value).success) return value;
196
- const def = unwrapped(schema);
197
- if (def?.type === "string" && (typeof value === "number" || typeof value === "boolean")) {
198
- return String(value);
684
+ let byValue = conformedBySchema.get(schema);
685
+ if (!byValue) {
686
+ byValue = /* @__PURE__ */ new WeakMap();
687
+ conformedBySchema.set(schema, byValue);
199
688
  }
200
- if (def?.type === "array" && def.element && Array.isArray(value)) {
201
- const element = def.element;
202
- return value.filter((entry) => element.safeParse(entry).success);
203
- }
204
- return absentValueOf(schema);
689
+ if (byValue.has(value)) return byValue.get(value);
690
+ const held = schema.safeParse(value).success ? value : facts.def?.type === "array" && facts.def.element && Array.isArray(value) ? keptEntries(schema, facts.def.element, value, facts.absent) : facts.absent;
691
+ byValue.set(value, held);
692
+ return held;
205
693
  }
206
694
  function conformProps(shape, props, asWritten = []) {
207
695
  let conformed;
@@ -229,18 +717,13 @@ function withDeclaredProps(definitions, renderers, asWritten = {}) {
229
717
  }
230
718
  function textOf(value) {
231
719
  if (value === void 0 || value === null) return "";
232
- if (typeof value !== "object") return String(value);
233
- try {
234
- return String(value);
235
- } catch {
236
- try {
237
- return JSON.stringify(value) ?? "";
238
- } catch {
239
- return "";
240
- }
720
+ if (Array.isArray(value)) {
721
+ return value.map(textOf).filter((text) => text !== "").join(", ");
241
722
  }
723
+ if (typeof value === "object") return "";
724
+ return String(value);
242
725
  }
243
- var FrameHostContext = React5.createContext({});
726
+ var FrameHostContext = React2.createContext({});
244
727
  function FrameHostProvider({
245
728
  host,
246
729
  children
@@ -248,25 +731,115 @@ function FrameHostProvider({
248
731
  return /* @__PURE__ */ jsx(FrameHostContext.Provider, { value: host, children });
249
732
  }
250
733
  function useFrameHost() {
251
- return React5.useContext(FrameHostContext);
734
+ return React2.useContext(FrameHostContext);
252
735
  }
253
- function useBoundField(literal, binding, empty, { checks, validateOn }) {
736
+
737
+ // src/generative/format.ts
738
+ var LAYOUT_LOCALE = "en-GB";
739
+ var NUMBER_OPTIONS = {
740
+ // Grouped, with the places it has up to two, never padded.
741
+ grouped: { maximumFractionDigits: 2 },
742
+ integer: { maximumFractionDigits: 0 },
743
+ decimal: { minimumFractionDigits: 2, maximumFractionDigits: 2 },
744
+ compact: { notation: "compact", maximumFractionDigits: 1 },
745
+ // As written: every place, and no grouping, so a year or an id reads as itself.
746
+ plain: { maximumFractionDigits: 20, useGrouping: false }
747
+ };
748
+ var numbers = /* @__PURE__ */ new Map();
749
+ function formatNumber(value, style = "grouped") {
750
+ let format = numbers.get(style);
751
+ if (!format) {
752
+ format = new Intl.NumberFormat(LAYOUT_LOCALE, NUMBER_OPTIONS[style]);
753
+ numbers.set(style, format);
754
+ }
755
+ return format.format(value);
756
+ }
757
+ var currencies = /* @__PURE__ */ new Map();
758
+ var KNOWN_CURRENCIES = (() => {
759
+ try {
760
+ return typeof Intl.supportedValuesOf === "function" ? new Set(Intl.supportedValuesOf("currency")) : null;
761
+ } catch {
762
+ return null;
763
+ }
764
+ })();
765
+ function currencyFormat(code) {
766
+ if (!currencies.has(code)) {
767
+ if (KNOWN_CURRENCIES && !KNOWN_CURRENCIES.has(code)) {
768
+ currencies.set(code, null);
769
+ return null;
770
+ }
771
+ let money;
772
+ try {
773
+ money = {
774
+ whole: new Intl.NumberFormat(LAYOUT_LOCALE, {
775
+ style: "currency",
776
+ currency: code,
777
+ minimumFractionDigits: 0,
778
+ maximumFractionDigits: 0
779
+ }),
780
+ fractional: new Intl.NumberFormat(LAYOUT_LOCALE, { style: "currency", currency: code })
781
+ };
782
+ } catch {
783
+ money = null;
784
+ }
785
+ currencies.set(code, money);
786
+ }
787
+ return currencies.get(code);
788
+ }
789
+ function formatAmount(value, unit) {
790
+ if (unit && /^[A-Z]{3}$/.test(unit)) {
791
+ const money = currencyFormat(unit);
792
+ if (money) {
793
+ const fraction = money.fractional.formatToParts(value).find((p) => p.type === "fraction");
794
+ const whole = fraction === void 0 || /^0+$/.test(fraction.value);
795
+ return (whole ? money.whole : money.fractional).format(value);
796
+ }
797
+ }
798
+ const written = formatNumber(value);
799
+ return unit ? `${written} ${unit}` : written;
800
+ }
801
+ var DAY_OPTIONS = {
802
+ long: { weekday: "long", day: "numeric", month: "long", year: "numeric", timeZone: "UTC" },
803
+ short: { day: "numeric", month: "short", year: "numeric", timeZone: "UTC" },
804
+ weekday: { weekday: "short", day: "numeric", month: "long", timeZone: "UTC" }
805
+ };
806
+ var days = /* @__PURE__ */ new Map();
807
+ function formatDay(value, style = "long") {
808
+ const written = readDateContent(value)?.date;
809
+ const day = written === void 0 ? null : /^\d{4}-\d{2}-\d{2}/.exec(written);
810
+ if (!day) return void 0;
811
+ const date = /* @__PURE__ */ new Date(`${day[0]}T12:00:00Z`);
812
+ if (Number.isNaN(date.getTime()) || date.toISOString().slice(0, 10) !== day[0]) {
813
+ return void 0;
814
+ }
815
+ let format = days.get(style);
816
+ if (!format) {
817
+ format = new Intl.DateTimeFormat(LAYOUT_LOCALE, DAY_OPTIONS[style]);
818
+ days.set(style, format);
819
+ }
820
+ return format.format(date);
821
+ }
822
+ function useBoundField(literal, binding, empty, { checks, validateOn }, id) {
254
823
  const [boundValue, setBoundValue] = useBoundProp(literal ?? void 0, binding);
255
- const [localValue, setLocalValue] = React5.useState(literal ?? empty);
824
+ const [localValue, setLocalValue] = React2.useState(literal ?? empty);
256
825
  const isBound = Boolean(binding);
257
826
  const hasValidation = Boolean(binding && checks?.length);
258
827
  const when = validateOn ?? "change";
259
- const { errors, validate } = useFieldValidation(
828
+ const { errors, validate, focusRef } = useCheckedField(
260
829
  binding ?? "",
261
830
  hasValidation ? { checks: checks ?? [], validateOn: when } : void 0
262
831
  );
832
+ const error = errors[0];
263
833
  return {
264
834
  value: isBound ? boundValue ?? empty : localValue,
265
835
  setValue: (next) => {
266
836
  if (isBound) setBoundValue(next);
267
837
  else setLocalValue(next);
268
838
  },
269
- error: errors[0],
839
+ error,
840
+ errorId: `${id}-error`,
841
+ describedBy: error ? `${id}-error` : void 0,
842
+ focusRef,
270
843
  validateOnChange: () => {
271
844
  if (hasValidation && when === "change") validate();
272
845
  },
@@ -276,8 +849,8 @@ function useBoundField(literal, binding, empty, { checks, validateOn }) {
276
849
  };
277
850
  }
278
851
  var LABEL = "block text-sm leading-none font-medium";
279
- function FieldError({ message }) {
280
- return message ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: message }) : null;
852
+ function FieldError({ id, message }) {
853
+ return message ? /* @__PURE__ */ jsx("p", { id, className: "text-sm text-destructive", children: message }) : null;
281
854
  }
282
855
  function pickKey(table, key, fallback) {
283
856
  return key != null && hasOwnProp(table, key) ? key : fallback;
@@ -338,19 +911,21 @@ function Stack({
338
911
  children
339
912
  }) {
340
913
  const horizontal = props.direction === "horizontal";
341
- return /* @__PURE__ */ jsx(
914
+ const column = !horizontal && props.align === "center";
915
+ const stack = /* @__PURE__ */ jsx(
342
916
  "div",
343
917
  {
344
918
  className: cn(
345
919
  "flex",
346
920
  horizontal ? "flex-row" : "flex-col",
347
921
  pick(GAP, props.gap, "md"),
348
- pick(ALIGN, props.align, horizontal ? "start" : "stretch"),
922
+ column ? "w-fit max-w-full grow items-stretch" : pick(ALIGN, props.align, horizontal ? "start" : "stretch"),
349
923
  props.justify ? pick(JUSTIFY, props.justify, "start") : void 0
350
924
  ),
351
925
  children
352
926
  }
353
927
  );
928
+ return column ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center", children: stack }) : stack;
354
929
  }
355
930
  var COLUMNS = {
356
931
  1: "grid-cols-1",
@@ -384,9 +959,14 @@ function Collapsible({
384
959
  props,
385
960
  children
386
961
  }) {
962
+ const details = React2.useRef(null);
963
+ const open = React2.useCallback(() => {
964
+ if (details.current) details.current.open = true;
965
+ }, []);
387
966
  return /* @__PURE__ */ jsxs(
388
967
  "details",
389
968
  {
969
+ ref: details,
390
970
  className: "rounded-md border border-border",
391
971
  open: props.defaultOpen ?? false,
392
972
  "data-slot": "collapsible",
@@ -401,7 +981,7 @@ function Collapsible({
401
981
  }
402
982
  )
403
983
  ] }),
404
- /* @__PURE__ */ jsx("div", { className: "border-t border-border px-4 py-3", children })
984
+ /* @__PURE__ */ jsx("div", { className: "border-t border-border px-4 py-3", children: /* @__PURE__ */ jsx(RevealPanel, { open, children }) })
405
985
  ]
406
986
  }
407
987
  );
@@ -524,7 +1104,7 @@ function Progress({
524
1104
  ] });
525
1105
  }
526
1106
  function useControlId(name) {
527
- return `jr-${name}-${React5.useId()}`;
1107
+ return `jr-${name}-${React2.useId()}`;
528
1108
  }
529
1109
  function Input({
530
1110
  props,
@@ -532,7 +1112,7 @@ function Input({
532
1112
  emit
533
1113
  }) {
534
1114
  const id = useControlId(props.name);
535
- const field = useBoundField(props.value, bindings?.value, "", props);
1115
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
536
1116
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
537
1117
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
538
1118
  /* @__PURE__ */ jsx(
@@ -544,7 +1124,9 @@ function Input({
544
1124
  className: cn(fieldControlClass, "min-h-11 px-3 py-2"),
545
1125
  placeholder: props.placeholder ?? void 0,
546
1126
  value: field.value,
1127
+ ref: field.focusRef,
547
1128
  "aria-invalid": field.error ? true : void 0,
1129
+ "aria-describedby": field.describedBy,
548
1130
  onChange: (event) => {
549
1131
  field.setValue(event.target.value);
550
1132
  field.validateOnChange();
@@ -556,7 +1138,7 @@ function Input({
556
1138
  }
557
1139
  }
558
1140
  ),
559
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1141
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
560
1142
  ] });
561
1143
  }
562
1144
  function Textarea({
@@ -564,7 +1146,7 @@ function Textarea({
564
1146
  bindings
565
1147
  }) {
566
1148
  const id = useControlId(props.name);
567
- const field = useBoundField(props.value, bindings?.value, "", props);
1149
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
568
1150
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
569
1151
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
570
1152
  /* @__PURE__ */ jsx(
@@ -576,7 +1158,9 @@ function Textarea({
576
1158
  className: cn(fieldControlClass, "px-3 py-2"),
577
1159
  placeholder: props.placeholder ?? void 0,
578
1160
  value: field.value,
1161
+ ref: field.focusRef,
579
1162
  "aria-invalid": field.error ? true : void 0,
1163
+ "aria-describedby": field.describedBy,
580
1164
  onChange: (event) => {
581
1165
  field.setValue(event.target.value);
582
1166
  field.validateOnChange();
@@ -584,7 +1168,7 @@ function Textarea({
584
1168
  onBlur: field.validateOnBlur
585
1169
  }
586
1170
  ),
587
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1171
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
588
1172
  ] });
589
1173
  }
590
1174
  var NATIVE_CHECK = cn("size-4 shrink-0", checkedAccentClass, focusOutlineClass);
@@ -595,30 +1179,39 @@ function Radio({
595
1179
  emit
596
1180
  }) {
597
1181
  const id = useControlId(props.name);
598
- const field = useBoundField(props.value, bindings?.value, "", props);
1182
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
599
1183
  const labelOf = enumLabeler(props.options ?? [], useFieldPresentation());
600
- return /* @__PURE__ */ jsxs("fieldset", { className: "space-y-2", "aria-invalid": field.error ? true : void 0, children: [
601
- /* @__PURE__ */ jsx("legend", { className: LABEL, children: props.label }),
602
- /* @__PURE__ */ jsx("div", { className: "space-y-2 max-sm:space-y-0", children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsxs("label", { className: NATIVE_CHECK_ROW, children: [
603
- /* @__PURE__ */ jsx(
604
- "input",
605
- {
606
- type: "radio",
607
- name: id,
608
- value: option,
609
- checked: field.value === option,
610
- className: NATIVE_CHECK,
611
- onChange: () => {
612
- field.setValue(option);
613
- field.validateOnChange();
614
- emit("change");
615
- }
616
- }
617
- ),
618
- /* @__PURE__ */ jsx("span", { children: labelOf(option) })
619
- ] }, `${index}-${option}`)) }),
620
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
621
- ] });
1184
+ return /* @__PURE__ */ jsxs(
1185
+ "fieldset",
1186
+ {
1187
+ className: "space-y-2",
1188
+ "aria-invalid": field.error ? true : void 0,
1189
+ "aria-describedby": field.describedBy,
1190
+ children: [
1191
+ /* @__PURE__ */ jsx("legend", { className: LABEL, children: props.label }),
1192
+ /* @__PURE__ */ jsx("div", { className: "space-y-2 max-sm:space-y-0", children: (props.options ?? []).map((option, index, options) => /* @__PURE__ */ jsxs("label", { className: NATIVE_CHECK_ROW, children: [
1193
+ /* @__PURE__ */ jsx(
1194
+ "input",
1195
+ {
1196
+ type: "radio",
1197
+ name: id,
1198
+ value: option,
1199
+ checked: field.value === option,
1200
+ ref: field.value === option || index === 0 && !options.some((other) => other === field.value) ? field.focusRef : void 0,
1201
+ className: NATIVE_CHECK,
1202
+ onChange: () => {
1203
+ field.setValue(option);
1204
+ field.validateOnChange();
1205
+ emit("change");
1206
+ }
1207
+ }
1208
+ ),
1209
+ /* @__PURE__ */ jsx("span", { children: labelOf(option) })
1210
+ ] }, `${index}-${option}`)) }),
1211
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
1212
+ ]
1213
+ }
1214
+ );
622
1215
  }
623
1216
  function Checkbox({
624
1217
  props,
@@ -626,7 +1219,7 @@ function Checkbox({
626
1219
  emit
627
1220
  }) {
628
1221
  const id = useControlId(props.name);
629
- const field = useBoundField(props.checked, bindings?.checked, false, props);
1222
+ const field = useBoundField(props.checked, bindings?.checked, false, props, id);
630
1223
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
631
1224
  /* @__PURE__ */ jsxs("label", { htmlFor: id, className: NATIVE_CHECK_ROW, children: [
632
1225
  /* @__PURE__ */ jsx(
@@ -637,7 +1230,9 @@ function Checkbox({
637
1230
  type: "checkbox",
638
1231
  className: NATIVE_CHECK,
639
1232
  checked: field.value,
1233
+ ref: field.focusRef,
640
1234
  "aria-invalid": field.error ? true : void 0,
1235
+ "aria-describedby": field.describedBy,
641
1236
  onChange: (event) => {
642
1237
  field.setValue(event.target.checked);
643
1238
  field.validateOnChange();
@@ -647,7 +1242,7 @@ function Checkbox({
647
1242
  ),
648
1243
  /* @__PURE__ */ jsx("span", { children: props.label })
649
1244
  ] }),
650
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1245
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
651
1246
  ] });
652
1247
  }
653
1248
  function Switch2({
@@ -656,7 +1251,7 @@ function Switch2({
656
1251
  emit
657
1252
  }) {
658
1253
  const id = useControlId(props.name);
659
- const field = useBoundField(props.checked, bindings?.checked, false, props);
1254
+ const field = useBoundField(props.checked, bindings?.checked, false, props, id);
660
1255
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
661
1256
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 max-sm:py-2.5", children: [
662
1257
  /* @__PURE__ */ jsx(
@@ -665,7 +1260,9 @@ function Switch2({
665
1260
  id,
666
1261
  name: props.name,
667
1262
  checked: field.value,
1263
+ ref: field.focusRef,
668
1264
  "aria-invalid": field.error ? true : void 0,
1265
+ "aria-describedby": field.describedBy,
669
1266
  onCheckedChange: (next) => {
670
1267
  field.setValue(next);
671
1268
  field.validateOnChange();
@@ -675,7 +1272,7 @@ function Switch2({
675
1272
  ),
676
1273
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm", children: props.label })
677
1274
  ] }),
678
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1275
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
679
1276
  ] });
680
1277
  }
681
1278
  var BUTTON = {
@@ -694,7 +1291,7 @@ function Button({
694
1291
  disabled: props.disabled ?? false,
695
1292
  className: cn(
696
1293
  "inline-flex min-h-11 w-fit items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors",
697
- "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1294
+ focusOutlineClass,
698
1295
  "disabled:pointer-events-none disabled:opacity-50",
699
1296
  pick(BUTTON, props.variant, "primary")
700
1297
  ),
@@ -703,9 +1300,9 @@ function Button({
703
1300
  }
704
1301
  );
705
1302
  }
706
- var DescriptorContext = React5.createContext({});
1303
+ var DescriptorContext = React2.createContext({});
707
1304
  function useDescriptorScope() {
708
- return React5.useContext(DescriptorContext);
1305
+ return React2.useContext(DescriptorContext);
709
1306
  }
710
1307
  function DescriptorProvider({
711
1308
  scope,
@@ -721,8 +1318,9 @@ function Unresolved({ path, side }) {
721
1318
  className: "rounded-md border border-destructive/50 px-2.5 py-1.5 text-[12px] text-destructive",
722
1319
  children: [
723
1320
  side === "input" ? "No input" : "No result",
724
- " at ",
725
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: path })
1321
+ " at",
1322
+ " ",
1323
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: typeof path === "string" ? path : JSON.stringify(path) ?? String(path) })
726
1324
  ]
727
1325
  }
728
1326
  );
@@ -747,10 +1345,12 @@ function pathFromDomId(prefix, id) {
747
1345
  const path = own.split("-").map(decodeSegment).join("/");
748
1346
  return `${path}${children.map((child) => `/${child}`).join("")}`;
749
1347
  }
750
- function useAbsolutePath(path) {
1348
+ function useAbsolutePath(path, emptyIsItem = false) {
751
1349
  const repeat = useRepeatScope();
1350
+ if (typeof path !== "string") return void 0;
752
1351
  if (path.startsWith("/")) return path;
753
- return repeat ? `${repeat.basePath}/${path}` : void 0;
1352
+ if (!repeat) return void 0;
1353
+ return path === "" && emptyIsItem ? repeat.basePath : `${repeat.basePath}/${path}`;
754
1354
  }
755
1355
  function withoutOptionalMark(label) {
756
1356
  return label.replace(/\s*\(optional\)\s*$/i, "") || label;
@@ -758,10 +1358,10 @@ function withoutOptionalMark(label) {
758
1358
  function MthdsField({
759
1359
  props
760
1360
  }) {
761
- const scope = React5.useContext(DescriptorContext);
1361
+ const scope = React2.useContext(DescriptorContext);
762
1362
  const path = useAbsolutePath(props.path) ?? "";
763
1363
  const found = scope.inputs && path ? inputFieldAtPath(scope.inputs, path) : void 0;
764
- const field = React5.useMemo(
1364
+ const field = React2.useMemo(
765
1365
  () => found && (props.label || props.hint) ? {
766
1366
  ...found,
767
1367
  // The control marks an optional field itself, so a label that
@@ -773,7 +1373,7 @@ function MthdsField({
773
1373
  );
774
1374
  const value = useStateValue(path);
775
1375
  const { set } = useStateStore();
776
- const onChange = React5.useCallback((next) => set(path, next), [set, path]);
1376
+ const onChange = React2.useCallback((next) => set(path, next), [set, path]);
777
1377
  if (!field) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "input" });
778
1378
  return /* @__PURE__ */ jsx(
779
1379
  FieldRenderer,
@@ -787,30 +1387,32 @@ function MthdsField({
787
1387
  );
788
1388
  }
789
1389
  function MthdsResult({ props }) {
790
- const scope = React5.useContext(DescriptorContext);
1390
+ const scope = React2.useContext(DescriptorContext);
791
1391
  const path = useAbsolutePath(props.path) ?? "";
792
1392
  const field = scope.result && path ? resultFieldAtPath(scope.result, path) : void 0;
793
1393
  const value = useStateValue(path);
794
1394
  if (!field || !scope.result) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "result" });
795
- const place = [scope.result.name, ...segmentsUnder(RESULT_ROOT, path) ?? []];
796
- return /* @__PURE__ */ jsx(ResultRoot, { baseName: scope.result.name, path: place, children: /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: props.hideLabel ?? false }) });
1395
+ const place2 = [scope.result.name, ...segmentsUnder(RESULT_ROOT, path) ?? []];
1396
+ return /* @__PURE__ */ jsx(ResultRoot, { baseName: scope.result.name, path: place2, children: /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: props.hideLabel ?? false }) });
797
1397
  }
798
1398
  function AccessibleSelect({ props, bindings, emit }) {
799
1399
  const [boundValue, setBoundValue] = useBoundProp(
800
1400
  props.value ?? void 0,
801
1401
  bindings?.value
802
1402
  );
803
- const [localValue, setLocalValue] = React5.useState("");
1403
+ const [localValue, setLocalValue] = React2.useState("");
804
1404
  const isBound = Boolean(bindings?.value);
805
1405
  const value = isBound ? boundValue ?? "" : localValue;
806
1406
  const setValue = isBound ? setBoundValue : setLocalValue;
807
1407
  const validateOn = props.validateOn ?? "change";
808
1408
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
809
- const { errors, validate } = useFieldValidation(
1409
+ const { errors, validate, focusRef } = useCheckedField(
810
1410
  bindings?.value ?? "",
811
1411
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
812
1412
  );
813
1413
  const id = useControlId(props.name);
1414
+ const errorId = `${id}-error`;
1415
+ const error = errors[0];
814
1416
  const labelOf = enumLabeler(props.options ?? [], useFieldPresentation());
815
1417
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
816
1418
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm leading-none font-medium", children: props.label }),
@@ -824,12 +1426,22 @@ function AccessibleSelect({ props, bindings, emit }) {
824
1426
  emit("change");
825
1427
  },
826
1428
  children: [
827
- /* @__PURE__ */ jsx(SelectTrigger, { id, className: "h-11 w-full", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: props.placeholder ?? "Select..." }) }),
1429
+ /* @__PURE__ */ jsx(
1430
+ SelectTrigger,
1431
+ {
1432
+ id,
1433
+ ref: focusRef,
1434
+ "aria-invalid": error ? true : void 0,
1435
+ "aria-describedby": error ? errorId : void 0,
1436
+ className: "h-11 w-full",
1437
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: props.placeholder ?? "Select..." })
1438
+ }
1439
+ ),
828
1440
  /* @__PURE__ */ jsx(SelectContent, { children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: labelOf(option) }, `${index}-${option}`)) })
829
1441
  ]
830
1442
  }
831
1443
  ),
832
- errors.length > 0 ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errors[0] }) : null
1444
+ error ? /* @__PURE__ */ jsx("p", { id: errorId, className: "text-sm text-destructive", children: error }) : null
833
1445
  ] });
834
1446
  }
835
1447
  function valueAt(row, relativePath) {
@@ -840,27 +1452,20 @@ function valueAt(row, relativePath) {
840
1452
  }
841
1453
  return current;
842
1454
  }
843
- var CELL_NUMBER = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
844
1455
  function formatCell(value) {
845
1456
  if (value === null || value === void 0 || value === "") return "\u2014";
846
1457
  if (typeof value === "boolean") return value ? "yes" : "no";
847
- if (typeof value === "number") return CELL_NUMBER.format(value);
1458
+ if (typeof value === "number") return formatNumber(value);
848
1459
  if (typeof value === "string") return value;
849
1460
  if (Array.isArray(value)) return value.map(formatCell).join(", ");
850
1461
  return JSON.stringify(value);
851
1462
  }
852
1463
  function formatColumnCell(value, format) {
853
- const written = format === "date" ? readDateContent(value)?.date : void 0;
854
- const day = written === void 0 ? null : /^\d{4}-\d{2}-\d{2}/.exec(written);
855
- if (day) {
856
- const date = /* @__PURE__ */ new Date(`${day[0]}T12:00:00Z`);
857
- if (!Number.isNaN(date.getTime()))
858
- return date.toLocaleDateString("en-GB", {
859
- day: "numeric",
860
- month: "short",
861
- year: "numeric",
862
- timeZone: "UTC"
863
- });
1464
+ if (format === "date") return formatDay(value, "short") ?? formatCell(value);
1465
+ if (format === "text" && typeof value === "number") return formatNumber(value, "plain");
1466
+ if (format === "number") {
1467
+ const amount = shareAmount(value);
1468
+ if (Number.isFinite(amount)) return formatNumber(amount);
864
1469
  }
865
1470
  return formatCell(value);
866
1471
  }
@@ -881,23 +1486,9 @@ function DataTable({ props }) {
881
1486
  /* @__PURE__ */ jsx("tbody", { children: rows.map((row, index) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/60 last:border-b-0", children: columns.map((column) => /* @__PURE__ */ jsx("td", { className: "px-2.5 py-1.5 align-top text-foreground", children: formatColumnCell(valueAt(row, column.path), column.format) }, column.path)) }, index)) })
882
1487
  ] }) });
883
1488
  }
884
- function numberFormat(format) {
885
- switch (format) {
886
- case "integer":
887
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
888
- case "decimal":
889
- return new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
890
- case "compact":
891
- return new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
892
- case "plain":
893
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 20, useGrouping: false });
894
- default:
895
- return format;
896
- }
897
- }
898
1489
  function Metric({ props }) {
899
1490
  const value = props.value;
900
- const text = typeof value === "number" ? numberFormat(props.format ?? "plain").format(value) : value === null || value === void 0 || value === "" ? "\u2014" : textOf(value);
1491
+ const text = typeof value === "number" ? formatNumber(value, props.format ?? "plain") : textOf(value) || "\u2014";
901
1492
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
902
1493
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: props.label }),
903
1494
  /* @__PURE__ */ jsxs("span", { className: "text-2xl font-semibold tabular-nums text-foreground", children: [
@@ -930,30 +1521,53 @@ function Split({
930
1521
  }
931
1522
  function Tabs({ props, children }) {
932
1523
  const tabs = props.tabs ?? [];
933
- const panels = React5.Children.toArray(children);
934
- const [value, setValue] = React5.useState(tabs[0]?.value ?? "");
935
- return /* @__PURE__ */ jsxs(TabsPrimitive.Root, { value, onValueChange: setValue, className: "w-full", children: [
936
- /* @__PURE__ */ jsx(TabsPrimitive.List, { className: "inline-flex h-10 items-center justify-start rounded-md bg-muted p-1 text-muted-foreground max-sm:h-auto max-sm:flex-wrap", children: tabs.map((tab) => /* @__PURE__ */ jsx(
1524
+ const panels = React2.Children.toArray(children);
1525
+ const [chosen, setChosen] = React2.useState(null);
1526
+ const open = chosen ? openIndex(tabs, chosen) : 0;
1527
+ const value = String(open);
1528
+ const choose = (next) => {
1529
+ const index = Number(next);
1530
+ const tab = tabs[index];
1531
+ if (!tab) return;
1532
+ const occurrence = tabs.slice(0, index).filter((t) => t.value === tab.value).length;
1533
+ setChosen({ value: tab.value, occurrence });
1534
+ };
1535
+ return /* @__PURE__ */ jsxs(TabsPrimitive.Root, { value, onValueChange: choose, className: "w-full", children: [
1536
+ /* @__PURE__ */ jsx(TabsPrimitive.List, { className: "inline-flex h-10 items-center justify-start rounded-md bg-muted p-1 text-muted-foreground max-sm:h-auto max-sm:flex-wrap", children: tabs.map((tab, index) => /* @__PURE__ */ jsx(
937
1537
  TabsPrimitive.Trigger,
938
1538
  {
939
- value: tab.value,
1539
+ value: String(index),
940
1540
  className: "inline-flex items-center justify-center rounded-sm px-3 py-1.5 text-sm font-medium whitespace-nowrap transition-all focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-hidden data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm max-sm:min-h-11 max-sm:min-w-11",
941
1541
  children: tab.label
942
1542
  },
943
- tab.value
1543
+ index
944
1544
  )) }),
945
- tabs.map((tab, index) => /* @__PURE__ */ jsx(
1545
+ tabs.map((_tab, index) => /* @__PURE__ */ jsx(
946
1546
  TabsPrimitive.Content,
947
1547
  {
948
- value: tab.value,
1548
+ value: String(index),
1549
+ forceMount: true,
1550
+ hidden: index !== open,
949
1551
  className: "mt-4 focus-visible:outline-hidden",
950
- children: panels[index] ?? null
1552
+ children: /* @__PURE__ */ jsx(RevealPanel, { open: () => choose(String(index)), children: panels[index] ?? null })
951
1553
  },
952
- tab.value
1554
+ index
953
1555
  ))
954
1556
  ] });
955
1557
  }
956
- var BUTTON_BASE = "inline-flex min-h-11 items-center justify-center rounded-md px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50";
1558
+ function openIndex(tabs, { value, occurrence }) {
1559
+ let seen = 0;
1560
+ for (const [index, tab] of tabs.entries()) {
1561
+ if (tab.value !== value) continue;
1562
+ if (seen === occurrence) return index;
1563
+ seen += 1;
1564
+ }
1565
+ return 0;
1566
+ }
1567
+ var BUTTON_BASE = cn(
1568
+ "inline-flex min-h-11 items-center justify-center rounded-md px-4 text-sm font-medium transition-colors disabled:pointer-events-none disabled:opacity-50",
1569
+ focusOutlineClass
1570
+ );
957
1571
  var PRIMARY_BUTTON = cn(BUTTON_BASE, "bg-primary text-primary-foreground hover:bg-primary/90");
958
1572
  var SECONDARY_BUTTON = cn(
959
1573
  BUTTON_BASE,
@@ -961,9 +1575,10 @@ var SECONDARY_BUTTON = cn(
961
1575
  );
962
1576
  function Steps({ props, children }) {
963
1577
  const steps = props.steps ?? [];
964
- const panels = React5.Children.toArray(children);
965
- const [index, setIndex] = React5.useState(0);
1578
+ const panels = React2.Children.toArray(children);
1579
+ const [chosen, setIndex] = React2.useState(0);
966
1580
  const last = steps.length - 1;
1581
+ const index = Math.max(0, Math.min(chosen, last));
967
1582
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
968
1583
  /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-x-5 gap-y-2", children: steps.map((label, position) => /* @__PURE__ */ jsxs(
969
1584
  "li",
@@ -989,14 +1604,14 @@ function Steps({ props, children }) {
989
1604
  },
990
1605
  `${position}-${label}`
991
1606
  )) }),
992
- /* @__PURE__ */ jsx("div", { children: panels[index] ?? null }),
1607
+ steps.map((_step, position) => /* @__PURE__ */ jsx("div", { hidden: position !== index, children: /* @__PURE__ */ jsx(RevealPanel, { open: () => setIndex(position), children: panels[position] ?? null }) }, position)),
993
1608
  index > 0 || index < last ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
994
1609
  index > 0 ? /* @__PURE__ */ jsx(
995
1610
  "button",
996
1611
  {
997
1612
  type: "button",
998
1613
  "data-testid": "steps-back",
999
- onClick: () => setIndex((current) => Math.max(0, current - 1)),
1614
+ onClick: () => setIndex(Math.max(0, index - 1)),
1000
1615
  className: SECONDARY_BUTTON,
1001
1616
  children: props.backLabel ?? "Back"
1002
1617
  }
@@ -1006,7 +1621,7 @@ function Steps({ props, children }) {
1006
1621
  {
1007
1622
  type: "button",
1008
1623
  "data-testid": "steps-next",
1009
- onClick: () => setIndex((current) => Math.min(last, current + 1)),
1624
+ onClick: () => setIndex(Math.min(last, index + 1)),
1010
1625
  className: PRIMARY_BUTTON,
1011
1626
  children: props.nextLabel ?? "Next"
1012
1627
  }
@@ -1091,7 +1706,7 @@ function Segmented({ props, bindings, emit }) {
1091
1706
  props.value ?? void 0,
1092
1707
  bindings?.value
1093
1708
  );
1094
- const [localValue, setLocalValue] = React5.useState("");
1709
+ const [localValue, setLocalValue] = React2.useState("");
1095
1710
  const isBound = Boolean(bindings?.value);
1096
1711
  const value = isBound ? boundValue ?? "" : localValue;
1097
1712
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -1111,7 +1726,7 @@ function Segmented({ props, bindings, emit }) {
1111
1726
  emit("change");
1112
1727
  },
1113
1728
  className: "flex-wrap justify-start gap-1.5",
1114
- children: (props.options ?? []).map((option) => /* @__PURE__ */ jsx(
1729
+ children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsx(
1115
1730
  ToggleGroupItem,
1116
1731
  {
1117
1732
  value: option,
@@ -1120,7 +1735,7 @@ function Segmented({ props, bindings, emit }) {
1120
1735
  className: "rounded-full px-3.5 data-[state=on]:border-primary data-[state=on]:bg-primary data-[state=on]:text-primary-foreground max-sm:h-11 max-sm:min-w-11",
1121
1736
  children: labelOf(option)
1122
1737
  },
1123
- option
1738
+ `${index}-${option}`
1124
1739
  ))
1125
1740
  }
1126
1741
  )
@@ -1131,7 +1746,7 @@ function NumberInput({ props, bindings, emit }) {
1131
1746
  props.value ?? void 0,
1132
1747
  bindings?.value
1133
1748
  );
1134
- const [localValue, setLocalValue] = React5.useState(void 0);
1749
+ const [localValue, setLocalValue] = React2.useState(void 0);
1135
1750
  const isBound = Boolean(bindings?.value);
1136
1751
  const value = isBound ? boundValue : localValue;
1137
1752
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -1206,23 +1821,62 @@ var components = {
1206
1821
  };
1207
1822
  var generativeRenderers = withDeclaredProps(
1208
1823
  baseCatalog.data.components,
1209
- components
1824
+ components,
1825
+ // A hatch resolves its `path` as written, relative inside a repeat, so a
1826
+ // refused one reaches it as refused rather than written out as text.
1827
+ { MthdsField: ["path"], MthdsResult: ["path"] }
1210
1828
  );
1211
1829
  var RADIUS = "rounded-[calc(var(--radius,0.5rem)*1.5)]";
1212
- function useAbsolute(path) {
1213
- const repeat = useRepeatScope();
1214
- if (path.startsWith("/")) return path;
1215
- if (!repeat) return path;
1216
- return path === "" ? repeat.basePath : `${repeat.basePath}/${path}`;
1830
+ var NAMED_WAITING = 3;
1831
+ function useRunWaitsFor() {
1832
+ const { inputs } = useDescriptorScope();
1833
+ const values = useStateValue(INPUTS_ROOT);
1834
+ const placed = usePlacedInputs();
1835
+ const presentation = useFieldPresentation();
1836
+ const strings = useFieldStrings();
1837
+ const waiting = waitingFields(inputs ?? [], isRecord2(values) ? values : void 0).map(({ field, path }) => {
1838
+ const at = joinPath(INPUTS_ROOT, ...path);
1839
+ return { field, path, on: placed.get(at), rank: rankOf(placed, at) };
1840
+ }).sort((a, b) => a.rank - b.rank);
1841
+ if (waiting.length === 0) return void 0;
1842
+ const labelAt = (path) => {
1843
+ const field = inputFieldAtPath(inputs ?? [], joinPath(INPUTS_ROOT, ...path));
1844
+ if (!field) return void 0;
1845
+ return placed.get(joinPath(INPUTS_ROOT, ...path))?.label ?? fieldLabel(field.title, field.name, presentation);
1846
+ };
1847
+ const named = waiting.slice(0, NAMED_WAITING);
1848
+ const labels = named.map(
1849
+ ({ field, on }) => on?.label ?? fieldLabel(field.title, field.name, presentation)
1850
+ );
1851
+ const names = labels.map((label, index) => {
1852
+ const parent = named[index].path.slice(0, -1);
1853
+ const shared = labels.filter((other) => other === label).length > 1;
1854
+ const around = shared && parent.length > 0 ? labelAt(parent) : void 0;
1855
+ return around ? `${label} (${around})` : label;
1856
+ });
1857
+ return strings.runWaitsFor(names, waiting.length - names.length);
1858
+ }
1859
+ function rankOf(placed, path) {
1860
+ const own = placed.get(path);
1861
+ if (own) return own.order;
1862
+ for (let at = path.lastIndexOf("/"); at > INPUTS_ROOT.length; at = path.lastIndexOf("/", at - 1)) {
1863
+ const around = placed.get(path.slice(0, at));
1864
+ if (around) return around.order;
1865
+ }
1866
+ let first = Number.MAX_SAFE_INTEGER;
1867
+ for (const [inside, { order }] of placed) {
1868
+ if (inside.startsWith(`${path}/`)) first = Math.min(first, order);
1869
+ }
1870
+ return first;
1217
1871
  }
1218
- function asDate(value) {
1219
- if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}/.test(value)) return void 0;
1220
- const date = /* @__PURE__ */ new Date(`${value.slice(0, 10)}T12:00:00Z`);
1221
- return Number.isNaN(date.getTime()) ? void 0 : date;
1872
+ function isRecord2(value) {
1873
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1222
1874
  }
1223
1875
  function Composer({ props, children, emit }) {
1224
1876
  const host = useFrameHost();
1225
1877
  const busy = useDescriptorScope().env?.disabled ?? false;
1878
+ const waitsFor = useRunWaitsFor();
1879
+ const waitsForId = React2.useId();
1226
1880
  const two = props.columns === "2";
1227
1881
  return /* @__PURE__ */ jsxs(
1228
1882
  "div",
@@ -1259,16 +1913,20 @@ function Composer({ props, children, emit }) {
1259
1913
  }
1260
1914
  ),
1261
1915
  /* @__PURE__ */ jsxs("div", { className: "sticky bottom-0 z-[2] -mx-7 -mb-7 mt-7 flex items-center justify-between gap-4 rounded-b-[calc(var(--radius,0.5rem)*1.5)] border-t border-border [background-color:var(--panel-fill,var(--card,hsl(0_0%_100%)))] px-7 pt-[18px] pb-7 max-sm:-mx-5 max-sm:-mb-5 max-sm:flex-col-reverse max-sm:items-stretch max-sm:px-5 max-sm:pb-5", children: [
1262
- host.eta ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground max-sm:justify-center", children: [
1263
- /* @__PURE__ */ jsx(Clock, { className: "size-4", "aria-hidden": "true" }),
1264
- " ",
1265
- host.eta
1266
- ] }) : /* @__PURE__ */ jsx("span", {}),
1916
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-col gap-1 text-sm text-muted-foreground max-sm:items-center max-sm:text-center", children: [
1917
+ waitsFor ? /* @__PURE__ */ jsx("p", { id: waitsForId, children: waitsFor }) : null,
1918
+ host.eta ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
1919
+ /* @__PURE__ */ jsx(Clock, { className: "size-4", "aria-hidden": "true" }),
1920
+ " ",
1921
+ host.eta
1922
+ ] }) : null
1923
+ ] }),
1267
1924
  /* @__PURE__ */ jsxs(
1268
1925
  "button",
1269
1926
  {
1270
1927
  type: "button",
1271
1928
  disabled: busy,
1929
+ "aria-describedby": waitsFor ? waitsForId : void 0,
1272
1930
  onClick: () => emit("press"),
1273
1931
  className: cn(
1274
1932
  "inline-flex min-h-11 items-center justify-center gap-2 rounded-[var(--radius,0.5rem)] bg-primary px-[22px] text-[15px] font-semibold text-primary-foreground transition-all hover:bg-primary/90",
@@ -1340,32 +1998,24 @@ function segmentMix(index, count) {
1340
1998
  function segmentColor(index, count) {
1341
1999
  return `color-mix(in oklab, var(--link, var(--primary, hsl(240 5.9% 10%))) ${segmentMix(index, count)}%, var(--muted, hsl(240 4.8% 95.9%)))`;
1342
2000
  }
1343
- function amount(value, unit) {
1344
- if (unit && /^[A-Z]{3}$/.test(unit)) {
1345
- try {
1346
- return new Intl.NumberFormat("en-GB", {
1347
- style: "currency",
1348
- currency: unit,
1349
- maximumFractionDigits: 0
1350
- }).format(value);
1351
- } catch {
1352
- }
1353
- }
1354
- const n = new Intl.NumberFormat("en-GB", { maximumFractionDigits: 2 }).format(value);
1355
- return unit ? `${n} ${unit}` : n;
2001
+ function statedAmount(raw) {
2002
+ if (raw === null || raw === void 0) return null;
2003
+ const amount = shareAmount(raw);
2004
+ return Number.isFinite(amount) ? amount : void 0;
1356
2005
  }
1357
2006
  function ProportionBar({ props }) {
1358
2007
  const rows = (Array.isArray(props.items) ? props.items : []).filter(
1359
2008
  (row) => typeof row === "object" && row !== null && !Array.isArray(row)
1360
2009
  );
1361
- const amountOf = (raw) => typeof raw === "object" && raw !== null ? Number.NaN : Number(raw ?? 0);
1362
2010
  const parts = rows.map((row) => ({
1363
2011
  label: textOf(row[props.labelKey]),
1364
- value: amountOf(row[props.valueKey])
2012
+ value: shareAmount(row[props.valueKey])
1365
2013
  })).filter((p) => Number.isFinite(p.value) && p.value > 0);
1366
2014
  const sum = parts.reduce((s, p) => s + p.value, 0);
1367
- const total = typeof props.total === "number" ? props.total : sum;
1368
- const scale = Math.max(sum, total, typeof props.of === "number" ? props.of : 0) || 1;
2015
+ const stated = statedAmount(props.total);
2016
+ const total = stated === null ? sum : stated;
2017
+ const ceiling = statedAmount(props.of) ?? void 0;
2018
+ const scale = Math.max(sum, total ?? 0, ceiling ?? 0) || 1;
1369
2019
  const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
1370
2020
  return /* @__PURE__ */ jsxs(
1371
2021
  "div",
@@ -1380,10 +2030,10 @@ function ProportionBar({ props }) {
1380
2030
  children: [
1381
2031
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2.5", children: [
1382
2032
  /* @__PURE__ */ jsx("span", { className: "text-[15px] text-muted-foreground", children: props.label }),
1383
- /* @__PURE__ */ jsx("strong", { className: "text-2xl font-semibold tabular-nums text-foreground", children: amount(total, props.unit) }),
1384
- typeof props.of === "number" ? /* @__PURE__ */ jsxs("span", { className: "text-[15px] text-muted-foreground", children: [
2033
+ total !== void 0 ? /* @__PURE__ */ jsx("strong", { className: "text-2xl font-semibold tabular-nums text-foreground", children: formatAmount(total, props.unit) }) : null,
2034
+ ceiling !== void 0 ? /* @__PURE__ */ jsxs("span", { className: "text-[15px] text-muted-foreground", children: [
1385
2035
  "of ",
1386
- amount(props.of, props.unit)
2036
+ formatAmount(ceiling, props.unit)
1387
2037
  ] }) : null
1388
2038
  ] }),
1389
2039
  /* @__PURE__ */ jsx("div", { className: "flex h-2 gap-0.5 overflow-hidden rounded-full bg-muted", "aria-hidden": "true", children: parts.map((p, i) => /* @__PURE__ */ jsx(
@@ -1413,7 +2063,7 @@ function ProportionBar({ props }) {
1413
2063
  ),
1414
2064
  cap(p.label),
1415
2065
  " ",
1416
- amount(p.value, props.unit)
2066
+ formatAmount(p.value, props.unit)
1417
2067
  ]
1418
2068
  },
1419
2069
  `${i}-${p.label}`
@@ -1446,11 +2096,10 @@ var ASPECT = {
1446
2096
  function Picture({
1447
2097
  props
1448
2098
  }) {
1449
- const written = typeof props.path === "string" ? props.path : void 0;
1450
- const path = useAbsolute(written ?? "");
1451
- const value = useStateValue(path);
2099
+ const path = useAbsolutePath(props.path, true);
2100
+ const value = useStateValue(path ?? "");
1452
2101
  const resolve = useResolveUrl();
1453
- const content = written === void 0 ? void 0 : readImageContent(value);
2102
+ const content = path === void 0 ? void 0 : readImageContent(value);
1454
2103
  const src = content ? viewableUrl(resolve?.(content.url)) ?? viewableUrl(content.publicUrl) ?? viewableUrl(content.url) : void 0;
1455
2104
  const aspect = props.aspect && props.aspect !== "auto" ? ASPECT[props.aspect] : void 0;
1456
2105
  if (!src) return null;
@@ -1467,39 +2116,33 @@ function Picture({
1467
2116
  }
1468
2117
  );
1469
2118
  }
1470
- function present(value) {
1471
- return value !== void 0 && value !== null && value !== "";
1472
- }
1473
2119
  function Fact({
1474
2120
  props
1475
2121
  }) {
2122
+ const text = textOf(props.value);
1476
2123
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 rounded-[var(--radius,0.5rem)] border border-border bg-background px-4 py-3", children: [
1477
2124
  /* @__PURE__ */ jsx("span", { className: "text-[13px] text-muted-foreground", children: props.label }),
1478
- /* @__PURE__ */ jsx("span", { className: "text-[16px] font-medium text-foreground tabular-nums", children: present(props.value) ? textOf(props.value) : "\u2014" }),
2125
+ /* @__PURE__ */ jsx("span", { className: "text-[16px] font-medium text-foreground tabular-nums", children: text !== "" ? text : "\u2014" }),
1479
2126
  props.detail ? /* @__PURE__ */ jsx("span", { className: "text-[13px] text-muted-foreground", children: props.detail }) : null
1480
2127
  ] });
1481
2128
  }
1482
2129
  function Detail({ props }) {
1483
- if (!present(props.value)) return null;
2130
+ const text = textOf(props.value);
2131
+ if (text === "") return null;
1484
2132
  return (
1485
2133
  // A rule between the rows of a list of details, never under one set in a
1486
2134
  // row: there it read as an underline on every detail but the last.
1487
2135
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-[minmax(0,8.5rem)_minmax(0,1fr)] gap-3 border-b border-border py-2.5 text-[14px] last:border-b-0 [.flex-row>&]:border-b-0 [.flex-row>&]:grid-cols-[auto_minmax(0,1fr)]", children: [
1488
2136
  /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: props.label }),
1489
- /* @__PURE__ */ jsx("span", { className: "text-foreground", children: textOf(props.value) })
2137
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: text })
1490
2138
  ] })
1491
2139
  );
1492
2140
  }
1493
- var DATE_STYLES = {
1494
- long: { weekday: "long", day: "numeric", month: "long", year: "numeric", timeZone: "UTC" },
1495
- short: { day: "numeric", month: "short", year: "numeric", timeZone: "UTC" },
1496
- weekday: { weekday: "short", day: "numeric", month: "long", timeZone: "UTC" }
1497
- };
1498
2141
  function DateText({ props }) {
1499
- const date = asDate(props.value);
1500
- if (!date) return null;
2142
+ const day = formatDay(props.value, props.style ?? "long");
2143
+ if (day === void 0) return null;
1501
2144
  const tone = props.tone === "accent" ? cn("text-[13px] font-semibold", linkTextClass) : props.tone === "muted" ? "text-sm text-muted-foreground" : "text-[15px] text-foreground";
1502
- return /* @__PURE__ */ jsx("span", { className: cn("block", tone), children: date.toLocaleDateString("en-GB", DATE_STYLES[props.style ?? "long"]) });
2145
+ return /* @__PURE__ */ jsx("span", { className: cn("block", tone), children: day });
1503
2146
  }
1504
2147
  function TextList({ props }) {
1505
2148
  const items = Array.isArray(props.items) ? props.items.filter((x) => typeof x === "string" && x.trim()) : [];
@@ -1538,10 +2181,13 @@ var frameRenderers = withDeclaredProps(
1538
2181
  Detail: (ctx) => /* @__PURE__ */ jsx(Detail, { ...ctx })
1539
2182
  },
1540
2183
  // A refused `path` reaches the renderer as refused: its nearest value, the
1541
- // empty text, is the item itself inside a repeat.
1542
- { Picture: ["path"] }
2184
+ // empty text, is the item itself inside a repeat. A share's total and its
2185
+ // ceiling are read as written too: a decimal a payload carries as text is
2186
+ // a number the bar can draw, where its nearest value, nothing, would put
2187
+ // the parts' sum in its place.
2188
+ { Picture: ["path"], ProportionBar: ["total", "of"] }
1543
2189
  );
1544
- var ResultSlotContext = React5.createContext(null);
2190
+ var ResultSlotContext = React2.createContext(null);
1545
2191
  function ResultSlotProvider({
1546
2192
  slot,
1547
2193
  children
@@ -1549,7 +2195,7 @@ function ResultSlotProvider({
1549
2195
  return /* @__PURE__ */ jsx(ResultSlotContext.Provider, { value: slot, children });
1550
2196
  }
1551
2197
  function useResultSlot() {
1552
- return React5.useContext(ResultSlotContext);
2198
+ return React2.useContext(ResultSlotContext);
1553
2199
  }
1554
2200
  var LABEL2 = "block text-[13.5px] leading-none font-medium text-foreground";
1555
2201
  function AppBar({ props }) {
@@ -1578,7 +2224,7 @@ function Hero({ props }) {
1578
2224
  ] });
1579
2225
  }
1580
2226
  function Workspace({ props, children }) {
1581
- const [work, rail] = React5.Children.toArray(children);
2227
+ const [work, rail] = React2.Children.toArray(children);
1582
2228
  const railLeft = props.rail === "left";
1583
2229
  return /* @__PURE__ */ jsxs(
1584
2230
  "div",
@@ -1650,11 +2296,8 @@ function Rail({ props, children }) {
1650
2296
  )
1651
2297
  );
1652
2298
  }
1653
- function present2(value) {
1654
- return value !== void 0 && value !== null && value !== "";
1655
- }
1656
2299
  function SummaryRow({ props }) {
1657
- const text = [props.value, props.detail].filter(present2).map(textOf).join(props.separator ?? " ");
2300
+ const text = [props.value, props.detail].map(textOf).filter((half) => half !== "").join(props.separator ?? " ");
1658
2301
  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: [
1659
2302
  /* @__PURE__ */ jsx("span", { className: "shrink-0 text-[13px] text-muted-foreground", children: props.label }),
1660
2303
  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" })
@@ -1707,13 +2350,13 @@ function useTextField(props, bindings) {
1707
2350
  props.value ?? void 0,
1708
2351
  bindings?.value
1709
2352
  );
1710
- const [localValue, setLocalValue] = React5.useState("");
2353
+ const [localValue, setLocalValue] = React2.useState("");
1711
2354
  const isBound = Boolean(bindings?.value);
1712
2355
  const value = isBound ? boundValue ?? "" : localValue;
1713
2356
  const setValue = isBound ? setBoundValue : setLocalValue;
1714
2357
  const validateOn = props.validateOn ?? "blur";
1715
2358
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
1716
- const { errors, validate } = useFieldValidation(
2359
+ const { errors, validate, focusRef } = useCheckedField(
1717
2360
  bindings?.value ?? "",
1718
2361
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
1719
2362
  );
@@ -1724,7 +2367,7 @@ function useTextField(props, bindings) {
1724
2367
  const onBlur = () => {
1725
2368
  if (hasValidation && validateOn === "blur") validate();
1726
2369
  };
1727
- return { value, errors, onChange, onBlur };
2370
+ return { value, errors, onChange, onBlur, focusRef };
1728
2371
  }
1729
2372
  function BrandInput({
1730
2373
  props,
@@ -1743,7 +2386,9 @@ function BrandInput({
1743
2386
  type: props.type ?? "text",
1744
2387
  value: field.value,
1745
2388
  placeholder: props.placeholder ?? "",
2389
+ ref: field.focusRef,
1746
2390
  "aria-invalid": field.errors.length > 0 || void 0,
2391
+ "aria-describedby": field.errors[0] ? `${id}-error` : void 0,
1747
2392
  onChange: (event) => field.onChange(event.target.value),
1748
2393
  onBlur: () => {
1749
2394
  field.onBlur();
@@ -1756,7 +2401,7 @@ function BrandInput({
1756
2401
  className: cn(fieldControlClass, "h-11 px-3.5")
1757
2402
  }
1758
2403
  ),
1759
- field.errors[0] ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
2404
+ field.errors[0] ? /* @__PURE__ */ jsx("p", { id: `${id}-error`, role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
1760
2405
  ] });
1761
2406
  }
1762
2407
  function BrandTextarea({
@@ -1775,13 +2420,15 @@ function BrandTextarea({
1775
2420
  rows: props.rows ?? 3,
1776
2421
  value: field.value,
1777
2422
  placeholder: props.placeholder ?? "",
2423
+ ref: field.focusRef,
1778
2424
  "aria-invalid": field.errors.length > 0 || void 0,
2425
+ "aria-describedby": field.errors[0] ? `${id}-error` : void 0,
1779
2426
  onChange: (event) => field.onChange(event.target.value),
1780
2427
  onBlur: field.onBlur,
1781
2428
  className: cn(fieldControlClass, "px-3.5 py-2.5 leading-relaxed")
1782
2429
  }
1783
2430
  ),
1784
- field.errors[0] ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
2431
+ field.errors[0] ? /* @__PURE__ */ jsx("p", { id: `${id}-error`, role: "alert", className: "text-[11px] text-destructive", children: field.errors[0] }) : null
1785
2432
  ] });
1786
2433
  }
1787
2434
  var { registry: generativeRegistry } = defineRegistry(catalog, {
@@ -1815,28 +2462,29 @@ function GenerativePage({
1815
2462
  onElementError
1816
2463
  }) {
1817
2464
  const { inputs, result, env, idPrefix } = scope;
1818
- const stableScope = React5.useMemo(
2465
+ const stableScope = React2.useMemo(
1819
2466
  () => ({ inputs, result, env, idPrefix }),
1820
2467
  [inputs, result, env, idPrefix]
1821
2468
  );
1822
- const latest = React5.useRef({ onRun, store });
1823
- React5.useEffect(() => {
2469
+ const latest = React2.useRef({ onRun, store });
2470
+ React2.useEffect(() => {
1824
2471
  latest.current = { onRun, store };
1825
2472
  });
1826
- const report = React5.useRef(onElementError);
1827
- React5.useInsertionEffect(() => {
2473
+ const report = React2.useRef(onElementError);
2474
+ React2.useInsertionEffect(() => {
1828
2475
  report.current = onElementError;
1829
2476
  });
1830
- const [made] = React5.useState(() => /* @__PURE__ */ new Map());
1831
- const pageRegistry = React5.useMemo(
2477
+ const [made] = React2.useState(() => /* @__PURE__ */ new Map());
2478
+ const pageRegistry = React2.useMemo(
1832
2479
  () => reportingRegistry(registry, report, made),
1833
2480
  [registry, made]
1834
2481
  );
1835
2482
  const rootType = spec?.elements?.[spec.root]?.type ?? "root";
1836
- const specContent = React5.useMemo(() => contentOf(spec), [spec]);
1837
2483
  const reporting = onElementError !== void 0;
1838
- const running = React5.useRef(false);
1839
- const [handlers] = React5.useState(() => {
2484
+ const validation = React2.useRef(null);
2485
+ const [checkedFields] = React2.useState(() => new CheckedFields());
2486
+ const running = React2.useRef(false);
2487
+ const [handlers] = React2.useState(() => {
1840
2488
  const release = () => {
1841
2489
  setTimeout(() => {
1842
2490
  running.current = false;
@@ -1845,6 +2493,8 @@ function GenerativePage({
1845
2493
  return {
1846
2494
  run: () => {
1847
2495
  if (running.current) return void 0;
2496
+ const checks = validation.current;
2497
+ if (checks && !checkedFields.check(checks)) return void 0;
1848
2498
  running.current = true;
1849
2499
  let pending;
1850
2500
  try {
@@ -1863,33 +2513,50 @@ function GenerativePage({
1863
2513
  }
1864
2514
  };
1865
2515
  });
1866
- const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(JSONUIProvider, { registry: pageRegistry, store, handlers, children: /* @__PURE__ */ jsx(ReportingContext.Provider, { value: reporting, children: /* @__PURE__ */ jsx(
1867
- ElementBoundary,
2516
+ const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(PlacedInputsProvider, { spec, children: /* @__PURE__ */ jsx(
2517
+ JSONUIProvider,
1868
2518
  {
1869
- type: rootType,
1870
- report,
1871
- reporting,
1872
- resetOn: specContent,
1873
- children: /* @__PURE__ */ jsx(Renderer, { spec, registry: pageRegistry, loading })
2519
+ registry: pageRegistry,
2520
+ store,
2521
+ handlers,
2522
+ validationFunctions: CHECKED_FIELD_FUNCTIONS,
2523
+ children: /* @__PURE__ */ jsx(ReportingContext.Provider, { value: reporting, children: /* @__PURE__ */ jsxs(CheckedFieldsContext.Provider, { value: checkedFields, children: [
2524
+ /* @__PURE__ */ jsx(ValidationBridge, { into: validation, fields: checkedFields }),
2525
+ /* @__PURE__ */ jsx(
2526
+ RootBoundary,
2527
+ {
2528
+ type: rootType,
2529
+ report,
2530
+ reporting,
2531
+ loading: loading === true,
2532
+ spec,
2533
+ children: /* @__PURE__ */ jsx(Renderer, { spec, registry: pageRegistry, loading })
2534
+ }
2535
+ )
2536
+ ] }) })
1874
2537
  }
1875
- ) }) }) });
2538
+ ) }) });
1876
2539
  return brand ? /* @__PURE__ */ jsx(BrandProvider, { manifest: brand, children: page }) : page;
1877
2540
  }
1878
- function contentOf(spec) {
1879
- try {
1880
- return JSON.stringify(spec);
1881
- } catch {
1882
- return spec;
1883
- }
2541
+ function ValidationBridge({
2542
+ into,
2543
+ fields
2544
+ }) {
2545
+ const validation = useOptionalValidation();
2546
+ React2.useInsertionEffect(() => {
2547
+ into.current = validation;
2548
+ fields.attach(validation);
2549
+ });
2550
+ return null;
1884
2551
  }
1885
2552
  function useStoreSnapshot(store) {
1886
- return React5.useSyncExternalStore(
2553
+ return React2.useSyncExternalStore(
1887
2554
  store.subscribe,
1888
2555
  store.getSnapshot,
1889
2556
  store.getServerSnapshot ?? store.getSnapshot
1890
2557
  );
1891
2558
  }
1892
2559
 
1893
- export { BrandBar, BrandProvider, DescriptorProvider, FrameHostProvider, GenerativePage, ResultSlotProvider, domIdFor, generativeRegistry, generativeRenderers, pathFromDomId, useBrand, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot };
2560
+ export { BrandBar, BrandProvider, DescriptorProvider, FrameHostProvider, GenerativePage, ResultSlotProvider, RevealPanel, domIdFor, generativeRegistry, generativeRenderers, pathFromDomId, useBrand, useCheckedField, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot };
1894
2561
  //# sourceMappingURL=index.js.map
1895
2562
  //# sourceMappingURL=index.js.map