@pipelex/mthds-form 0.33.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 (34) hide show
  1. package/dist/brand/index.js +2 -2
  2. package/dist/{chunk-UKTYADY3.js → chunk-5RV3ASMO.js} +141 -69
  3. package/dist/chunk-5RV3ASMO.js.map +1 -0
  4. package/dist/{chunk-VH5CDQDR.js → chunk-AURDKAIC.js} +3 -3
  5. package/dist/{chunk-VH5CDQDR.js.map → chunk-AURDKAIC.js.map} +1 -1
  6. package/dist/{chunk-ZX52CQJ4.js → chunk-CFBK7NO7.js} +3 -3
  7. package/dist/{chunk-ZX52CQJ4.js.map → chunk-CFBK7NO7.js.map} +1 -1
  8. package/dist/{chunk-3GO65HWP.js → chunk-PNNWNRPG.js} +11 -7
  9. package/dist/chunk-PNNWNRPG.js.map +1 -0
  10. package/dist/{chunk-XO5OENNF.js → chunk-TF4RTKLH.js} +32 -5
  11. package/dist/chunk-TF4RTKLH.js.map +1 -0
  12. package/dist/{chunk-IBBAJXIO.js → chunk-XIWBKV7T.js} +3 -3
  13. package/dist/{chunk-IBBAJXIO.js.map → chunk-XIWBKV7T.js.map} +1 -1
  14. package/dist/{chunk-4SRSGZWM.js → chunk-XYS5KCXB.js} +158 -31
  15. package/dist/chunk-XYS5KCXB.js.map +1 -0
  16. package/dist/core/gate.js +2 -2
  17. package/dist/core/index.js +4 -4
  18. package/dist/core/readiness.d.ts +34 -1
  19. package/dist/core/readiness.js +1 -1
  20. package/dist/core/seed.js +2 -2
  21. package/dist/core/values.js +2 -2
  22. package/dist/generative/headless/index.d.ts +15 -9
  23. package/dist/generative/headless/index.js +3 -2
  24. package/dist/generative/index.d.ts +27 -4
  25. package/dist/generative/index.js +628 -240
  26. package/dist/generative/index.js.map +1 -1
  27. package/dist/react/index.d.ts +30 -3
  28. package/dist/react/index.js +4 -4
  29. package/dist/styles.css +1 -1
  30. package/package.json +1 -1
  31. package/dist/chunk-3GO65HWP.js.map +0 -1
  32. package/dist/chunk-4SRSGZWM.js.map +0 -1
  33. package/dist/chunk-UKTYADY3.js.map +0 -1
  34. package/dist/chunk-XO5OENNF.js.map +0 -1
@@ -1,34 +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-UKTYADY3.js';
3
- import { baseCatalog, catalog, frameComponents, shareAmount, resultFieldAtPath, segmentsUnder, RESULT_ROOT, inputFieldAtPath, definitionChain, unwrapped } from '../chunk-4SRSGZWM.js';
4
- export { COMPONENT_NAMES, COMPONENT_RENDERINGS, COMPOSER_COMPONENTS, CUSTOM_COMPONENTS, DESIGNER_BRIEF_CONCEPT, DESIGNER_CATALOG_CONCEPT, FRAMES, FRAME_COMPONENTS, ICON_NAMES, INPUTS_ROOT, METRIC_FORMATS, PICKED_SHADCN, PRODUCT_COMPONENTS, PROMPT_HASH, REPAIR_ATTEMPTS, RESULT_COMPONENTS, RESULT_ROOT, absoluteHatchPath, catalog, componentNamesFor, designerCatalog, fieldAtSegments, findFixture, fixtureId, fixtureLabel, formatProblems, frameCatalog, inputBrief, inputFieldAtPath, isDelegatedInput, isDelegatedResult, joinPath, jsonlLines, layoutCheck, layoutCheckProblems, layoutFits, layoutProblems, payloadToState, promptHashSubject, repeatBasePathOf, resultBrief, resultFieldAtPath, seedLine, segmentsUnder, specFromJsonl, specToJsonl, validateAgainstCatalog } from '../chunk-4SRSGZWM.js';
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 React6 from 'react';
23
+ import * as React2 from 'react';
24
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 { resolveRepeatStatePath, getByPath, evaluateVisibility, resolveElementProps } from '@json-render/core';
26
+ import { builtInValidationFunctions, resolveRepeatStatePath, getByPath, evaluateVisibility, resolveElementProps, resolveRepeatItemStatePath } from '@json-render/core';
27
27
  export { createStateStore } from '@json-render/core';
28
28
  import { Clock, ArrowRight, Send, Pencil, Zap, Lightbulb, Truck, Package, Briefcase, Bookmark, Flag, Shield, Info, AlertCircle, CheckCircle2, ListChecks, Layers, Tag, Phone, Mail, Paperclip, Upload, FileText, BarChart3, TrendingUp, Hash, Percent, Receipt, Coins, Banknote, Wallet, Gift, Ticket, Image, Camera, Music, Coffee, Utensils, Star, Heart, Accessibility, Baby, User, Users, Hourglass, Calendar, Moon, Sun, Leaf, Mountain, Globe, Landmark, Building2, Home, Hotel, Ship, Car, Train, Plane, Route, MapPin, Map as Map$1, Compass, Rocket, Sparkles, ChevronDown } from 'lucide-react';
29
29
  import * as TabsPrimitive from '@radix-ui/react-tabs';
30
30
 
31
- var BrandContext = React6.createContext(null);
31
+ var BrandContext = React2.createContext(null);
32
32
  function BrandProvider({
33
33
  manifest,
34
34
  children
@@ -37,7 +37,7 @@ function BrandProvider({
37
37
  return /* @__PURE__ */ jsx(BrandContext.Provider, { value: manifest, children });
38
38
  }
39
39
  function useBrand() {
40
- const manifest = React6.useContext(BrandContext);
40
+ const manifest = React2.useContext(BrandContext);
41
41
  if (!manifest)
42
42
  throw new Error(
43
43
  "useBrand: no brand in scope. Pass `brand` to GenerativePage, or wrap the tree in BrandProvider."
@@ -48,7 +48,7 @@ var WEIGHTS = "400;500;600;700";
48
48
  function useWebfont(webfont) {
49
49
  const provider = webfont?.provider;
50
50
  const family = webfont?.family;
51
- React6.useEffect(() => {
51
+ React2.useEffect(() => {
52
52
  if (!provider || !family) return;
53
53
  const key = `${provider}:${family}`;
54
54
  if (document.head.querySelector(`link[data-brand-webfont="${CSS.escape(key)}"]`)) return;
@@ -68,11 +68,42 @@ function useWebfont(webfont) {
68
68
  document.head.appendChild(link);
69
69
  }, [provider, family]);
70
70
  }
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
+ }
71
93
  var CheckedFields = class {
72
94
  constructor() {
73
95
  // One entry per mounted field, so two fields handed one config object are
74
96
  // two entries, and one leaving does not take the other's checks with it.
75
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();
76
107
  this.validation = null;
77
108
  }
78
109
  /** Hands the record json-render's validation, and tells it what is already mounted. */
@@ -80,73 +111,178 @@ var CheckedFields = class {
80
111
  if (validation === this.validation) return;
81
112
  const first = this.validation === null;
82
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
+ }
83
119
  if (first) for (const path of this.byPath.keys()) this.publish(path);
84
120
  }
85
- /** Enters one mounted field's checks; what it returns leaves them again. */
86
- enter(path, config) {
87
- const configs = this.byPath.get(path) ?? /* @__PURE__ */ new Set();
88
- this.byPath.set(path, configs);
89
- const entry = { config };
90
- configs.add(entry);
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);
91
134
  this.publish(path);
92
135
  return () => {
93
- configs.delete(entry);
94
- if (configs.size === 0) this.byPath.delete(path);
136
+ entries.delete(entry);
137
+ if (entries.size === 0) this.byPath.delete(path);
95
138
  this.publish(path);
96
139
  };
97
140
  }
98
- /** The checks of every field mounted at `path`, combined, or none when no field is. */
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
+ */
99
148
  configFor(path) {
100
- const configs = this.byPath.get(path);
101
- if (!configs || configs.size === 0) return void 0;
102
- const [first] = configs;
149
+ const entries = this.byPath.get(path);
150
+ if (!entries || entries.size === 0) return void 0;
151
+ const [first] = entries;
103
152
  return {
104
- checks: [...configs].flatMap(({ config }) => config.checks ?? []),
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
+ }),
105
157
  ...first?.config.validateOn ? { validateOn: first.config.validateOn } : {}
106
158
  };
107
159
  }
108
160
  /**
109
- * Runs the checks of every path a mounted field carries, as the record
110
- * holds them now, and answers whether they all pass; every path is checked,
111
- * so each failing field shows its message. json-render's own `validateAll`
112
- * reads what it was told as of its provider's last render, which a press
113
- * can beat by one task when a field has just mounted.
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.
114
176
  */
115
177
  check(validation) {
178
+ const before = validation.fieldStates;
179
+ validation.validateAll();
180
+ const after = validation.fieldStates;
116
181
  let pass = true;
117
- for (const path of this.byPath.keys()) {
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) {
118
188
  const combined = this.configFor(path);
119
- if (combined && !validation.validate(path, combined).valid) pass = false;
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();
120
202
  }
121
203
  return pass;
122
204
  }
123
205
  publish(path) {
124
- this.validation?.registerField(path, this.configFor(path) ?? { checks: [] });
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: [] });
125
212
  }
126
213
  };
127
- var CheckedFieldsContext = React6.createContext(null);
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
+ }
128
247
  function useCheckedField(path, config) {
129
- const fields = React6.useContext(CheckedFieldsContext);
248
+ const fields = React2.useContext(CheckedFieldsContext);
130
249
  const field = useFieldValidation(path, fields ? void 0 : config);
131
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]);
132
264
  const signature = config ? JSON.stringify(config) : "";
133
- const latest = React6.useRef(config);
134
- React6.useInsertionEffect(() => {
135
- latest.current = config;
265
+ const latest = React2.useRef({ config, reveal });
266
+ React2.useInsertionEffect(() => {
267
+ latest.current = { config, reveal };
136
268
  });
137
- React6.useEffect(() => {
138
- const entered = latest.current;
269
+ React2.useEffect(() => {
270
+ const entered = latest.current.config;
139
271
  if (!fields || !path || !entered) return void 0;
140
- return fields.enter(path, entered);
272
+ return fields.enter(path, entered, {
273
+ reveal: () => latest.current.reveal?.(),
274
+ focus: () => askFocus((asked) => asked + 1),
275
+ element: () => element.current
276
+ });
141
277
  }, [fields, path, signature]);
142
278
  const fallback = field.validate;
143
- const validate = React6.useCallback(() => {
279
+ const validate = React2.useCallback(() => {
144
280
  const combined = fields?.configFor(path);
145
281
  return validation && combined ? validation.validate(path, combined) : fallback();
146
282
  }, [fields, path, validation, fallback]);
147
- return { ...field, validate };
283
+ return { ...field, validate, focusRef };
148
284
  }
149
- var ReportingContext = React6.createContext(false);
285
+ var ReportingContext = React2.createContext(false);
150
286
  function said(error) {
151
287
  try {
152
288
  return String(error);
@@ -155,13 +291,16 @@ function said(error) {
155
291
  }
156
292
  }
157
293
  function contentOf(value) {
158
- const open = /* @__PURE__ */ new WeakSet();
294
+ const open = [];
295
+ let entries = 0;
159
296
  try {
160
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();
161
300
  if (typeof entry === "bigint") return `${entry}n`;
162
301
  if (typeof entry === "object" && entry !== null) {
163
- if (open.has(entry)) return "[cycle]";
164
- open.add(entry);
302
+ if (open.includes(entry)) return "[cycle]";
303
+ open.push(entry);
165
304
  }
166
305
  return entry;
167
306
  }) ?? "undefined";
@@ -169,13 +308,14 @@ function contentOf(value) {
169
308
  return "an input that cannot be written out";
170
309
  }
171
310
  }
311
+ var MAX_CONTENT_ENTRIES = 5e6;
172
312
  function sameCue(a, b) {
173
313
  if (Array.isArray(a) && Array.isArray(b)) {
174
314
  return a.length === b.length && a.every((entry, index) => Object.is(entry, b[index]));
175
315
  }
176
316
  return Object.is(a, b);
177
317
  }
178
- var ElementBoundary = class extends React6.Component {
318
+ var ElementBoundary = class extends React2.Component {
179
319
  constructor(props) {
180
320
  super(props);
181
321
  this.state = { failed: false, error: void 0 };
@@ -251,11 +391,21 @@ function resolvedOf(element, ctx) {
251
391
  }
252
392
  }
253
393
  function childElementOf(node) {
254
- if (!React6.isValidElement(node)) return void 0;
394
+ if (!React2.isValidElement(node)) return void 0;
255
395
  const { element, elementKey } = node.props;
256
396
  if (typeof elementKey !== "string") return void 0;
257
397
  return typeof element === "object" && element !== null ? element : void 0;
258
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
+ }
259
409
  function inputOfElement(props, visibility, scope) {
260
410
  const element = props.element;
261
411
  const state = visibility.stateModel;
@@ -265,15 +415,41 @@ function inputOfElement(props, visibility, scope) {
265
415
  };
266
416
  const statePath = element?.repeat?.statePath;
267
417
  let list;
418
+ let listPath;
268
419
  if (statePath !== void 0) {
269
420
  try {
270
- const path = resolveRepeatStatePath(statePath, scope?.basePath);
271
- list = path === void 0 ? void 0 : getByPath(state, path);
421
+ listPath = resolveRepeatStatePath(statePath, scope?.basePath);
422
+ list = listPath === void 0 ? void 0 : getByPath(state, listPath);
272
423
  } catch (error) {
273
424
  list = `throws: ${said(error)}`;
274
425
  }
275
426
  }
276
- const under = (nodes) => React6.Children.toArray(nodes).map((node) => {
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) };
277
453
  const child = childElementOf(node);
278
454
  return child ? { element: child, resolved: resolvedOf(child, ctx) } : null;
279
455
  });
@@ -310,7 +486,7 @@ function reportingRegistry(registry, report, made) {
310
486
  }
311
487
  const Component2 = Renderer2;
312
488
  const Reported = (props) => {
313
- const reporting = React6.useContext(ReportingContext);
489
+ const reporting = React2.useContext(ReportingContext);
314
490
  const { ctx } = useVisibility();
315
491
  const scope = useRepeatScope();
316
492
  return /* @__PURE__ */ jsx(
@@ -332,6 +508,67 @@ function reportingRegistry(registry, report, made) {
332
508
  }
333
509
  return wrapped;
334
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
+ }
335
572
  var CONTAINER = "mx-auto w-full max-w-6xl px-6 sm:px-8";
336
573
  function BrandBar({ app, links, tag }) {
337
574
  const brand = useBrand();
@@ -362,7 +599,10 @@ function BrandBar({ app, links, tag }) {
362
599
  {
363
600
  href: "#",
364
601
  onClick: (event) => event.preventDefault(),
365
- className: "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background focus-visible:outline-hidden",
602
+ className: cn(
603
+ "rounded-sm text-sm text-muted-foreground transition-colors hover:text-foreground",
604
+ focusOutlineClass
605
+ ),
366
606
  children: link
367
607
  },
368
608
  `${index}-${link}`
@@ -483,7 +723,7 @@ function textOf(value) {
483
723
  if (typeof value === "object") return "";
484
724
  return String(value);
485
725
  }
486
- var FrameHostContext = React6.createContext({});
726
+ var FrameHostContext = React2.createContext({});
487
727
  function FrameHostProvider({
488
728
  host,
489
729
  children
@@ -491,25 +731,115 @@ function FrameHostProvider({
491
731
  return /* @__PURE__ */ jsx(FrameHostContext.Provider, { value: host, children });
492
732
  }
493
733
  function useFrameHost() {
494
- return React6.useContext(FrameHostContext);
734
+ return React2.useContext(FrameHostContext);
735
+ }
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);
495
756
  }
496
- function useBoundField(literal, binding, empty, { checks, validateOn }) {
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) {
497
823
  const [boundValue, setBoundValue] = useBoundProp(literal ?? void 0, binding);
498
- const [localValue, setLocalValue] = React6.useState(literal ?? empty);
824
+ const [localValue, setLocalValue] = React2.useState(literal ?? empty);
499
825
  const isBound = Boolean(binding);
500
826
  const hasValidation = Boolean(binding && checks?.length);
501
827
  const when = validateOn ?? "change";
502
- const { errors, validate } = useCheckedField(
828
+ const { errors, validate, focusRef } = useCheckedField(
503
829
  binding ?? "",
504
830
  hasValidation ? { checks: checks ?? [], validateOn: when } : void 0
505
831
  );
832
+ const error = errors[0];
506
833
  return {
507
834
  value: isBound ? boundValue ?? empty : localValue,
508
835
  setValue: (next) => {
509
836
  if (isBound) setBoundValue(next);
510
837
  else setLocalValue(next);
511
838
  },
512
- error: errors[0],
839
+ error,
840
+ errorId: `${id}-error`,
841
+ describedBy: error ? `${id}-error` : void 0,
842
+ focusRef,
513
843
  validateOnChange: () => {
514
844
  if (hasValidation && when === "change") validate();
515
845
  },
@@ -519,8 +849,8 @@ function useBoundField(literal, binding, empty, { checks, validateOn }) {
519
849
  };
520
850
  }
521
851
  var LABEL = "block text-sm leading-none font-medium";
522
- function FieldError({ message }) {
523
- 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;
524
854
  }
525
855
  function pickKey(table, key, fallback) {
526
856
  return key != null && hasOwnProp(table, key) ? key : fallback;
@@ -581,19 +911,21 @@ function Stack({
581
911
  children
582
912
  }) {
583
913
  const horizontal = props.direction === "horizontal";
584
- return /* @__PURE__ */ jsx(
914
+ const column = !horizontal && props.align === "center";
915
+ const stack = /* @__PURE__ */ jsx(
585
916
  "div",
586
917
  {
587
918
  className: cn(
588
919
  "flex",
589
920
  horizontal ? "flex-row" : "flex-col",
590
921
  pick(GAP, props.gap, "md"),
591
- pick(ALIGN, props.align, horizontal ? "start" : "stretch"),
922
+ column ? "w-fit max-w-full grow items-stretch" : pick(ALIGN, props.align, horizontal ? "start" : "stretch"),
592
923
  props.justify ? pick(JUSTIFY, props.justify, "start") : void 0
593
924
  ),
594
925
  children
595
926
  }
596
927
  );
928
+ return column ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center", children: stack }) : stack;
597
929
  }
598
930
  var COLUMNS = {
599
931
  1: "grid-cols-1",
@@ -627,9 +959,14 @@ function Collapsible({
627
959
  props,
628
960
  children
629
961
  }) {
962
+ const details = React2.useRef(null);
963
+ const open = React2.useCallback(() => {
964
+ if (details.current) details.current.open = true;
965
+ }, []);
630
966
  return /* @__PURE__ */ jsxs(
631
967
  "details",
632
968
  {
969
+ ref: details,
633
970
  className: "rounded-md border border-border",
634
971
  open: props.defaultOpen ?? false,
635
972
  "data-slot": "collapsible",
@@ -644,7 +981,7 @@ function Collapsible({
644
981
  }
645
982
  )
646
983
  ] }),
647
- /* @__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 }) })
648
985
  ]
649
986
  }
650
987
  );
@@ -767,7 +1104,7 @@ function Progress({
767
1104
  ] });
768
1105
  }
769
1106
  function useControlId(name) {
770
- return `jr-${name}-${React6.useId()}`;
1107
+ return `jr-${name}-${React2.useId()}`;
771
1108
  }
772
1109
  function Input({
773
1110
  props,
@@ -775,7 +1112,7 @@ function Input({
775
1112
  emit
776
1113
  }) {
777
1114
  const id = useControlId(props.name);
778
- const field = useBoundField(props.value, bindings?.value, "", props);
1115
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
779
1116
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
780
1117
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
781
1118
  /* @__PURE__ */ jsx(
@@ -787,7 +1124,9 @@ function Input({
787
1124
  className: cn(fieldControlClass, "min-h-11 px-3 py-2"),
788
1125
  placeholder: props.placeholder ?? void 0,
789
1126
  value: field.value,
1127
+ ref: field.focusRef,
790
1128
  "aria-invalid": field.error ? true : void 0,
1129
+ "aria-describedby": field.describedBy,
791
1130
  onChange: (event) => {
792
1131
  field.setValue(event.target.value);
793
1132
  field.validateOnChange();
@@ -799,7 +1138,7 @@ function Input({
799
1138
  }
800
1139
  }
801
1140
  ),
802
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1141
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
803
1142
  ] });
804
1143
  }
805
1144
  function Textarea({
@@ -807,7 +1146,7 @@ function Textarea({
807
1146
  bindings
808
1147
  }) {
809
1148
  const id = useControlId(props.name);
810
- const field = useBoundField(props.value, bindings?.value, "", props);
1149
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
811
1150
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
812
1151
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: LABEL, children: props.label }),
813
1152
  /* @__PURE__ */ jsx(
@@ -819,7 +1158,9 @@ function Textarea({
819
1158
  className: cn(fieldControlClass, "px-3 py-2"),
820
1159
  placeholder: props.placeholder ?? void 0,
821
1160
  value: field.value,
1161
+ ref: field.focusRef,
822
1162
  "aria-invalid": field.error ? true : void 0,
1163
+ "aria-describedby": field.describedBy,
823
1164
  onChange: (event) => {
824
1165
  field.setValue(event.target.value);
825
1166
  field.validateOnChange();
@@ -827,7 +1168,7 @@ function Textarea({
827
1168
  onBlur: field.validateOnBlur
828
1169
  }
829
1170
  ),
830
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1171
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
831
1172
  ] });
832
1173
  }
833
1174
  var NATIVE_CHECK = cn("size-4 shrink-0", checkedAccentClass, focusOutlineClass);
@@ -838,30 +1179,39 @@ function Radio({
838
1179
  emit
839
1180
  }) {
840
1181
  const id = useControlId(props.name);
841
- const field = useBoundField(props.value, bindings?.value, "", props);
1182
+ const field = useBoundField(props.value, bindings?.value, "", props, id);
842
1183
  const labelOf = enumLabeler(props.options ?? [], useFieldPresentation());
843
- return /* @__PURE__ */ jsxs("fieldset", { className: "space-y-2", "aria-invalid": field.error ? true : void 0, children: [
844
- /* @__PURE__ */ jsx("legend", { className: LABEL, children: props.label }),
845
- /* @__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: [
846
- /* @__PURE__ */ jsx(
847
- "input",
848
- {
849
- type: "radio",
850
- name: id,
851
- value: option,
852
- checked: field.value === option,
853
- className: NATIVE_CHECK,
854
- onChange: () => {
855
- field.setValue(option);
856
- field.validateOnChange();
857
- emit("change");
858
- }
859
- }
860
- ),
861
- /* @__PURE__ */ jsx("span", { children: labelOf(option) })
862
- ] }, `${index}-${option}`)) }),
863
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
864
- ] });
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
+ );
865
1215
  }
866
1216
  function Checkbox({
867
1217
  props,
@@ -869,7 +1219,7 @@ function Checkbox({
869
1219
  emit
870
1220
  }) {
871
1221
  const id = useControlId(props.name);
872
- const field = useBoundField(props.checked, bindings?.checked, false, props);
1222
+ const field = useBoundField(props.checked, bindings?.checked, false, props, id);
873
1223
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
874
1224
  /* @__PURE__ */ jsxs("label", { htmlFor: id, className: NATIVE_CHECK_ROW, children: [
875
1225
  /* @__PURE__ */ jsx(
@@ -880,7 +1230,9 @@ function Checkbox({
880
1230
  type: "checkbox",
881
1231
  className: NATIVE_CHECK,
882
1232
  checked: field.value,
1233
+ ref: field.focusRef,
883
1234
  "aria-invalid": field.error ? true : void 0,
1235
+ "aria-describedby": field.describedBy,
884
1236
  onChange: (event) => {
885
1237
  field.setValue(event.target.checked);
886
1238
  field.validateOnChange();
@@ -890,7 +1242,7 @@ function Checkbox({
890
1242
  ),
891
1243
  /* @__PURE__ */ jsx("span", { children: props.label })
892
1244
  ] }),
893
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1245
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
894
1246
  ] });
895
1247
  }
896
1248
  function Switch2({
@@ -899,7 +1251,7 @@ function Switch2({
899
1251
  emit
900
1252
  }) {
901
1253
  const id = useControlId(props.name);
902
- const field = useBoundField(props.checked, bindings?.checked, false, props);
1254
+ const field = useBoundField(props.checked, bindings?.checked, false, props, id);
903
1255
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
904
1256
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 max-sm:py-2.5", children: [
905
1257
  /* @__PURE__ */ jsx(
@@ -908,7 +1260,9 @@ function Switch2({
908
1260
  id,
909
1261
  name: props.name,
910
1262
  checked: field.value,
1263
+ ref: field.focusRef,
911
1264
  "aria-invalid": field.error ? true : void 0,
1265
+ "aria-describedby": field.describedBy,
912
1266
  onCheckedChange: (next) => {
913
1267
  field.setValue(next);
914
1268
  field.validateOnChange();
@@ -918,7 +1272,7 @@ function Switch2({
918
1272
  ),
919
1273
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: "text-sm", children: props.label })
920
1274
  ] }),
921
- /* @__PURE__ */ jsx(FieldError, { message: field.error })
1275
+ /* @__PURE__ */ jsx(FieldError, { id: field.errorId, message: field.error })
922
1276
  ] });
923
1277
  }
924
1278
  var BUTTON = {
@@ -937,7 +1291,7 @@ function Button({
937
1291
  disabled: props.disabled ?? false,
938
1292
  className: cn(
939
1293
  "inline-flex min-h-11 w-fit items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-colors",
940
- "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
1294
+ focusOutlineClass,
941
1295
  "disabled:pointer-events-none disabled:opacity-50",
942
1296
  pick(BUTTON, props.variant, "primary")
943
1297
  ),
@@ -946,9 +1300,9 @@ function Button({
946
1300
  }
947
1301
  );
948
1302
  }
949
- var DescriptorContext = React6.createContext({});
1303
+ var DescriptorContext = React2.createContext({});
950
1304
  function useDescriptorScope() {
951
- return React6.useContext(DescriptorContext);
1305
+ return React2.useContext(DescriptorContext);
952
1306
  }
953
1307
  function DescriptorProvider({
954
1308
  scope,
@@ -991,11 +1345,12 @@ function pathFromDomId(prefix, id) {
991
1345
  const path = own.split("-").map(decodeSegment).join("/");
992
1346
  return `${path}${children.map((child) => `/${child}`).join("")}`;
993
1347
  }
994
- function useAbsolutePath(path) {
1348
+ function useAbsolutePath(path, emptyIsItem = false) {
995
1349
  const repeat = useRepeatScope();
996
1350
  if (typeof path !== "string") return void 0;
997
1351
  if (path.startsWith("/")) return path;
998
- return repeat ? `${repeat.basePath}/${path}` : void 0;
1352
+ if (!repeat) return void 0;
1353
+ return path === "" && emptyIsItem ? repeat.basePath : `${repeat.basePath}/${path}`;
999
1354
  }
1000
1355
  function withoutOptionalMark(label) {
1001
1356
  return label.replace(/\s*\(optional\)\s*$/i, "") || label;
@@ -1003,10 +1358,10 @@ function withoutOptionalMark(label) {
1003
1358
  function MthdsField({
1004
1359
  props
1005
1360
  }) {
1006
- const scope = React6.useContext(DescriptorContext);
1361
+ const scope = React2.useContext(DescriptorContext);
1007
1362
  const path = useAbsolutePath(props.path) ?? "";
1008
1363
  const found = scope.inputs && path ? inputFieldAtPath(scope.inputs, path) : void 0;
1009
- const field = React6.useMemo(
1364
+ const field = React2.useMemo(
1010
1365
  () => found && (props.label || props.hint) ? {
1011
1366
  ...found,
1012
1367
  // The control marks an optional field itself, so a label that
@@ -1018,7 +1373,7 @@ function MthdsField({
1018
1373
  );
1019
1374
  const value = useStateValue(path);
1020
1375
  const { set } = useStateStore();
1021
- const onChange = React6.useCallback((next) => set(path, next), [set, path]);
1376
+ const onChange = React2.useCallback((next) => set(path, next), [set, path]);
1022
1377
  if (!field) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "input" });
1023
1378
  return /* @__PURE__ */ jsx(
1024
1379
  FieldRenderer,
@@ -1032,30 +1387,32 @@ function MthdsField({
1032
1387
  );
1033
1388
  }
1034
1389
  function MthdsResult({ props }) {
1035
- const scope = React6.useContext(DescriptorContext);
1390
+ const scope = React2.useContext(DescriptorContext);
1036
1391
  const path = useAbsolutePath(props.path) ?? "";
1037
1392
  const field = scope.result && path ? resultFieldAtPath(scope.result, path) : void 0;
1038
1393
  const value = useStateValue(path);
1039
1394
  if (!field || !scope.result) return /* @__PURE__ */ jsx(Unresolved, { path: props.path, side: "result" });
1040
- const place = [scope.result.name, ...segmentsUnder(RESULT_ROOT, path) ?? []];
1041
- 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 }) });
1042
1397
  }
1043
1398
  function AccessibleSelect({ props, bindings, emit }) {
1044
1399
  const [boundValue, setBoundValue] = useBoundProp(
1045
1400
  props.value ?? void 0,
1046
1401
  bindings?.value
1047
1402
  );
1048
- const [localValue, setLocalValue] = React6.useState("");
1403
+ const [localValue, setLocalValue] = React2.useState("");
1049
1404
  const isBound = Boolean(bindings?.value);
1050
1405
  const value = isBound ? boundValue ?? "" : localValue;
1051
1406
  const setValue = isBound ? setBoundValue : setLocalValue;
1052
1407
  const validateOn = props.validateOn ?? "change";
1053
1408
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
1054
- const { errors, validate } = useCheckedField(
1409
+ const { errors, validate, focusRef } = useCheckedField(
1055
1410
  bindings?.value ?? "",
1056
1411
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
1057
1412
  );
1058
1413
  const id = useControlId(props.name);
1414
+ const errorId = `${id}-error`;
1415
+ const error = errors[0];
1059
1416
  const labelOf = enumLabeler(props.options ?? [], useFieldPresentation());
1060
1417
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1061
1418
  /* @__PURE__ */ jsx("label", { htmlFor: id, className: "block text-sm leading-none font-medium", children: props.label }),
@@ -1069,12 +1426,22 @@ function AccessibleSelect({ props, bindings, emit }) {
1069
1426
  emit("change");
1070
1427
  },
1071
1428
  children: [
1072
- /* @__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
+ ),
1073
1440
  /* @__PURE__ */ jsx(SelectContent, { children: (props.options ?? []).map((option, index) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: labelOf(option) }, `${index}-${option}`)) })
1074
1441
  ]
1075
1442
  }
1076
1443
  ),
1077
- 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
1078
1445
  ] });
1079
1446
  }
1080
1447
  function valueAt(row, relativePath) {
@@ -1085,27 +1452,20 @@ function valueAt(row, relativePath) {
1085
1452
  }
1086
1453
  return current;
1087
1454
  }
1088
- var CELL_NUMBER = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
1089
1455
  function formatCell(value) {
1090
1456
  if (value === null || value === void 0 || value === "") return "\u2014";
1091
1457
  if (typeof value === "boolean") return value ? "yes" : "no";
1092
- if (typeof value === "number") return CELL_NUMBER.format(value);
1458
+ if (typeof value === "number") return formatNumber(value);
1093
1459
  if (typeof value === "string") return value;
1094
1460
  if (Array.isArray(value)) return value.map(formatCell).join(", ");
1095
1461
  return JSON.stringify(value);
1096
1462
  }
1097
1463
  function formatColumnCell(value, format) {
1098
- const written = format === "date" ? readDateContent(value)?.date : void 0;
1099
- const day = written === void 0 ? null : /^\d{4}-\d{2}-\d{2}/.exec(written);
1100
- if (day) {
1101
- const date = /* @__PURE__ */ new Date(`${day[0]}T12:00:00Z`);
1102
- if (!Number.isNaN(date.getTime()))
1103
- return date.toLocaleDateString("en-GB", {
1104
- day: "numeric",
1105
- month: "short",
1106
- year: "numeric",
1107
- timeZone: "UTC"
1108
- });
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);
1109
1469
  }
1110
1470
  return formatCell(value);
1111
1471
  }
@@ -1126,23 +1486,9 @@ function DataTable({ props }) {
1126
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)) })
1127
1487
  ] }) });
1128
1488
  }
1129
- function numberFormat(format) {
1130
- switch (format) {
1131
- case "integer":
1132
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 });
1133
- case "decimal":
1134
- return new Intl.NumberFormat("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
1135
- case "compact":
1136
- return new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 });
1137
- case "plain":
1138
- return new Intl.NumberFormat("en-US", { maximumFractionDigits: 20, useGrouping: false });
1139
- default:
1140
- return format;
1141
- }
1142
- }
1143
1489
  function Metric({ props }) {
1144
1490
  const value = props.value;
1145
- const text = typeof value === "number" ? numberFormat(props.format ?? "plain").format(value) : textOf(value) || "\u2014";
1491
+ const text = typeof value === "number" ? formatNumber(value, props.format ?? "plain") : textOf(value) || "\u2014";
1146
1492
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
1147
1493
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium uppercase tracking-wider text-muted-foreground", children: props.label }),
1148
1494
  /* @__PURE__ */ jsxs("span", { className: "text-2xl font-semibold tabular-nums text-foreground", children: [
@@ -1175,8 +1521,8 @@ function Split({
1175
1521
  }
1176
1522
  function Tabs({ props, children }) {
1177
1523
  const tabs = props.tabs ?? [];
1178
- const panels = React6.Children.toArray(children);
1179
- const [chosen, setChosen] = React6.useState(null);
1524
+ const panels = React2.Children.toArray(children);
1525
+ const [chosen, setChosen] = React2.useState(null);
1180
1526
  const open = chosen ? openIndex(tabs, chosen) : 0;
1181
1527
  const value = String(open);
1182
1528
  const choose = (next) => {
@@ -1203,7 +1549,7 @@ function Tabs({ props, children }) {
1203
1549
  forceMount: true,
1204
1550
  hidden: index !== open,
1205
1551
  className: "mt-4 focus-visible:outline-hidden",
1206
- children: panels[index] ?? null
1552
+ children: /* @__PURE__ */ jsx(RevealPanel, { open: () => choose(String(index)), children: panels[index] ?? null })
1207
1553
  },
1208
1554
  index
1209
1555
  ))
@@ -1218,7 +1564,10 @@ function openIndex(tabs, { value, occurrence }) {
1218
1564
  }
1219
1565
  return 0;
1220
1566
  }
1221
- var BUTTON_BASE = "inline-flex min-h-11 items-center justify-center rounded-md px-4 text-sm font-medium transition-colors focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50";
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
+ );
1222
1571
  var PRIMARY_BUTTON = cn(BUTTON_BASE, "bg-primary text-primary-foreground hover:bg-primary/90");
1223
1572
  var SECONDARY_BUTTON = cn(
1224
1573
  BUTTON_BASE,
@@ -1226,8 +1575,8 @@ var SECONDARY_BUTTON = cn(
1226
1575
  );
1227
1576
  function Steps({ props, children }) {
1228
1577
  const steps = props.steps ?? [];
1229
- const panels = React6.Children.toArray(children);
1230
- const [chosen, setIndex] = React6.useState(0);
1578
+ const panels = React2.Children.toArray(children);
1579
+ const [chosen, setIndex] = React2.useState(0);
1231
1580
  const last = steps.length - 1;
1232
1581
  const index = Math.max(0, Math.min(chosen, last));
1233
1582
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-5", children: [
@@ -1255,14 +1604,14 @@ function Steps({ props, children }) {
1255
1604
  },
1256
1605
  `${position}-${label}`
1257
1606
  )) }),
1258
- steps.map((_step, position) => /* @__PURE__ */ jsx("div", { hidden: position !== index, children: panels[position] ?? null }, position)),
1607
+ steps.map((_step, position) => /* @__PURE__ */ jsx("div", { hidden: position !== index, children: /* @__PURE__ */ jsx(RevealPanel, { open: () => setIndex(position), children: panels[position] ?? null }) }, position)),
1259
1608
  index > 0 || index < last ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
1260
1609
  index > 0 ? /* @__PURE__ */ jsx(
1261
1610
  "button",
1262
1611
  {
1263
1612
  type: "button",
1264
1613
  "data-testid": "steps-back",
1265
- onClick: () => setIndex((current) => Math.max(0, current - 1)),
1614
+ onClick: () => setIndex(Math.max(0, index - 1)),
1266
1615
  className: SECONDARY_BUTTON,
1267
1616
  children: props.backLabel ?? "Back"
1268
1617
  }
@@ -1272,7 +1621,7 @@ function Steps({ props, children }) {
1272
1621
  {
1273
1622
  type: "button",
1274
1623
  "data-testid": "steps-next",
1275
- onClick: () => setIndex((current) => Math.min(last, current + 1)),
1624
+ onClick: () => setIndex(Math.min(last, index + 1)),
1276
1625
  className: PRIMARY_BUTTON,
1277
1626
  children: props.nextLabel ?? "Next"
1278
1627
  }
@@ -1357,7 +1706,7 @@ function Segmented({ props, bindings, emit }) {
1357
1706
  props.value ?? void 0,
1358
1707
  bindings?.value
1359
1708
  );
1360
- const [localValue, setLocalValue] = React6.useState("");
1709
+ const [localValue, setLocalValue] = React2.useState("");
1361
1710
  const isBound = Boolean(bindings?.value);
1362
1711
  const value = isBound ? boundValue ?? "" : localValue;
1363
1712
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -1397,7 +1746,7 @@ function NumberInput({ props, bindings, emit }) {
1397
1746
  props.value ?? void 0,
1398
1747
  bindings?.value
1399
1748
  );
1400
- const [localValue, setLocalValue] = React6.useState(void 0);
1749
+ const [localValue, setLocalValue] = React2.useState(void 0);
1401
1750
  const isBound = Boolean(bindings?.value);
1402
1751
  const value = isBound ? boundValue : localValue;
1403
1752
  const setValue = isBound ? setBoundValue : setLocalValue;
@@ -1478,20 +1827,56 @@ var generativeRenderers = withDeclaredProps(
1478
1827
  { MthdsField: ["path"], MthdsResult: ["path"] }
1479
1828
  );
1480
1829
  var RADIUS = "rounded-[calc(var(--radius,0.5rem)*1.5)]";
1481
- function useAbsolute(path) {
1482
- const repeat = useRepeatScope();
1483
- if (path.startsWith("/")) return path;
1484
- if (!repeat) return path;
1485
- 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;
1486
1871
  }
1487
- function asDate(value) {
1488
- if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}/.test(value)) return void 0;
1489
- const date = /* @__PURE__ */ new Date(`${value.slice(0, 10)}T12:00:00Z`);
1490
- return Number.isNaN(date.getTime()) ? void 0 : date;
1872
+ function isRecord2(value) {
1873
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1491
1874
  }
1492
1875
  function Composer({ props, children, emit }) {
1493
1876
  const host = useFrameHost();
1494
1877
  const busy = useDescriptorScope().env?.disabled ?? false;
1878
+ const waitsFor = useRunWaitsFor();
1879
+ const waitsForId = React2.useId();
1495
1880
  const two = props.columns === "2";
1496
1881
  return /* @__PURE__ */ jsxs(
1497
1882
  "div",
@@ -1528,16 +1913,20 @@ function Composer({ props, children, emit }) {
1528
1913
  }
1529
1914
  ),
1530
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: [
1531
- host.eta ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2 text-sm text-muted-foreground max-sm:justify-center", children: [
1532
- /* @__PURE__ */ jsx(Clock, { className: "size-4", "aria-hidden": "true" }),
1533
- " ",
1534
- host.eta
1535
- ] }) : /* @__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
+ ] }),
1536
1924
  /* @__PURE__ */ jsxs(
1537
1925
  "button",
1538
1926
  {
1539
1927
  type: "button",
1540
1928
  disabled: busy,
1929
+ "aria-describedby": waitsFor ? waitsForId : void 0,
1541
1930
  onClick: () => emit("press"),
1542
1931
  className: cn(
1543
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",
@@ -1609,19 +1998,10 @@ function segmentMix(index, count) {
1609
1998
  function segmentColor(index, count) {
1610
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%)))`;
1611
2000
  }
1612
- function amount(value, unit) {
1613
- if (unit && /^[A-Z]{3}$/.test(unit)) {
1614
- try {
1615
- return new Intl.NumberFormat("en-GB", {
1616
- style: "currency",
1617
- currency: unit,
1618
- maximumFractionDigits: 0
1619
- }).format(value);
1620
- } catch {
1621
- }
1622
- }
1623
- const n = new Intl.NumberFormat("en-GB", { maximumFractionDigits: 2 }).format(value);
1624
- 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;
1625
2005
  }
1626
2006
  function ProportionBar({ props }) {
1627
2007
  const rows = (Array.isArray(props.items) ? props.items : []).filter(
@@ -1632,8 +2012,10 @@ function ProportionBar({ props }) {
1632
2012
  value: shareAmount(row[props.valueKey])
1633
2013
  })).filter((p) => Number.isFinite(p.value) && p.value > 0);
1634
2014
  const sum = parts.reduce((s, p) => s + p.value, 0);
1635
- const total = typeof props.total === "number" ? props.total : sum;
1636
- 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;
1637
2019
  const cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
1638
2020
  return /* @__PURE__ */ jsxs(
1639
2021
  "div",
@@ -1648,10 +2030,10 @@ function ProportionBar({ props }) {
1648
2030
  children: [
1649
2031
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2.5", children: [
1650
2032
  /* @__PURE__ */ jsx("span", { className: "text-[15px] text-muted-foreground", children: props.label }),
1651
- /* @__PURE__ */ jsx("strong", { className: "text-2xl font-semibold tabular-nums text-foreground", children: amount(total, props.unit) }),
1652
- 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: [
1653
2035
  "of ",
1654
- amount(props.of, props.unit)
2036
+ formatAmount(ceiling, props.unit)
1655
2037
  ] }) : null
1656
2038
  ] }),
1657
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(
@@ -1681,7 +2063,7 @@ function ProportionBar({ props }) {
1681
2063
  ),
1682
2064
  cap(p.label),
1683
2065
  " ",
1684
- amount(p.value, props.unit)
2066
+ formatAmount(p.value, props.unit)
1685
2067
  ]
1686
2068
  },
1687
2069
  `${i}-${p.label}`
@@ -1714,11 +2096,10 @@ var ASPECT = {
1714
2096
  function Picture({
1715
2097
  props
1716
2098
  }) {
1717
- const written = typeof props.path === "string" ? props.path : void 0;
1718
- const path = useAbsolute(written ?? "");
1719
- const value = useStateValue(path);
2099
+ const path = useAbsolutePath(props.path, true);
2100
+ const value = useStateValue(path ?? "");
1720
2101
  const resolve = useResolveUrl();
1721
- const content = written === void 0 ? void 0 : readImageContent(value);
2102
+ const content = path === void 0 ? void 0 : readImageContent(value);
1722
2103
  const src = content ? viewableUrl(resolve?.(content.url)) ?? viewableUrl(content.publicUrl) ?? viewableUrl(content.url) : void 0;
1723
2104
  const aspect = props.aspect && props.aspect !== "auto" ? ASPECT[props.aspect] : void 0;
1724
2105
  if (!src) return null;
@@ -1757,16 +2138,11 @@ function Detail({ props }) {
1757
2138
  ] })
1758
2139
  );
1759
2140
  }
1760
- var DATE_STYLES = {
1761
- long: { weekday: "long", day: "numeric", month: "long", year: "numeric", timeZone: "UTC" },
1762
- short: { day: "numeric", month: "short", year: "numeric", timeZone: "UTC" },
1763
- weekday: { weekday: "short", day: "numeric", month: "long", timeZone: "UTC" }
1764
- };
1765
2141
  function DateText({ props }) {
1766
- const date = asDate(props.value);
1767
- if (!date) return null;
2142
+ const day = formatDay(props.value, props.style ?? "long");
2143
+ if (day === void 0) return null;
1768
2144
  const tone = props.tone === "accent" ? cn("text-[13px] font-semibold", linkTextClass) : props.tone === "muted" ? "text-sm text-muted-foreground" : "text-[15px] text-foreground";
1769
- 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 });
1770
2146
  }
1771
2147
  function TextList({ props }) {
1772
2148
  const items = Array.isArray(props.items) ? props.items.filter((x) => typeof x === "string" && x.trim()) : [];
@@ -1805,10 +2181,13 @@ var frameRenderers = withDeclaredProps(
1805
2181
  Detail: (ctx) => /* @__PURE__ */ jsx(Detail, { ...ctx })
1806
2182
  },
1807
2183
  // A refused `path` reaches the renderer as refused: its nearest value, the
1808
- // empty text, is the item itself inside a repeat.
1809
- { Picture: ["path"] }
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"] }
1810
2189
  );
1811
- var ResultSlotContext = React6.createContext(null);
2190
+ var ResultSlotContext = React2.createContext(null);
1812
2191
  function ResultSlotProvider({
1813
2192
  slot,
1814
2193
  children
@@ -1816,7 +2195,7 @@ function ResultSlotProvider({
1816
2195
  return /* @__PURE__ */ jsx(ResultSlotContext.Provider, { value: slot, children });
1817
2196
  }
1818
2197
  function useResultSlot() {
1819
- return React6.useContext(ResultSlotContext);
2198
+ return React2.useContext(ResultSlotContext);
1820
2199
  }
1821
2200
  var LABEL2 = "block text-[13.5px] leading-none font-medium text-foreground";
1822
2201
  function AppBar({ props }) {
@@ -1845,7 +2224,7 @@ function Hero({ props }) {
1845
2224
  ] });
1846
2225
  }
1847
2226
  function Workspace({ props, children }) {
1848
- const [work, rail] = React6.Children.toArray(children);
2227
+ const [work, rail] = React2.Children.toArray(children);
1849
2228
  const railLeft = props.rail === "left";
1850
2229
  return /* @__PURE__ */ jsxs(
1851
2230
  "div",
@@ -1971,13 +2350,13 @@ function useTextField(props, bindings) {
1971
2350
  props.value ?? void 0,
1972
2351
  bindings?.value
1973
2352
  );
1974
- const [localValue, setLocalValue] = React6.useState("");
2353
+ const [localValue, setLocalValue] = React2.useState("");
1975
2354
  const isBound = Boolean(bindings?.value);
1976
2355
  const value = isBound ? boundValue ?? "" : localValue;
1977
2356
  const setValue = isBound ? setBoundValue : setLocalValue;
1978
2357
  const validateOn = props.validateOn ?? "blur";
1979
2358
  const hasValidation = Boolean(bindings?.value && props.checks?.length);
1980
- const { errors, validate } = useCheckedField(
2359
+ const { errors, validate, focusRef } = useCheckedField(
1981
2360
  bindings?.value ?? "",
1982
2361
  hasValidation ? { checks: props.checks ?? [], validateOn } : void 0
1983
2362
  );
@@ -1988,7 +2367,7 @@ function useTextField(props, bindings) {
1988
2367
  const onBlur = () => {
1989
2368
  if (hasValidation && validateOn === "blur") validate();
1990
2369
  };
1991
- return { value, errors, onChange, onBlur };
2370
+ return { value, errors, onChange, onBlur, focusRef };
1992
2371
  }
1993
2372
  function BrandInput({
1994
2373
  props,
@@ -2007,7 +2386,9 @@ function BrandInput({
2007
2386
  type: props.type ?? "text",
2008
2387
  value: field.value,
2009
2388
  placeholder: props.placeholder ?? "",
2389
+ ref: field.focusRef,
2010
2390
  "aria-invalid": field.errors.length > 0 || void 0,
2391
+ "aria-describedby": field.errors[0] ? `${id}-error` : void 0,
2011
2392
  onChange: (event) => field.onChange(event.target.value),
2012
2393
  onBlur: () => {
2013
2394
  field.onBlur();
@@ -2020,7 +2401,7 @@ function BrandInput({
2020
2401
  className: cn(fieldControlClass, "h-11 px-3.5")
2021
2402
  }
2022
2403
  ),
2023
- 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
2024
2405
  ] });
2025
2406
  }
2026
2407
  function BrandTextarea({
@@ -2039,13 +2420,15 @@ function BrandTextarea({
2039
2420
  rows: props.rows ?? 3,
2040
2421
  value: field.value,
2041
2422
  placeholder: props.placeholder ?? "",
2423
+ ref: field.focusRef,
2042
2424
  "aria-invalid": field.errors.length > 0 || void 0,
2425
+ "aria-describedby": field.errors[0] ? `${id}-error` : void 0,
2043
2426
  onChange: (event) => field.onChange(event.target.value),
2044
2427
  onBlur: field.onBlur,
2045
2428
  className: cn(fieldControlClass, "px-3.5 py-2.5 leading-relaxed")
2046
2429
  }
2047
2430
  ),
2048
- 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
2049
2432
  ] });
2050
2433
  }
2051
2434
  var { registry: generativeRegistry } = defineRegistry(catalog, {
@@ -2079,29 +2462,29 @@ function GenerativePage({
2079
2462
  onElementError
2080
2463
  }) {
2081
2464
  const { inputs, result, env, idPrefix } = scope;
2082
- const stableScope = React6.useMemo(
2465
+ const stableScope = React2.useMemo(
2083
2466
  () => ({ inputs, result, env, idPrefix }),
2084
2467
  [inputs, result, env, idPrefix]
2085
2468
  );
2086
- const latest = React6.useRef({ onRun, store });
2087
- React6.useEffect(() => {
2469
+ const latest = React2.useRef({ onRun, store });
2470
+ React2.useEffect(() => {
2088
2471
  latest.current = { onRun, store };
2089
2472
  });
2090
- const report = React6.useRef(onElementError);
2091
- React6.useInsertionEffect(() => {
2473
+ const report = React2.useRef(onElementError);
2474
+ React2.useInsertionEffect(() => {
2092
2475
  report.current = onElementError;
2093
2476
  });
2094
- const [made] = React6.useState(() => /* @__PURE__ */ new Map());
2095
- const pageRegistry = React6.useMemo(
2477
+ const [made] = React2.useState(() => /* @__PURE__ */ new Map());
2478
+ const pageRegistry = React2.useMemo(
2096
2479
  () => reportingRegistry(registry, report, made),
2097
2480
  [registry, made]
2098
2481
  );
2099
2482
  const rootType = spec?.elements?.[spec.root]?.type ?? "root";
2100
2483
  const reporting = onElementError !== void 0;
2101
- const validation = React6.useRef(null);
2102
- const [checkedFields] = React6.useState(() => new CheckedFields());
2103
- const running = React6.useRef(false);
2104
- const [handlers] = React6.useState(() => {
2484
+ const validation = React2.useRef(null);
2485
+ const [checkedFields] = React2.useState(() => new CheckedFields());
2486
+ const running = React2.useRef(false);
2487
+ const [handlers] = React2.useState(() => {
2105
2488
  const release = () => {
2106
2489
  setTimeout(() => {
2107
2490
  running.current = false;
@@ -2111,11 +2494,7 @@ function GenerativePage({
2111
2494
  run: () => {
2112
2495
  if (running.current) return void 0;
2113
2496
  const checks = validation.current;
2114
- if (checks) {
2115
- const registered = checks.validateAll();
2116
- const mounted = checkedFields.check(checks);
2117
- if (!registered || !mounted) return void 0;
2118
- }
2497
+ if (checks && !checkedFields.check(checks)) return void 0;
2119
2498
  running.current = true;
2120
2499
  let pending;
2121
2500
  try {
@@ -2134,20 +2513,29 @@ function GenerativePage({
2134
2513
  }
2135
2514
  };
2136
2515
  });
2137
- const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(JSONUIProvider, { registry: pageRegistry, store, handlers, children: /* @__PURE__ */ jsx(ReportingContext.Provider, { value: reporting, children: /* @__PURE__ */ jsxs(CheckedFieldsContext.Provider, { value: checkedFields, children: [
2138
- /* @__PURE__ */ jsx(ValidationBridge, { into: validation, fields: checkedFields }),
2139
- /* @__PURE__ */ jsx(
2140
- RootBoundary,
2141
- {
2142
- type: rootType,
2143
- report,
2144
- reporting,
2145
- loading: loading === true,
2146
- spec,
2147
- children: /* @__PURE__ */ jsx(Renderer, { spec, registry: pageRegistry, loading })
2148
- }
2149
- )
2150
- ] }) }) }) });
2516
+ const page = /* @__PURE__ */ jsx(DescriptorProvider, { scope: stableScope, children: /* @__PURE__ */ jsx(PlacedInputsProvider, { spec, children: /* @__PURE__ */ jsx(
2517
+ JSONUIProvider,
2518
+ {
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
+ ] }) })
2537
+ }
2538
+ ) }) });
2151
2539
  return brand ? /* @__PURE__ */ jsx(BrandProvider, { manifest: brand, children: page }) : page;
2152
2540
  }
2153
2541
  function ValidationBridge({
@@ -2155,20 +2543,20 @@ function ValidationBridge({
2155
2543
  fields
2156
2544
  }) {
2157
2545
  const validation = useOptionalValidation();
2158
- React6.useInsertionEffect(() => {
2546
+ React2.useInsertionEffect(() => {
2159
2547
  into.current = validation;
2160
2548
  fields.attach(validation);
2161
2549
  });
2162
2550
  return null;
2163
2551
  }
2164
2552
  function useStoreSnapshot(store) {
2165
- return React6.useSyncExternalStore(
2553
+ return React2.useSyncExternalStore(
2166
2554
  store.subscribe,
2167
2555
  store.getSnapshot,
2168
2556
  store.getServerSnapshot ?? store.getSnapshot
2169
2557
  );
2170
2558
  }
2171
2559
 
2172
- export { BrandBar, BrandProvider, DescriptorProvider, FrameHostProvider, GenerativePage, ResultSlotProvider, domIdFor, generativeRegistry, generativeRenderers, pathFromDomId, useBrand, useCheckedField, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot };
2560
+ export { BrandBar, BrandProvider, DescriptorProvider, FrameHostProvider, GenerativePage, ResultSlotProvider, RevealPanel, domIdFor, generativeRegistry, generativeRenderers, pathFromDomId, useBrand, useCheckedField, useDescriptorScope, useFrameHost, useResultSlot, useStoreSnapshot };
2173
2561
  //# sourceMappingURL=index.js.map
2174
2562
  //# sourceMappingURL=index.js.map