@pipelex/mthds-form 0.8.0 → 0.10.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 (66) hide show
  1. package/README.md +9 -3
  2. package/dist/{chunk-4BSBBG2T.js → chunk-4JQAJT7C.js} +3 -3
  3. package/dist/{chunk-4BSBBG2T.js.map → chunk-4JQAJT7C.js.map} +1 -1
  4. package/dist/{chunk-T4NSAGOS.js → chunk-4KGQ65TQ.js} +4 -4
  5. package/dist/{chunk-T4NSAGOS.js.map → chunk-4KGQ65TQ.js.map} +1 -1
  6. package/dist/chunk-CAKQ5VFT.js +35 -0
  7. package/dist/chunk-CAKQ5VFT.js.map +1 -0
  8. package/dist/{chunk-IIU3YK4O.js → chunk-DARLDVJM.js} +3 -3
  9. package/dist/{chunk-IIU3YK4O.js.map → chunk-DARLDVJM.js.map} +1 -1
  10. package/dist/{chunk-25I6JDIK.js → chunk-FABY3DEY.js} +2 -2
  11. package/dist/chunk-FABY3DEY.js.map +1 -0
  12. package/dist/chunk-G6BWZUGV.js +2840 -0
  13. package/dist/chunk-G6BWZUGV.js.map +1 -0
  14. package/dist/{chunk-R7B7ID25.js → chunk-IUWLOSAM.js} +4 -4
  15. package/dist/{chunk-R7B7ID25.js.map → chunk-IUWLOSAM.js.map} +1 -1
  16. package/dist/chunk-TID7ZN63.js +198 -0
  17. package/dist/chunk-TID7ZN63.js.map +1 -0
  18. package/dist/{chunk-XYQ7NXQZ.js → chunk-WYH56SOZ.js} +2 -2
  19. package/dist/chunk-WYH56SOZ.js.map +1 -0
  20. package/dist/chunk-XCO6GTEC.js +38 -0
  21. package/dist/chunk-XCO6GTEC.js.map +1 -0
  22. package/dist/{chunk-YEVVR6FE.js → chunk-Y2ND6W4W.js} +3 -3
  23. package/dist/{chunk-YEVVR6FE.js.map → chunk-Y2ND6W4W.js.map} +1 -1
  24. package/dist/{chunk-J4L32LD6.js → chunk-ZB4RYUKV.js} +5 -5
  25. package/dist/chunk-ZB4RYUKV.js.map +1 -0
  26. package/dist/core/contracts.d.ts +4 -4
  27. package/dist/core/contracts.js +1 -1
  28. package/dist/core/derive.d.ts +1 -0
  29. package/dist/core/derive.js +3 -3
  30. package/dist/core/descriptor.d.ts +13 -2
  31. package/dist/core/descriptor.js +1 -1
  32. package/dist/core/file-formats.d.ts +34 -7
  33. package/dist/core/file-formats.js +1 -1
  34. package/dist/core/gate.js +3 -3
  35. package/dist/core/gating-fields.d.ts +1 -0
  36. package/dist/core/gating-fields.js +2 -2
  37. package/dist/core/index.d.ts +3 -2
  38. package/dist/core/index.js +11 -10
  39. package/dist/core/narrow-file-formats.d.ts +50 -0
  40. package/dist/core/narrow-file-formats.js +4 -0
  41. package/dist/core/narrow-file-formats.js.map +1 -0
  42. package/dist/core/native-content.d.ts +75 -19
  43. package/dist/core/native-content.js +1 -1
  44. package/dist/core/output-form.js +2 -2
  45. package/dist/core/readiness.d.ts +1 -0
  46. package/dist/core/stuff-files.d.ts +1 -0
  47. package/dist/core/stuff-files.js +2 -2
  48. package/dist/core/values.d.ts +1 -0
  49. package/dist/core/values.js +4 -4
  50. package/dist/field-renderer-BSU2hMf6.d.ts +34 -0
  51. package/dist/generative/index.d.ts +2106 -0
  52. package/dist/generative/index.js +3093 -0
  53. package/dist/generative/index.js.map +1 -0
  54. package/dist/layout-design.mthds +452 -0
  55. package/dist/react/index.d.ts +107 -45
  56. package/dist/react/index.js +8 -2614
  57. package/dist/react/index.js.map +1 -1
  58. package/dist/styles.css +1 -1
  59. package/package.json +19 -7
  60. package/dist/chunk-25I6JDIK.js.map +0 -1
  61. package/dist/chunk-2JWRG53S.js +0 -33
  62. package/dist/chunk-2JWRG53S.js.map +0 -1
  63. package/dist/chunk-45C2MKK6.js +0 -94
  64. package/dist/chunk-45C2MKK6.js.map +0 -1
  65. package/dist/chunk-J4L32LD6.js.map +0 -1
  66. package/dist/chunk-XYQ7NXQZ.js.map +0 -1
@@ -0,0 +1,2840 @@
1
+ import { collectStuffFiles } from './chunk-4JQAJT7C.js';
2
+ import { viewableUrl, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativeValueNode, readDateContent, formatDateContent, readImageContent, readDataUrl, readDocumentContent } from './chunk-TID7ZN63.js';
3
+ import { conceptCategory } from './chunk-FABY3DEY.js';
4
+ import { acceptLabel, acceptMap, isAcceptedFile, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
5
+ import { isFilled } from './chunk-PM55AHRO.js';
6
+ import { toStoredDateValue, toDateInputValue } from './chunk-GKV77RAS.js';
7
+ import { ownProp } from './chunk-V3OV2XQT.js';
8
+ import { clsx } from 'clsx';
9
+ import { twMerge } from 'tailwind-merge';
10
+ import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image, FileText, CalendarDays, Type, Loader2, ImageOff, Upload, Link2, EyeOff, Eye, X, Minus, Plus, Trash2, Copy, Download, ChevronRight } from 'lucide-react';
11
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
12
+ import * as React2 from 'react';
13
+ import { createContext, use, useId, useMemo, useContext, useState, useRef, useCallback, useEffect, Fragment as Fragment$1, useLayoutEffect } from 'react';
14
+ import * as SwitchPrimitives from '@radix-ui/react-switch';
15
+ import { useDropzone } from 'react-dropzone';
16
+ import * as SelectPrimitive from '@radix-ui/react-select';
17
+ import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
18
+ import * as TogglePrimitive from '@radix-ui/react-toggle';
19
+ import { cva } from 'class-variance-authority';
20
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
21
+ import { marked } from 'marked';
22
+
23
+ function cn(...inputs) {
24
+ return twMerge(clsx(inputs));
25
+ }
26
+ var CATEGORY_ICON = {
27
+ text: Type,
28
+ date: CalendarDays,
29
+ document: FileText,
30
+ image: Image,
31
+ number: Hash,
32
+ boolean: ToggleLeft,
33
+ choice: ListChecks,
34
+ structured: Braces,
35
+ list: List
36
+ };
37
+ function ConceptPill({ conceptRef, category, className }) {
38
+ const Icon2 = CATEGORY_ICON[category];
39
+ const label = conceptRef ?? category;
40
+ return /* @__PURE__ */ jsxs(
41
+ "span",
42
+ {
43
+ className: cn(
44
+ "inline-flex items-center gap-1 font-mono text-[10.5px] leading-none text-muted-foreground",
45
+ className
46
+ ),
47
+ children: [
48
+ /* @__PURE__ */ jsx(Icon2, { className: "h-3 w-3 shrink-0 opacity-70", strokeWidth: 1.75, "aria-hidden": true }),
49
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: label })
50
+ ]
51
+ }
52
+ );
53
+ }
54
+ var FieldPresentationContext = createContext("studio");
55
+ function FieldPresentationProvider({
56
+ presentation,
57
+ children
58
+ }) {
59
+ return /* @__PURE__ */ jsx(FieldPresentationContext, { value: presentation, children });
60
+ }
61
+ function useFieldPresentation() {
62
+ return use(FieldPresentationContext);
63
+ }
64
+ function humanizeFieldName(name) {
65
+ const words = name.replace(/[_-]+/g, " ").trim();
66
+ if (words === "") return name;
67
+ return words.charAt(0).toUpperCase() + words.slice(1);
68
+ }
69
+ function humanizeEnumValue(code) {
70
+ if (!ENUM_CODE.test(code)) return code;
71
+ const allCaps = code === code.toUpperCase() && code !== code.toLowerCase();
72
+ if (!allCaps) return humanizeFieldName(code);
73
+ return CODE_SEPARATOR.test(code) ? humanizeFieldName(code.toLowerCase()) : code;
74
+ }
75
+ var ENUM_CODE = /^\p{L}[\p{L}\p{N}_-]*$/u;
76
+ var CODE_SEPARATOR = /[_-]/;
77
+ function enumLabeler(options, presentation) {
78
+ if (presentation !== "app") return asWritten;
79
+ let labeler = APP_LABELERS.get(options);
80
+ if (labeler === void 0) {
81
+ labeler = wordsOrCodes(options);
82
+ APP_LABELERS.set(options, labeler);
83
+ }
84
+ return labeler;
85
+ }
86
+ function asWritten(value) {
87
+ return value;
88
+ }
89
+ function wordsOrCodes(options) {
90
+ const labels = /* @__PURE__ */ new Map();
91
+ const owners = /* @__PURE__ */ new Map();
92
+ for (const option of options) {
93
+ const label = humanizeEnumValue(option);
94
+ const owner = owners.get(label);
95
+ if (owner !== void 0 && owner !== option) return asWritten;
96
+ owners.set(label, option);
97
+ labels.set(option, label);
98
+ }
99
+ return (value) => labels.get(value) ?? value;
100
+ }
101
+ var APP_LABELERS = /* @__PURE__ */ new WeakMap();
102
+ function fieldLabel(title, name, presentation) {
103
+ if (title !== void 0) return title;
104
+ return presentation === "app" ? humanizeFieldName(name) : name;
105
+ }
106
+ var Switch = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
107
+ SwitchPrimitives.Root,
108
+ {
109
+ className: cn(
110
+ "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
111
+ className
112
+ ),
113
+ ...props,
114
+ ref,
115
+ children: /* @__PURE__ */ jsx(
116
+ SwitchPrimitives.Thumb,
117
+ {
118
+ className: cn(
119
+ "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
120
+ )
121
+ }
122
+ )
123
+ }
124
+ ));
125
+ Switch.displayName = SwitchPrimitives.Root.displayName;
126
+ var FieldDomIdContext = createContext(null);
127
+ function FieldDomIdProvider({
128
+ prefix,
129
+ children
130
+ }) {
131
+ const generated = useId();
132
+ return /* @__PURE__ */ jsx(FieldDomIdContext, { value: prefix ?? generated, children });
133
+ }
134
+ function useFieldDomId(path) {
135
+ const scoped = use(FieldDomIdContext);
136
+ const local = useId();
137
+ const prefix = scoped ?? local;
138
+ return prefix === "" ? path : `${prefix}-${path}`;
139
+ }
140
+ function BooleanField({ field, value, onChange, id, disabled }) {
141
+ const presentation = useFieldPresentation();
142
+ const isApp = presentation === "app";
143
+ const domId = useFieldDomId(id);
144
+ return /* @__PURE__ */ jsxs(
145
+ "div",
146
+ {
147
+ className: cn(
148
+ "flex items-center justify-between gap-4 rounded-md border border-border bg-input px-3.5 py-3",
149
+ disabled && "opacity-50"
150
+ ),
151
+ children: [
152
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
153
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
154
+ /* @__PURE__ */ jsx(
155
+ "label",
156
+ {
157
+ htmlFor: domId,
158
+ className: cn(
159
+ "text-[13px] font-medium leading-none text-foreground",
160
+ isApp ? "text-[13.5px]" : "font-mono"
161
+ ),
162
+ children: fieldLabel(field.title, field.name, presentation)
163
+ }
164
+ ),
165
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "boolean" })
166
+ ] }),
167
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description })
168
+ ] }),
169
+ /* @__PURE__ */ jsx(
170
+ Switch,
171
+ {
172
+ id: domId,
173
+ checked: value ?? false,
174
+ onCheckedChange: onChange,
175
+ disabled,
176
+ className: "shrink-0"
177
+ }
178
+ )
179
+ ]
180
+ }
181
+ );
182
+ }
183
+ function formatBytes(bytes) {
184
+ if (bytes < 1024) return bytes === 1 ? "1 byte" : `${bytes} bytes`;
185
+ if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
186
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
187
+ }
188
+ var DEFAULT_FIELD_STRINGS = {
189
+ optionalBadge: "optional",
190
+ selectPlaceholder: "Select\u2026",
191
+ typeValuePlaceholder: "Type a value\u2026",
192
+ writeHerePlaceholder: "Write here\u2026",
193
+ jsonHint: "Enter this value as raw JSON.",
194
+ decrease: "Decrease",
195
+ increase: "Increase",
196
+ noItemsYet: "No items yet",
197
+ addItem: "Add item",
198
+ removeItemAria: (index) => `Remove item ${index}`,
199
+ itemsCount: (count) => count === 1 ? "1 item" : `${count} items`,
200
+ itemsCountOf: (count, total) => `${count} of ${total} ${total === 1 ? "item" : "items"}`,
201
+ fieldsCount: (count) => count === 1 ? "1 field" : `${count} fields`,
202
+ uploading: "Uploading\u2026",
203
+ dropToUpload: "Drop to upload",
204
+ dropOrBrowse: "Drop a file or click to browse",
205
+ pasteUrlInstead: "paste a URL instead",
206
+ urlPlaceholder: "https://\u2026",
207
+ fileUrlAria: (label) => label ? `Link to the file for ${label}` : "Link to the file",
208
+ uploadedFile: "Attached file",
209
+ encodedFileSummary: (format, bytes) => `${format} \xB7 ${formatBytes(bytes)}`,
210
+ preview: "Preview",
211
+ removeFileAria: "Remove file",
212
+ previewUnavailablePdf: "Preview unavailable - open to view.",
213
+ // U+002D HYPHEN-MINUS, deliberately: not an en dash, not an em dash.
214
+ resultAbsent: "-",
215
+ resultAbsentDescription: "Not provided",
216
+ resultUndescribed: "No output descriptor for this pipe \u2014 showing the raw value.",
217
+ toggleRowDetails: (index) => `Show or hide the details of row ${index}`,
218
+ rowDetailsColumn: "Details",
219
+ copyUrl: "Copy the URL",
220
+ copyText: "Copy the text",
221
+ download: "Download",
222
+ downloading: "Downloading\u2026",
223
+ viewRendered: "Result",
224
+ viewJson: "JSON",
225
+ copyJson: "Copy the JSON",
226
+ resultViewGroup: "Result view",
227
+ yes: "Yes",
228
+ no: "No",
229
+ unsupportedFileType: (formats) => `That file type is not supported. Accepted formats: ${formats}.`,
230
+ hideOptionalFields: "Hide optional fields",
231
+ hideOptionalInputs: "Hide optional inputs",
232
+ optionalFieldsCount: (count) => count === 1 ? "1 optional field" : `${count} optional fields`,
233
+ optionalInputsCount: (count) => count === 1 ? "1 optional input" : `${count} optional inputs`
234
+ };
235
+ var FieldStringsContext = createContext(DEFAULT_FIELD_STRINGS);
236
+ function FieldStringsProvider({
237
+ strings,
238
+ children
239
+ }) {
240
+ const merged = useMemo(() => ({ ...DEFAULT_FIELD_STRINGS, ...strings }), [strings]);
241
+ return /* @__PURE__ */ jsx(FieldStringsContext.Provider, { value: merged, children });
242
+ }
243
+ function useFieldStrings() {
244
+ return useContext(FieldStringsContext);
245
+ }
246
+ function FieldShell({
247
+ name,
248
+ title,
249
+ conceptRef,
250
+ category,
251
+ description,
252
+ required,
253
+ error,
254
+ htmlFor,
255
+ children,
256
+ className
257
+ }) {
258
+ const s = useFieldStrings();
259
+ const presentation = useFieldPresentation();
260
+ const isApp = presentation === "app";
261
+ const Label3 = htmlFor ? "label" : "div";
262
+ const labelText = fieldLabel(title, name, presentation);
263
+ const showLabel = labelText.trim() !== "";
264
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
265
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
266
+ showLabel && /* @__PURE__ */ jsx(
267
+ Label3,
268
+ {
269
+ ...htmlFor ? { htmlFor } : {},
270
+ className: cn(
271
+ "text-[13px] font-medium leading-none text-foreground",
272
+ isApp ? "text-[13.5px]" : "font-mono"
273
+ ),
274
+ children: labelText
275
+ }
276
+ ),
277
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef, category }),
278
+ !required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
279
+ ] }),
280
+ description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: description }),
281
+ isApp && error ? /* @__PURE__ */ jsx("div", { className: "rounded-lg ring-1 ring-destructive/70 ring-offset-4 ring-offset-background", children }) : children,
282
+ error && /* @__PURE__ */ jsx("p", { className: "flex items-center gap-1 text-[11px] text-destructive", role: "alert", children: error })
283
+ ] });
284
+ }
285
+
286
+ // src/react/field-styles.ts
287
+ var fieldControlClass = cn(
288
+ "w-full rounded-md border border-border bg-input text-sm text-foreground",
289
+ "placeholder:text-muted-foreground/70 transition-colors",
290
+ "focus-visible:outline-hidden focus-visible:border-primary focus-visible:ring-1 focus-visible:ring-primary/40",
291
+ "disabled:cursor-not-allowed disabled:opacity-50",
292
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/30"
293
+ );
294
+ function DateField({ field, value, onChange, id, error, disabled }) {
295
+ const domId = useFieldDomId(id);
296
+ return /* @__PURE__ */ jsx(
297
+ FieldShell,
298
+ {
299
+ name: field.name,
300
+ title: field.title,
301
+ conceptRef: field.conceptRef,
302
+ category: "date",
303
+ description: field.description,
304
+ required: field.required,
305
+ error,
306
+ htmlFor: domId,
307
+ children: /* @__PURE__ */ jsx(
308
+ "input",
309
+ {
310
+ id: domId,
311
+ type: "date",
312
+ value: toDateInputValue(value),
313
+ disabled,
314
+ "aria-invalid": !!error,
315
+ onChange: (e) => onChange(toStoredDateValue(e.target.value, field.datetime)),
316
+ className: cn(fieldControlClass, "h-11 px-3 font-mono tabular-nums")
317
+ }
318
+ )
319
+ }
320
+ );
321
+ }
322
+
323
+ // src/react/encoded-file.ts
324
+ function encodedFileSummary(url, strings) {
325
+ const view = readDataUrl(url);
326
+ if (!view) return void 0;
327
+ const format = [...DOCUMENT_FORMATS, ...IMAGE_FORMATS].find((known) => known.mimeType === view.mediaType)?.label ?? view.mediaType;
328
+ return strings.encodedFileSummary(format, view.bytes);
329
+ }
330
+ var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
331
+ var PDF_EXT_RE = /\.pdf(\?|$)/i;
332
+ var DATA_URL_MIME_RE = /^data:([^;,]+)/i;
333
+ var WEB_URL_RE = /^\s*https?:\/\//i;
334
+ var URL_SCHEME_RE = /^\s*[a-z][a-z0-9+.-]*:/i;
335
+ function isReadableLink(url) {
336
+ return WEB_URL_RE.test(url) || !URL_SCHEME_RE.test(url);
337
+ }
338
+ function dataUrlMime(url) {
339
+ return DATA_URL_MIME_RE.exec(url)?.[1]?.toLowerCase();
340
+ }
341
+ function DocumentField(props) {
342
+ return /* @__PURE__ */ jsx(FileField, { ...props, category: "document" });
343
+ }
344
+ function ImageField(props) {
345
+ return /* @__PURE__ */ jsx(FileField, { ...props, category: "image" });
346
+ }
347
+ function FileField({
348
+ field,
349
+ value,
350
+ onDropFile,
351
+ onChange,
352
+ id,
353
+ uploading,
354
+ resolveUrl,
355
+ error,
356
+ disabled,
357
+ category
358
+ }) {
359
+ const s = useFieldStrings();
360
+ const presentation = useFieldPresentation();
361
+ const domId = useFieldDomId(id);
362
+ const [showUrl, setShowUrl] = useState(false);
363
+ const [typedUrl, setTypedUrl] = useState(null);
364
+ const [previewOpen, setPreviewOpen] = useState(false);
365
+ const [localPreview, setLocalPreview] = useState(null);
366
+ const [resolved, setResolved] = useState(null);
367
+ const localUrlRef = useRef(null);
368
+ const setLocal = useCallback((next) => {
369
+ if (localUrlRef.current) URL.revokeObjectURL(localUrlRef.current);
370
+ localUrlRef.current = next?.objectUrl ?? null;
371
+ setLocalPreview(next);
372
+ }, []);
373
+ useEffect(() => () => setLocal(null), [setLocal]);
374
+ const [rejected, setRejected] = useState(null);
375
+ const formats = field.formats;
376
+ const hint = acceptLabel(formats);
377
+ const accept = useMemo(() => acceptMap(formats), [formats]);
378
+ const handleFile = useCallback(
379
+ (file) => {
380
+ if (!isAcceptedFile(formats, file)) {
381
+ setRejected(file.name);
382
+ return;
383
+ }
384
+ setRejected(null);
385
+ setLocal({ objectUrl: URL.createObjectURL(file), type: file.type });
386
+ onDropFile(file);
387
+ },
388
+ [formats, setLocal, onDropFile]
389
+ );
390
+ const busy = disabled || uploading;
391
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
392
+ maxFiles: 1,
393
+ multiple: false,
394
+ disabled: busy,
395
+ // Filters the OS picker so a wrong file is hard to choose in the first
396
+ // place. It is NOT the enforcement: a drag-and-drop bypasses the picker
397
+ // entirely, and a browser reporting an empty `File.type` slips through
398
+ // react-dropzone's own matcher. `handleFile` is where the answer is
399
+ // decided; this is the affordance in front of it.
400
+ accept,
401
+ // The tab stop belongs on the INPUT, not on this div - see the root element
402
+ // below. Without this, react-dropzone puts `tabIndex: 0` and its own key
403
+ // handlers on a `role="presentation"` div, and the element a keyboard or
404
+ // voice-control user lands on is a generic with no role and no name.
405
+ noKeyboard: true,
406
+ onDrop: (files) => {
407
+ const file = files[0];
408
+ if (file) handleFile(file);
409
+ },
410
+ // react-dropzone rejects a file its own matcher refuses without ever calling
411
+ // `onDrop`, so without this a wrong drop is silently swallowed - the control
412
+ // simply does nothing, which reads as a broken dropzone rather than a
413
+ // refused file.
414
+ onDropRejected: (rejections) => {
415
+ const name = rejections[0]?.file.name;
416
+ if (name) setRejected(name);
417
+ }
418
+ });
419
+ const clear = useCallback(() => {
420
+ setLocal(null);
421
+ setResolved(null);
422
+ setPreviewOpen(false);
423
+ setRejected(null);
424
+ onChange(void 0);
425
+ }, [setLocal, onChange]);
426
+ const localIsCurrent = !!localPreview && (localPreview.boundUrl === void 0 || localPreview.boundUrl === value?.url);
427
+ useEffect(() => {
428
+ if (!localPreview) return;
429
+ if (localPreview.boundUrl === void 0) {
430
+ if (uploading || !value?.url) return;
431
+ setLocalPreview({ ...localPreview, boundUrl: value.url });
432
+ return;
433
+ }
434
+ if (value?.url !== localPreview.boundUrl) setLocal(null);
435
+ }, [localPreview, value?.url, uploading, setLocal]);
436
+ const hasFile = !!value?.url;
437
+ const localType = localIsCurrent ? localPreview?.type : void 0;
438
+ const filename = value?.filename ?? "";
439
+ const url = value?.url ?? "";
440
+ const urlMime = dataUrlMime(url);
441
+ const isImage = category === "image" || localType?.startsWith("image/") === true || urlMime?.startsWith("image/") === true || IMAGE_EXT_RE.test(filename) || IMAGE_EXT_RE.test(url);
442
+ const isPdf = localType === "application/pdf" || urlMime === "application/pdf" || PDF_EXT_RE.test(filename) || PDF_EXT_RE.test(url);
443
+ const canPreview = isImage || isPdf;
444
+ const directSrc = viewableUrl(value?.url);
445
+ const storageUri = value?.url && !directSrc && urlMime === void 0 ? value.url : null;
446
+ useEffect(() => {
447
+ if (!previewOpen || !storageUri || localIsCurrent || !resolveUrl) return;
448
+ let cancelled = false;
449
+ void resolveUrl(storageUri).then((src) => {
450
+ if (!cancelled) setResolved(src ? { uri: storageUri, src } : null);
451
+ }).catch(() => {
452
+ if (!cancelled) setResolved(null);
453
+ });
454
+ return () => {
455
+ cancelled = true;
456
+ };
457
+ }, [previewOpen, storageUri, localIsCurrent, resolveUrl]);
458
+ const resolvedSrc = resolved && resolved.uri === storageUri ? viewableUrl(resolved.src) : void 0;
459
+ const previewSrc = (localIsCurrent ? localPreview?.objectUrl : void 0) ?? directSrc ?? resolvedSrc;
460
+ const previewPending = !previewSrc && !!storageUri && resolved?.uri !== storageUri;
461
+ const urlValue = value?.url ?? "";
462
+ const urlText = presentation === "studio" || isReadableLink(urlValue) || urlValue === typedUrl ? urlValue : "";
463
+ return /* @__PURE__ */ jsxs(
464
+ FieldShell,
465
+ {
466
+ name: field.name,
467
+ title: field.title,
468
+ conceptRef: field.conceptRef,
469
+ category,
470
+ description: field.description,
471
+ required: field.required,
472
+ error,
473
+ htmlFor: domId,
474
+ children: [
475
+ hasFile && !uploading ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
476
+ /* @__PURE__ */ jsx(
477
+ FileChip,
478
+ {
479
+ value,
480
+ formats,
481
+ presentation,
482
+ disabled,
483
+ onClear: clear,
484
+ canPreview,
485
+ previewOpen,
486
+ onTogglePreview: () => setPreviewOpen((v) => !v)
487
+ }
488
+ ),
489
+ previewOpen && (previewSrc && isImage ? /* @__PURE__ */ jsx(ImagePreview, { src: previewSrc, filename: value?.filename }) : previewSrc && isPdf ? /* @__PURE__ */ jsx(PdfPreview, { src: previewSrc }) : previewPending ? /* @__PURE__ */ jsx("div", { className: "flex h-24 items-center justify-center rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin text-muted-foreground" }) }) : (
490
+ // Nothing to show and nothing on its way: a reference the gate
491
+ // refuses, or a stored one with no resolver behind it. A spinner
492
+ // here said "loading" about something that was never going to
493
+ // load, which is the one thing a refusal must not look like.
494
+ /* @__PURE__ */ jsx("div", { className: "flex h-24 items-center justify-center rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(ImageOff, { className: "h-5 w-5 text-muted-foreground" }) })
495
+ ))
496
+ ] }) : (
497
+ // `role="presentation"` and no tab stop: this div is the drop TARGET and
498
+ // the visual affordance, and the control inside it is a real
499
+ // `<input type="file">`, which already has the right role and now has a
500
+ // name. Giving the div a `role="button"` of its own would put a second
501
+ // named control in the accessibility tree for one value. The focus ring
502
+ // is `focus-within` because the element that takes focus is clip-hidden.
503
+ /* @__PURE__ */ jsxs(
504
+ "div",
505
+ {
506
+ ...getRootProps(),
507
+ className: cn(
508
+ "flex flex-col items-center justify-center gap-2 rounded-lg border border-dashed px-4 py-6 text-center",
509
+ "cursor-pointer select-none transition-colors",
510
+ "focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 focus-within:ring-offset-background",
511
+ isDragActive ? "border-primary/60 bg-primary/5" : "border-border bg-input hover:border-border hover:bg-muted",
512
+ busy && "cursor-not-allowed opacity-60"
513
+ ),
514
+ children: [
515
+ /* @__PURE__ */ jsx(
516
+ "input",
517
+ {
518
+ ...getInputProps({ tabIndex: 0 }),
519
+ id: domId,
520
+ disabled: busy,
521
+ "aria-invalid": !!error
522
+ }
523
+ ),
524
+ uploading ? /* @__PURE__ */ jsxs(Fragment, { children: [
525
+ /* @__PURE__ */ jsx(Loader2, { className: "h-5 w-5 animate-spin text-muted-foreground" }),
526
+ /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground", children: s.uploading })
527
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
528
+ /* @__PURE__ */ jsx(Upload, { className: "h-5 w-5 text-muted-foreground", strokeWidth: 1.75 }),
529
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
530
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropToUpload : s.dropOrBrowse }),
531
+ hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
532
+ ] })
533
+ ] })
534
+ ]
535
+ }
536
+ )
537
+ ),
538
+ rejected && !busy && /* @__PURE__ */ jsxs("p", { role: "alert", className: "text-[12px] text-destructive", children: [
539
+ /* @__PURE__ */ jsx("span", { className: "font-mono", children: rejected }),
540
+ " \u2014 ",
541
+ s.unsupportedFileType(hint)
542
+ ] }),
543
+ !showUrl ? /* @__PURE__ */ jsxs(
544
+ "button",
545
+ {
546
+ type: "button",
547
+ disabled: busy,
548
+ onClick: () => setShowUrl(true),
549
+ className: "flex w-fit items-center gap-1 font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50",
550
+ children: [
551
+ /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
552
+ s.pasteUrlInstead
553
+ ]
554
+ }
555
+ ) : /* @__PURE__ */ jsx(
556
+ "input",
557
+ {
558
+ type: "text",
559
+ autoFocus: true,
560
+ value: urlText,
561
+ disabled: busy,
562
+ "aria-label": s.fileUrlAria(fieldLabel(field.title, field.name, presentation)),
563
+ placeholder: s.urlPlaceholder,
564
+ onChange: (e) => {
565
+ setLocal(null);
566
+ setTypedUrl(e.target.value);
567
+ onChange(e.target.value ? { url: e.target.value } : void 0);
568
+ },
569
+ className: cn(fieldControlClass, "h-9 px-3 font-mono text-[12px]")
570
+ }
571
+ )
572
+ ]
573
+ }
574
+ );
575
+ }
576
+ function ImagePreview({ src, filename }) {
577
+ const s = useFieldStrings();
578
+ const [broken, setBroken] = useState(false);
579
+ if (broken) {
580
+ return /* @__PURE__ */ jsx("div", { className: "flex h-28 items-center justify-center rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(ImageOff, { className: "h-5 w-5 text-muted-foreground" }) });
581
+ }
582
+ return /* @__PURE__ */ jsx("div", { className: "flex max-h-[640px] justify-center overflow-auto rounded-md border border-border bg-muted", children: /* @__PURE__ */ jsx(
583
+ "img",
584
+ {
585
+ src,
586
+ alt: filename ?? s.preview,
587
+ referrerPolicy: "no-referrer",
588
+ className: "h-auto w-full max-w-full object-contain",
589
+ onError: () => setBroken(true)
590
+ }
591
+ ) });
592
+ }
593
+ function PdfPreview({ src }) {
594
+ const s = useFieldStrings();
595
+ return /* @__PURE__ */ jsx(
596
+ "object",
597
+ {
598
+ data: `${src}#view=FitH`,
599
+ type: "application/pdf",
600
+ className: "h-[78vh] min-h-[520px] w-full rounded-md border border-border bg-muted",
601
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-28 flex-col items-center justify-center gap-1.5 text-center", children: [
602
+ /* @__PURE__ */ jsx(FileText, { className: "h-5 w-5 text-muted-foreground" }),
603
+ /* @__PURE__ */ jsx("p", { className: "text-[12px] text-muted-foreground", children: s.previewUnavailablePdf })
604
+ ] })
605
+ }
606
+ );
607
+ }
608
+ function fileChipSubtitle(url, filename, formats, presentation, strings) {
609
+ if (url === void 0) return void 0;
610
+ const summary = encodedFileSummary(url, strings);
611
+ if (summary !== void 0) return summary;
612
+ if (presentation === "studio" || isReadableLink(url)) return url;
613
+ return filename ? formatOfFilename(formats, filename)?.label : void 0;
614
+ }
615
+ function FileChip({
616
+ value,
617
+ formats,
618
+ presentation,
619
+ disabled,
620
+ onClear,
621
+ canPreview,
622
+ previewOpen,
623
+ onTogglePreview
624
+ }) {
625
+ const s = useFieldStrings();
626
+ const url = value?.url;
627
+ const filename = value?.filename;
628
+ const subtitle = useMemo(
629
+ () => fileChipSubtitle(url, filename, formats, presentation, s),
630
+ [url, filename, formats, presentation, s]
631
+ );
632
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-input px-3 py-2.5", children: [
633
+ /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }) }),
634
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
635
+ /* @__PURE__ */ jsx("p", { className: "truncate text-[13px] font-medium text-foreground", children: value?.filename ?? s.uploadedFile }),
636
+ subtitle && /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[10.5px] text-muted-foreground", children: subtitle })
637
+ ] }),
638
+ canPreview && /* @__PURE__ */ jsxs(
639
+ "button",
640
+ {
641
+ type: "button",
642
+ onClick: onTogglePreview,
643
+ className: "inline-flex h-7 shrink-0 items-center gap-1 rounded-md border border-border px-2 text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
644
+ children: [
645
+ previewOpen ? /* @__PURE__ */ jsx(EyeOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" }),
646
+ s.preview
647
+ ]
648
+ }
649
+ ),
650
+ /* @__PURE__ */ jsx(ClearButton, { disabled, onClick: onClear })
651
+ ] });
652
+ }
653
+ function ClearButton({ disabled, onClick }) {
654
+ const s = useFieldStrings();
655
+ return /* @__PURE__ */ jsx(
656
+ "button",
657
+ {
658
+ type: "button",
659
+ onClick,
660
+ disabled,
661
+ "aria-label": s.removeFileAria,
662
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50",
663
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
664
+ }
665
+ );
666
+ }
667
+ var Select = SelectPrimitive.Root;
668
+ var SelectValue = SelectPrimitive.Value;
669
+ var SelectTrigger = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
670
+ SelectPrimitive.Trigger,
671
+ {
672
+ ref,
673
+ className: cn(
674
+ // Adapted from the registry to DESIGN.md ("Inputs"): the `--input` surface,
675
+ // a hairline `--border`, and a border-color focus signal instead of the
676
+ // upstream 2px focus ring ("No focus glow by default").
677
+ "flex h-10 w-full items-center justify-between rounded-md border border-border bg-input px-3 py-2 text-sm transition-colors data-placeholder:text-muted-foreground focus:border-ring focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive [&>span]:line-clamp-1",
678
+ className
679
+ ),
680
+ ...props,
681
+ children: [
682
+ children,
683
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
684
+ ]
685
+ }
686
+ ));
687
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
688
+ var SelectScrollUpButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
689
+ SelectPrimitive.ScrollUpButton,
690
+ {
691
+ ref,
692
+ className: cn("flex cursor-default items-center justify-center py-1", className),
693
+ ...props,
694
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
695
+ }
696
+ ));
697
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
698
+ var SelectScrollDownButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
699
+ SelectPrimitive.ScrollDownButton,
700
+ {
701
+ ref,
702
+ className: cn("flex cursor-default items-center justify-center py-1", className),
703
+ ...props,
704
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
705
+ }
706
+ ));
707
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
708
+ var SelectContent = React2.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
709
+ SelectPrimitive.Content,
710
+ {
711
+ ref,
712
+ className: cn(
713
+ "relative z-50 max-h-(--radix-select-content-available-height) min-w-32 overflow-y-auto overflow-x-hidden rounded-md border border-border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-(--radix-select-content-transform-origin)",
714
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
715
+ className
716
+ ),
717
+ position,
718
+ ...props,
719
+ children: [
720
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
721
+ /* @__PURE__ */ jsx(
722
+ SelectPrimitive.Viewport,
723
+ {
724
+ className: cn(
725
+ "p-1",
726
+ position === "popper" && "h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width)"
727
+ ),
728
+ children
729
+ }
730
+ ),
731
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
732
+ ]
733
+ }
734
+ ) }));
735
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
736
+ var SelectLabel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
737
+ SelectPrimitive.Label,
738
+ {
739
+ ref,
740
+ className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
741
+ ...props
742
+ }
743
+ ));
744
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
745
+ var SelectItem = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
746
+ SelectPrimitive.Item,
747
+ {
748
+ ref,
749
+ className: cn(
750
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
751
+ className
752
+ ),
753
+ ...props,
754
+ children: [
755
+ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
756
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
757
+ ]
758
+ }
759
+ ));
760
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
761
+ var SelectSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
762
+ SelectPrimitive.Separator,
763
+ {
764
+ ref,
765
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
766
+ ...props
767
+ }
768
+ ));
769
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
770
+ var toggleVariants = cva(
771
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
772
+ {
773
+ variants: {
774
+ variant: {
775
+ default: "bg-transparent",
776
+ outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground"
777
+ },
778
+ size: {
779
+ default: "h-10 px-3 min-w-10",
780
+ sm: "h-9 px-2.5 min-w-9",
781
+ lg: "h-11 px-5 min-w-11"
782
+ }
783
+ },
784
+ defaultVariants: {
785
+ variant: "default",
786
+ size: "default"
787
+ }
788
+ }
789
+ );
790
+ var Toggle = React2.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
791
+ TogglePrimitive.Root,
792
+ {
793
+ ref,
794
+ className: cn(toggleVariants({ variant, size, className })),
795
+ ...props
796
+ }
797
+ ));
798
+ Toggle.displayName = TogglePrimitive.Root.displayName;
799
+ var ToggleGroupContext = React2.createContext({
800
+ size: "default",
801
+ variant: "default"
802
+ });
803
+ var ToggleGroup = React2.forwardRef(({ className, variant, size, children, ...props }, ref) => /* @__PURE__ */ jsx(
804
+ ToggleGroupPrimitive.Root,
805
+ {
806
+ ref,
807
+ className: cn("flex items-center justify-center gap-1", className),
808
+ ...props,
809
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
810
+ }
811
+ ));
812
+ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
813
+ var ToggleGroupItem = React2.forwardRef(({ className, children, variant, size, ...props }, ref) => {
814
+ const context = React2.useContext(ToggleGroupContext);
815
+ return /* @__PURE__ */ jsx(
816
+ ToggleGroupPrimitive.Item,
817
+ {
818
+ ref,
819
+ className: cn(
820
+ toggleVariants({
821
+ variant: context.variant || variant,
822
+ size: context.size || size
823
+ }),
824
+ className
825
+ ),
826
+ ...props,
827
+ children
828
+ }
829
+ );
830
+ });
831
+ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
832
+ var CLEAR_VALUE = "__none__";
833
+ function EnumField({ field, value, onChange, id, error, disabled }) {
834
+ const s = useFieldStrings();
835
+ const presentation = useFieldPresentation();
836
+ const labelOf = enumLabeler(field.options, presentation);
837
+ const isSegmented = field.options.length <= 4 && field.options.every((o) => labelOf(o).length <= 16);
838
+ const domId = useFieldDomId(id);
839
+ return /* @__PURE__ */ jsx(
840
+ FieldShell,
841
+ {
842
+ name: field.name,
843
+ title: field.title,
844
+ conceptRef: field.conceptRef,
845
+ category: "choice",
846
+ description: field.description,
847
+ required: field.required,
848
+ error,
849
+ htmlFor: isSegmented ? void 0 : domId,
850
+ children: isSegmented ? (
851
+ // `type="single"` gives the roving tabindex and arrow-key navigation a
852
+ // radiogroup owes its user, and emits `''` when the active item is
853
+ // pressed again - which is the field's "clear" path.
854
+ /* @__PURE__ */ jsx(
855
+ ToggleGroup,
856
+ {
857
+ type: "single",
858
+ value: value ?? "",
859
+ onValueChange: (next) => onChange(next === "" ? void 0 : next),
860
+ disabled,
861
+ "aria-label": fieldLabel(field.title, field.name, presentation),
862
+ className: "flex flex-wrap justify-start gap-1.5",
863
+ children: field.options.map((option) => /* @__PURE__ */ jsxs(
864
+ ToggleGroupItem,
865
+ {
866
+ value: option,
867
+ variant: "outline",
868
+ className: "h-9 min-w-0 gap-1.5 border-border bg-input px-3 text-[13px] text-muted-foreground hover:bg-muted hover:text-foreground data-[state=on]:border-primary/40 data-[state=on]:bg-primary/10 data-[state=on]:font-medium data-[state=on]:text-foreground",
869
+ children: [
870
+ value === option && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary", strokeWidth: 2.5, "aria-hidden": "true" }),
871
+ labelOf(option)
872
+ ]
873
+ },
874
+ option
875
+ ))
876
+ }
877
+ )
878
+ ) : /* @__PURE__ */ jsxs(
879
+ Select,
880
+ {
881
+ value: value ?? "",
882
+ disabled,
883
+ onValueChange: (next) => onChange(next === CLEAR_VALUE ? void 0 : next),
884
+ children: [
885
+ /* @__PURE__ */ jsx(SelectTrigger, { id: domId, "aria-invalid": !!error, className: "h-11", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: s.selectPlaceholder }) }),
886
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
887
+ /* @__PURE__ */ jsx(SelectItem, { value: CLEAR_VALUE, children: s.selectPlaceholder }),
888
+ field.options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: labelOf(option) }, option))
889
+ ] })
890
+ ]
891
+ }
892
+ )
893
+ }
894
+ );
895
+ }
896
+ function NumberField({ field, value, onChange, id, error, disabled }) {
897
+ const s = useFieldStrings();
898
+ const domId = useFieldDomId(id);
899
+ const step = field.integer ? 1 : 0.1;
900
+ const clamp = (n) => {
901
+ let next = n;
902
+ if (typeof field.min === "number") next = Math.max(field.min, next);
903
+ if (typeof field.max === "number") next = Math.min(field.max, next);
904
+ return field.integer ? Math.round(next) : next;
905
+ };
906
+ const nudge = (delta) => onChange(clamp((value ?? 0) + delta));
907
+ const rangeHint = field.min !== void 0 || field.max !== void 0 ? `${field.min ?? "\u2212\u221E"} \u2013 ${field.max ?? "\u221E"}` : void 0;
908
+ return /* @__PURE__ */ jsx(
909
+ FieldShell,
910
+ {
911
+ name: field.name,
912
+ title: field.title,
913
+ conceptRef: field.conceptRef,
914
+ category: "number",
915
+ description: field.description ?? rangeHint,
916
+ required: field.required,
917
+ error,
918
+ htmlFor: domId,
919
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-1.5", children: [
920
+ /* @__PURE__ */ jsx(
921
+ "input",
922
+ {
923
+ id: domId,
924
+ type: "number",
925
+ inputMode: field.integer ? "numeric" : "decimal",
926
+ value: value ?? "",
927
+ step,
928
+ min: field.min,
929
+ max: field.max,
930
+ disabled,
931
+ "aria-invalid": !!error,
932
+ placeholder: "0",
933
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value)),
934
+ className: cn(
935
+ fieldControlClass,
936
+ "h-11 px-3 font-mono tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
937
+ )
938
+ }
939
+ ),
940
+ /* @__PURE__ */ jsx(StepButton, { onClick: () => nudge(-step), disabled, label: s.decrease, children: /* @__PURE__ */ jsx(Minus, { className: "h-4 w-4" }) }),
941
+ /* @__PURE__ */ jsx(StepButton, { onClick: () => nudge(step), disabled, label: s.increase, children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }) })
942
+ ] })
943
+ }
944
+ );
945
+ }
946
+ function StepButton({
947
+ onClick,
948
+ disabled,
949
+ label,
950
+ children
951
+ }) {
952
+ return /* @__PURE__ */ jsx(
953
+ "button",
954
+ {
955
+ type: "button",
956
+ onClick,
957
+ disabled,
958
+ "aria-label": label,
959
+ className: cn(
960
+ "flex h-11 w-11 shrink-0 items-center justify-center rounded-md border border-border bg-input text-muted-foreground transition-colors",
961
+ "hover:border-border hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
962
+ ),
963
+ children
964
+ }
965
+ );
966
+ }
967
+ function TextField({ field, value, onChange, id, error, disabled }) {
968
+ const s = useFieldStrings();
969
+ const domId = useFieldDomId(id);
970
+ return /* @__PURE__ */ jsx(
971
+ FieldShell,
972
+ {
973
+ name: field.name,
974
+ title: field.title,
975
+ conceptRef: field.conceptRef,
976
+ category: "text",
977
+ description: field.description,
978
+ required: field.required,
979
+ error,
980
+ htmlFor: domId,
981
+ children: /* @__PURE__ */ jsx(
982
+ "input",
983
+ {
984
+ id: domId,
985
+ type: "text",
986
+ value: value ?? "",
987
+ disabled,
988
+ "aria-invalid": !!error,
989
+ placeholder: field.placeholder ?? s.typeValuePlaceholder,
990
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
991
+ className: cn(fieldControlClass, "h-11 px-3")
992
+ }
993
+ )
994
+ }
995
+ );
996
+ }
997
+ function ProseField({ field, value, onChange, id, error, disabled }) {
998
+ const s = useFieldStrings();
999
+ const domId = useFieldDomId(id);
1000
+ return /* @__PURE__ */ jsx(
1001
+ FieldShell,
1002
+ {
1003
+ name: field.name,
1004
+ title: field.title,
1005
+ conceptRef: field.conceptRef,
1006
+ category: "text",
1007
+ description: field.description,
1008
+ required: field.required,
1009
+ error,
1010
+ htmlFor: domId,
1011
+ children: /* @__PURE__ */ jsx(
1012
+ "textarea",
1013
+ {
1014
+ id: domId,
1015
+ value: value ?? "",
1016
+ disabled,
1017
+ "aria-invalid": !!error,
1018
+ rows: 3,
1019
+ placeholder: field.placeholder ?? s.writeHerePlaceholder,
1020
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
1021
+ className: cn(
1022
+ fieldControlClass,
1023
+ "min-h-[88px] resize-y px-3 py-2.5 leading-relaxed field-sizing-content"
1024
+ )
1025
+ }
1026
+ )
1027
+ }
1028
+ );
1029
+ }
1030
+ function OptionalToggle({ count, expanded, onToggle, noun = "field" }) {
1031
+ const s = useFieldStrings();
1032
+ return /* @__PURE__ */ jsxs(
1033
+ "button",
1034
+ {
1035
+ type: "button",
1036
+ onClick: onToggle,
1037
+ className: "flex w-fit items-center gap-1.5 text-[11px] text-muted-foreground transition-colors hover:text-foreground",
1038
+ children: [
1039
+ expanded ? /* @__PURE__ */ jsx(Minus, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx(Plus, { className: "h-3 w-3" }),
1040
+ expanded ? noun === "field" ? s.hideOptionalFields : s.hideOptionalInputs : noun === "field" ? s.optionalFieldsCount(count) : s.optionalInputsCount(count)
1041
+ ]
1042
+ }
1043
+ );
1044
+ }
1045
+ function ObjectField({ field, value, onChange, id, error, env }) {
1046
+ const s = useFieldStrings();
1047
+ const [showOptional, setShowOptional] = useState(false);
1048
+ const presentation = useFieldPresentation();
1049
+ const isApp = presentation === "app";
1050
+ const data = value ?? {};
1051
+ const setChild = (name, childValue) => onChange({ ...data, [name]: childValue });
1052
+ const optionalEmpty = field.fields.filter((f) => !f.required && !isFilled(ownProp(data, f.name)));
1053
+ const visible = showOptional ? field.fields : field.fields.filter((f) => f.required || isFilled(ownProp(data, f.name)));
1054
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1055
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
1056
+ /* @__PURE__ */ jsx(
1057
+ "span",
1058
+ {
1059
+ className: cn(
1060
+ "text-[13px] font-medium leading-none text-foreground",
1061
+ isApp ? "text-[13.5px]" : "font-mono"
1062
+ ),
1063
+ children: fieldLabel(field.title, field.name, presentation)
1064
+ }
1065
+ ),
1066
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" }),
1067
+ !field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
1068
+ ] }),
1069
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
1070
+ /* @__PURE__ */ jsxs(
1071
+ "div",
1072
+ {
1073
+ className: cn(
1074
+ "space-y-4 rounded-lg border bg-card/40 p-4",
1075
+ error ? "border-destructive/50" : "border-border"
1076
+ ),
1077
+ children: [
1078
+ visible.map((child) => /* @__PURE__ */ jsx(
1079
+ FieldRenderer,
1080
+ {
1081
+ field: child,
1082
+ value: ownProp(data, child.name),
1083
+ onChange: (v) => setChild(child.name, v),
1084
+ id: `${id}.${child.name}`,
1085
+ env
1086
+ },
1087
+ child.name
1088
+ )),
1089
+ optionalEmpty.length > 0 && /* @__PURE__ */ jsx(
1090
+ OptionalToggle,
1091
+ {
1092
+ count: optionalEmpty.length,
1093
+ expanded: showOptional,
1094
+ onToggle: () => setShowOptional((v) => !v),
1095
+ noun: "field"
1096
+ }
1097
+ )
1098
+ ]
1099
+ }
1100
+ ),
1101
+ error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
1102
+ ] });
1103
+ }
1104
+ function ListField({ field, value, onChange, id, error, env }) {
1105
+ const s = useFieldStrings();
1106
+ const presentation = useFieldPresentation();
1107
+ const isApp = presentation === "app";
1108
+ const items = value ?? [];
1109
+ const [rowKeys, setRowKeys] = useRowKeys(items.length);
1110
+ const setItem = (index, itemValue) => {
1111
+ const next = items.slice();
1112
+ next[index] = itemValue;
1113
+ onChange(next);
1114
+ };
1115
+ const removeItem = (index) => {
1116
+ setRowKeys((prev) => prev.filter((_, i) => i !== index));
1117
+ onChange(items.filter((_, i) => i !== index));
1118
+ };
1119
+ const addItem = () => onChange([...items, emptyValue(field.item)]);
1120
+ const busy = listIsBusy(id, env?.uploadingIds);
1121
+ const isFull = field.maxItemCount !== void 0 && items.length >= field.maxItemCount;
1122
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1123
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
1124
+ /* @__PURE__ */ jsx(
1125
+ "span",
1126
+ {
1127
+ className: cn(
1128
+ "text-[13px] font-medium leading-none text-foreground",
1129
+ isApp ? "text-[13.5px]" : "font-mono"
1130
+ ),
1131
+ children: fieldLabel(field.title, field.name, presentation)
1132
+ }
1133
+ ),
1134
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "list" }),
1135
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: field.itemCount !== void 0 && field.maxItemCount === field.itemCount ? s.itemsCountOf(items.length, field.itemCount) : s.itemsCount(items.length) }),
1136
+ !field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
1137
+ ] }),
1138
+ field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
1139
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1140
+ items.length === 0 && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
1141
+ items.map((item, index) => /* @__PURE__ */ jsxs(
1142
+ "div",
1143
+ {
1144
+ className: "flex items-start gap-2 rounded-md border border-border bg-card/40 p-3",
1145
+ children: [
1146
+ /* @__PURE__ */ jsx("span", { className: "mt-1 w-5 shrink-0 text-center font-mono text-[11px] text-muted-foreground", children: index + 1 }),
1147
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
1148
+ FieldRenderer,
1149
+ {
1150
+ field: { ...field.item, title: "", name: `${field.name}[${index}]` },
1151
+ value: item,
1152
+ onChange: (v) => setItem(index, v),
1153
+ id: `${id}.${index}`,
1154
+ env
1155
+ }
1156
+ ) }),
1157
+ /* @__PURE__ */ jsx(
1158
+ "button",
1159
+ {
1160
+ type: "button",
1161
+ onClick: () => removeItem(index),
1162
+ disabled: env?.disabled || busy,
1163
+ "aria-label": s.removeItemAria(index + 1),
1164
+ className: "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-destructive disabled:opacity-50",
1165
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
1166
+ }
1167
+ )
1168
+ ]
1169
+ },
1170
+ rowKeys[index] ?? index
1171
+ )),
1172
+ /* @__PURE__ */ jsxs(
1173
+ "button",
1174
+ {
1175
+ type: "button",
1176
+ onClick: addItem,
1177
+ disabled: env?.disabled || isFull,
1178
+ className: cn(
1179
+ "inline-flex h-9 w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-border text-[13px] text-muted-foreground transition-colors",
1180
+ "hover:border-primary/40 hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50"
1181
+ ),
1182
+ children: [
1183
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
1184
+ s.addItem
1185
+ ]
1186
+ }
1187
+ )
1188
+ ] }),
1189
+ error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
1190
+ ] });
1191
+ }
1192
+ function listIsBusy(id, uploadingIds) {
1193
+ if (!uploadingIds?.size) return false;
1194
+ const prefix = `${id}.`;
1195
+ for (const uploading of uploadingIds) if (uploading.startsWith(prefix)) return true;
1196
+ return false;
1197
+ }
1198
+ function useRowKeys(length) {
1199
+ const [state, setState] = useState(() => ({
1200
+ keys: Array.from({ length }, (_, i) => i),
1201
+ next: length
1202
+ }));
1203
+ if (state.keys.length !== length) {
1204
+ setState((prev) => {
1205
+ if (length <= prev.keys.length) return { keys: prev.keys.slice(0, length), next: prev.next };
1206
+ const added = length - prev.keys.length;
1207
+ return {
1208
+ keys: [...prev.keys, ...Array.from({ length: added }, (_, i) => prev.next + i)],
1209
+ next: prev.next + added
1210
+ };
1211
+ });
1212
+ }
1213
+ const update = (fn) => setState((prev) => ({ keys: fn(prev.keys), next: prev.next }));
1214
+ return [state.keys, update];
1215
+ }
1216
+ function emptyValue(field) {
1217
+ switch (field.kind) {
1218
+ case "boolean":
1219
+ return false;
1220
+ case "object":
1221
+ return {};
1222
+ case "list":
1223
+ return [];
1224
+ case "document":
1225
+ case "image":
1226
+ return void 0;
1227
+ default:
1228
+ return void 0;
1229
+ }
1230
+ }
1231
+ function UnknownField({ field, value, onChange, id, error, disabled }) {
1232
+ const s = useFieldStrings();
1233
+ const domId = useFieldDomId(id);
1234
+ return /* @__PURE__ */ jsx(
1235
+ FieldShell,
1236
+ {
1237
+ name: field.name,
1238
+ title: field.title,
1239
+ conceptRef: field.conceptRef,
1240
+ category: "structured",
1241
+ description: field.description ?? s.jsonHint,
1242
+ required: field.required,
1243
+ error,
1244
+ htmlFor: domId,
1245
+ children: /* @__PURE__ */ jsx(
1246
+ "textarea",
1247
+ {
1248
+ id: domId,
1249
+ value: value ?? "",
1250
+ disabled,
1251
+ "aria-invalid": !!error,
1252
+ rows: 3,
1253
+ spellCheck: false,
1254
+ placeholder: '{\n "key": "value"\n}',
1255
+ onChange: (e) => onChange(e.target.value === "" ? void 0 : e.target.value),
1256
+ className: cn(fieldControlClass, "min-h-[80px] resize-y px-3 py-2.5 font-mono text-[12px]")
1257
+ }
1258
+ )
1259
+ }
1260
+ );
1261
+ }
1262
+ function FieldRenderer({ field, value, onChange, id, error, env }) {
1263
+ const disabled = env?.disabled;
1264
+ switch (field.kind) {
1265
+ case "text":
1266
+ return /* @__PURE__ */ jsx(
1267
+ TextField,
1268
+ {
1269
+ field,
1270
+ value,
1271
+ onChange,
1272
+ id,
1273
+ error,
1274
+ disabled
1275
+ }
1276
+ );
1277
+ case "prose":
1278
+ return /* @__PURE__ */ jsx(
1279
+ ProseField,
1280
+ {
1281
+ field,
1282
+ value,
1283
+ onChange,
1284
+ id,
1285
+ error,
1286
+ disabled
1287
+ }
1288
+ );
1289
+ case "date":
1290
+ return /* @__PURE__ */ jsx(
1291
+ DateField,
1292
+ {
1293
+ field,
1294
+ value,
1295
+ onChange,
1296
+ id,
1297
+ error,
1298
+ disabled
1299
+ }
1300
+ );
1301
+ case "number":
1302
+ return /* @__PURE__ */ jsx(
1303
+ NumberField,
1304
+ {
1305
+ field,
1306
+ value,
1307
+ onChange,
1308
+ id,
1309
+ error,
1310
+ disabled
1311
+ }
1312
+ );
1313
+ case "boolean":
1314
+ return /* @__PURE__ */ jsx(
1315
+ BooleanField,
1316
+ {
1317
+ field,
1318
+ value,
1319
+ onChange,
1320
+ id,
1321
+ disabled
1322
+ }
1323
+ );
1324
+ case "enum":
1325
+ return /* @__PURE__ */ jsx(
1326
+ EnumField,
1327
+ {
1328
+ field,
1329
+ value,
1330
+ onChange,
1331
+ id,
1332
+ error,
1333
+ disabled
1334
+ }
1335
+ );
1336
+ case "document":
1337
+ return /* @__PURE__ */ jsx(
1338
+ DocumentField,
1339
+ {
1340
+ field,
1341
+ value,
1342
+ onChange,
1343
+ onDropFile: (file) => env?.onDropFile?.(id, file),
1344
+ uploading: env?.uploadingIds?.has(id),
1345
+ resolveUrl: env?.resolveUrl,
1346
+ id,
1347
+ error,
1348
+ disabled
1349
+ }
1350
+ );
1351
+ case "image":
1352
+ return /* @__PURE__ */ jsx(
1353
+ ImageField,
1354
+ {
1355
+ field,
1356
+ value,
1357
+ onChange,
1358
+ onDropFile: (file) => env?.onDropFile?.(id, file),
1359
+ uploading: env?.uploadingIds?.has(id),
1360
+ resolveUrl: env?.resolveUrl,
1361
+ id,
1362
+ error,
1363
+ disabled
1364
+ }
1365
+ );
1366
+ case "object":
1367
+ return /* @__PURE__ */ jsx(
1368
+ ObjectField,
1369
+ {
1370
+ field,
1371
+ value,
1372
+ onChange,
1373
+ id,
1374
+ error,
1375
+ env
1376
+ }
1377
+ );
1378
+ case "list":
1379
+ return /* @__PURE__ */ jsx(
1380
+ ListField,
1381
+ {
1382
+ field,
1383
+ value,
1384
+ onChange,
1385
+ id,
1386
+ error,
1387
+ env
1388
+ }
1389
+ );
1390
+ default:
1391
+ return /* @__PURE__ */ jsx(
1392
+ UnknownField,
1393
+ {
1394
+ field,
1395
+ value,
1396
+ onChange,
1397
+ id,
1398
+ error,
1399
+ disabled
1400
+ }
1401
+ );
1402
+ }
1403
+ }
1404
+ var ResultEnvContext = createContext({});
1405
+ function ResultEnvProvider({
1406
+ resolveUrl,
1407
+ resolveShareUrl,
1408
+ proseImages,
1409
+ tableColumns,
1410
+ children
1411
+ }) {
1412
+ const env = useMemo(
1413
+ () => ({ resolveUrl, resolveShareUrl, proseImages, tableColumns }),
1414
+ [resolveUrl, resolveShareUrl, proseImages, tableColumns]
1415
+ );
1416
+ return /* @__PURE__ */ jsx(ResultEnvContext, { value: env, children });
1417
+ }
1418
+ function useResolvedUrl(url) {
1419
+ return use(ResultEnvContext).resolveUrl?.(url) ?? url;
1420
+ }
1421
+ function useResolveUrl() {
1422
+ return use(ResultEnvContext).resolveUrl;
1423
+ }
1424
+ function useResolveShareUrl() {
1425
+ return use(ResultEnvContext).resolveShareUrl;
1426
+ }
1427
+ function useProseImages() {
1428
+ return use(ResultEnvContext).proseImages ?? "link";
1429
+ }
1430
+ var DEFAULT_TABLE_COLUMNS = 5;
1431
+ function useTableColumns() {
1432
+ const budget = use(ResultEnvContext).tableColumns;
1433
+ if (budget === void 0 || Number.isNaN(budget)) return DEFAULT_TABLE_COLUMNS;
1434
+ return Math.max(1, Math.floor(budget));
1435
+ }
1436
+ var TooltipProvider = TooltipPrimitive.Provider;
1437
+ var TooltipRoot = TooltipPrimitive.Root;
1438
+ var TooltipTrigger = TooltipPrimitive.Trigger;
1439
+ var TooltipContent = React2.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
1440
+ TooltipPrimitive.Content,
1441
+ {
1442
+ ref,
1443
+ sideOffset,
1444
+ className: cn(
1445
+ "z-50 max-w-xs rounded-md border border-border bg-popover px-2.5 py-1.5 text-[12px] leading-relaxed text-popover-foreground shadow-md",
1446
+ "animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
1447
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
1448
+ className
1449
+ ),
1450
+ ...props
1451
+ }
1452
+ ) }));
1453
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
1454
+ var DEFAULT_IMG_SRC = "data: https:";
1455
+ function frameStyles(color, mutedColor, borderColor, font) {
1456
+ return `
1457
+ :root { color-scheme: inherit; }
1458
+ body {
1459
+ margin: 0;
1460
+ color: ${color};
1461
+ font-family: ${font};
1462
+ font-size: 13px;
1463
+ line-height: 1.55;
1464
+ background: transparent;
1465
+ overflow-wrap: anywhere;
1466
+ }
1467
+ :where(h1, h2, h3, h4, h5, h6) { margin: 0 0 0.4em; line-height: 1.25; }
1468
+ :where(h1) { font-size: 1.4em; }
1469
+ :where(h2) { font-size: 1.2em; }
1470
+ :where(h3) { font-size: 1.05em; }
1471
+ :where(p, ul, ol, table, pre, blockquote) { margin: 0 0 0.75em; }
1472
+ :where(ul, ol) { padding-inline-start: 1.25em; }
1473
+ :where(table) { border-collapse: collapse; width: 100%; }
1474
+ :where(th, td) {
1475
+ border: 1px solid ${borderColor};
1476
+ padding: 4px 8px;
1477
+ text-align: left;
1478
+ vertical-align: top;
1479
+ }
1480
+ :where(th) { font-weight: 600; }
1481
+ :where(caption) { caption-side: top; color: ${mutedColor}; padding-bottom: 4px; text-align: left; }
1482
+ :where(a) { color: inherit; }
1483
+ :where(code, pre) { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
1484
+ :where(pre) { white-space: pre-wrap; }
1485
+ :where(blockquote) { border-inline-start: 2px solid ${borderColor}; margin-inline-start: 0; padding-inline-start: 0.75em; color: ${mutedColor}; }
1486
+ :where(img) { max-width: 100%; height: auto; }
1487
+ :where(hr) { border: 0; border-top: 1px solid ${borderColor}; }
1488
+ :where(*:last-child) { margin-bottom: 0; }
1489
+ `;
1490
+ }
1491
+ function frameDocument(content, styles, imgSrc) {
1492
+ const body = content.cssClass ? (
1493
+ // The class the value states, honoured the way the runtime's own HTML
1494
+ // rendering honours it: as a wrapper, not as something merged into ours.
1495
+ `<div class="${escapeAttribute(content.cssClass)}">${content.innerHtml}</div>`
1496
+ ) : content.innerHtml;
1497
+ return [
1498
+ '<!doctype html><html><head><meta charset="utf-8">',
1499
+ `<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src ${imgSrc}; base-uri 'none'; form-action 'none'">`,
1500
+ `<style>${styles}</style></head><body>${body}</body></html>`
1501
+ ].join("");
1502
+ }
1503
+ function escapeAttribute(value) {
1504
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
1505
+ }
1506
+ var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
1507
+ function HtmlPreview({
1508
+ content,
1509
+ maxHeight = 1400,
1510
+ imgSrc = DEFAULT_IMG_SRC
1511
+ }) {
1512
+ const hostRef = useRef(null);
1513
+ const frameRef = useRef(null);
1514
+ const [doc, setDoc] = useState(null);
1515
+ const [height, setHeight] = useState(0);
1516
+ useIsomorphicLayoutEffect(() => {
1517
+ const host = hostRef.current;
1518
+ if (!host) return;
1519
+ const computed = getComputedStyle(host);
1520
+ setDoc(
1521
+ frameDocument(
1522
+ content,
1523
+ frameStyles(
1524
+ computed.color,
1525
+ computed.getPropertyValue("--muted-foreground").trim() || computed.color,
1526
+ computed.borderColor || computed.color,
1527
+ computed.fontFamily
1528
+ ),
1529
+ imgSrc
1530
+ )
1531
+ );
1532
+ }, [content, imgSrc]);
1533
+ const measure = () => {
1534
+ const body = frameRef.current?.contentDocument?.body;
1535
+ if (body) setHeight(body.scrollHeight);
1536
+ };
1537
+ useEffect(() => {
1538
+ if (!doc || !hostRef.current || typeof ResizeObserver === "undefined") return;
1539
+ const observer = new ResizeObserver(measure);
1540
+ observer.observe(hostRef.current);
1541
+ return () => observer.disconnect();
1542
+ }, [doc]);
1543
+ return /* @__PURE__ */ jsx(
1544
+ "div",
1545
+ {
1546
+ ref: hostRef,
1547
+ className: "overflow-hidden rounded-lg border border-border bg-card/40 px-3.5 py-3 text-[13px] text-foreground",
1548
+ children: doc === null ? null : /* @__PURE__ */ jsx(
1549
+ "iframe",
1550
+ {
1551
+ ref: frameRef,
1552
+ title: content.cssClass ? `HTML result (${content.cssClass})` : "HTML result",
1553
+ srcDoc: doc,
1554
+ onLoad: measure,
1555
+ sandbox: "allow-same-origin",
1556
+ style: {
1557
+ display: "block",
1558
+ width: "100%",
1559
+ border: 0,
1560
+ height: height === 0 ? void 0 : Math.min(height, maxHeight),
1561
+ maxHeight
1562
+ }
1563
+ }
1564
+ )
1565
+ }
1566
+ );
1567
+ }
1568
+ var LEX_OPTIONS = { gfm: true, breaks: true };
1569
+ function Markdown({ text, className, proseImages }) {
1570
+ const tokens = React2.useMemo(() => marked.lexer(text, LEX_OPTIONS), [text]);
1571
+ const fromEnv = useProseImages();
1572
+ const mode = proseImages ?? fromEnv;
1573
+ return /* @__PURE__ */ jsx(ProseImagesContext, { value: mode, children: /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) }) });
1574
+ }
1575
+ var ProseImagesContext = React2.createContext("link");
1576
+ var HEADING_CLASS = {
1577
+ 1: "text-[16px] font-semibold",
1578
+ 2: "text-[14.5px] font-semibold",
1579
+ 3: "text-[13.5px] font-semibold",
1580
+ 4: "text-[13px] font-semibold",
1581
+ 5: "text-[13px] font-semibold",
1582
+ 6: "text-[13px] font-semibold"
1583
+ };
1584
+ function Blocks({ tokens }) {
1585
+ return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(Block, { token }, index)) });
1586
+ }
1587
+ function Block({ token }) {
1588
+ switch (token.type) {
1589
+ case "space":
1590
+ return null;
1591
+ case "heading": {
1592
+ const heading = token;
1593
+ const level = Math.min(Math.max(heading.depth, 1), 6);
1594
+ const Tag = `h${level}`;
1595
+ return /* @__PURE__ */ jsx(Tag, { className: cn("mt-3 first:mt-0", HEADING_CLASS[level]), children: /* @__PURE__ */ jsx(Inline, { tokens: heading.tokens }) });
1596
+ }
1597
+ case "paragraph":
1598
+ return /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
1599
+ // A `text` token in BLOCK position, which is what a tight list's items are
1600
+ // made of: the lexer wraps a loose item's content in a paragraph and a
1601
+ // tight one's in a bare `text`. Without this arm it fell through to the raw
1602
+ // fallback below and a bulleted `**Type:** …` rendered with its asterisks
1603
+ // showing - typeset everywhere except inside a list, which is where model
1604
+ // output puts most of its emphasis.
1605
+ case "text":
1606
+ return /* @__PURE__ */ jsx(Inline, { tokens: token.tokens ?? [token] });
1607
+ case "list": {
1608
+ const list = token;
1609
+ const Tag = list.ordered ? "ol" : "ul";
1610
+ return /* @__PURE__ */ jsx(
1611
+ Tag,
1612
+ {
1613
+ className: cn("space-y-1 ps-5", list.ordered ? "list-decimal" : "list-disc"),
1614
+ ...list.ordered && list.start !== 1 ? { start: Number(list.start) } : {},
1615
+ children: list.items.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "marker:text-muted-foreground", children: [
1616
+ item.task ? /* @__PURE__ */ jsx(
1617
+ "input",
1618
+ {
1619
+ type: "checkbox",
1620
+ checked: item.checked ?? false,
1621
+ disabled: true,
1622
+ readOnly: true,
1623
+ className: "me-1.5 align-middle"
1624
+ }
1625
+ ) : null,
1626
+ /* @__PURE__ */ jsx(Blocks, { tokens: item.tokens })
1627
+ ] }, index))
1628
+ }
1629
+ );
1630
+ }
1631
+ case "code": {
1632
+ const code = token;
1633
+ return (
1634
+ // WRAPPED, not scrolled. A code block in a side panel is usually not
1635
+ // code at all: it is a model that opened a fence and did not close it,
1636
+ // or closed one it never opened, and the rest of a report ends up
1637
+ // inside it. Scrolling that sideways hides the text behind a scrollbar
1638
+ // for no gain — the lines are prose, and prose wraps. Real code loses
1639
+ // its column alignment and keeps every character, which is the better
1640
+ // half of that trade at this width.
1641
+ /* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap wrap-break-word rounded-md border border-border bg-card/40 p-2.5 text-[12px]", children: /* @__PURE__ */ jsx("code", { children: code.text }) })
1642
+ );
1643
+ }
1644
+ case "blockquote":
1645
+ return /* @__PURE__ */ jsx("blockquote", { className: "border-s-2 border-border ps-3 text-muted-foreground", children: /* @__PURE__ */ jsx(Blocks, { tokens: token.tokens }) });
1646
+ case "hr":
1647
+ return /* @__PURE__ */ jsx("hr", { className: "border-border" });
1648
+ case "table": {
1649
+ const table = token;
1650
+ return (
1651
+ // The same overflow rule the result tables follow: a wide table scrolls
1652
+ // inside its own box rather than widening the panel around it.
1653
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto rounded-lg border border-border", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full border-collapse text-[12.5px]", children: [
1654
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-card/40", children: table.header.map((cell, index) => /* @__PURE__ */ jsx(
1655
+ "th",
1656
+ {
1657
+ scope: "col",
1658
+ className: "px-2.5 py-1.5 text-start font-semibold",
1659
+ style: alignOf(table.align[index]),
1660
+ children: /* @__PURE__ */ jsx(Inline, { tokens: cell.tokens })
1661
+ },
1662
+ index
1663
+ )) }) }),
1664
+ /* @__PURE__ */ jsx("tbody", { children: table.rows.map((row, rowIndex) => /* @__PURE__ */ jsx("tr", { className: "border-b border-border/60 last:border-b-0", children: row.map((cell, cellIndex) => /* @__PURE__ */ jsx(
1665
+ "td",
1666
+ {
1667
+ className: "px-2.5 py-1.5 align-top",
1668
+ style: alignOf(table.align[cellIndex]),
1669
+ children: /* @__PURE__ */ jsx(Inline, { tokens: cell.tokens })
1670
+ },
1671
+ cellIndex
1672
+ )) }, rowIndex)) })
1673
+ ] }) })
1674
+ );
1675
+ }
1676
+ // `html` and anything this version of the lexer grows that we have no arm
1677
+ // for: shown as its own source text. Rendering markup we have decided not to
1678
+ // execute as the characters it is made of is the honest fallback - it never
1679
+ // silently drops content, and it never injects any.
1680
+ default:
1681
+ return /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap", children: rawOf(token) });
1682
+ }
1683
+ }
1684
+ function Inline({ tokens }) {
1685
+ if (!tokens) return null;
1686
+ return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(InlineToken, { token }, index)) });
1687
+ }
1688
+ function InlineToken({ token }) {
1689
+ switch (token.type) {
1690
+ case "text": {
1691
+ const text = token;
1692
+ return text.tokens ? /* @__PURE__ */ jsx(Inline, { tokens: text.tokens }) : /* @__PURE__ */ jsx(Fragment, { children: decode(text.text) });
1693
+ }
1694
+ case "escape":
1695
+ return /* @__PURE__ */ jsx(Fragment, { children: token.text });
1696
+ case "strong":
1697
+ return /* @__PURE__ */ jsx("strong", { className: "font-semibold", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
1698
+ case "em":
1699
+ return /* @__PURE__ */ jsx("em", { className: "italic", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
1700
+ case "del":
1701
+ return /* @__PURE__ */ jsx("del", { className: "line-through", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
1702
+ case "codespan":
1703
+ return /* @__PURE__ */ jsx("code", { className: "rounded border border-border bg-card/40 px-1 py-0.5 text-[0.92em]", children: decode(token.text) });
1704
+ case "br":
1705
+ return /* @__PURE__ */ jsx("br", {});
1706
+ case "link": {
1707
+ const link = token;
1708
+ const href = safeHref(link.href);
1709
+ if (!href) return /* @__PURE__ */ jsx(Inline, { tokens: link.tokens });
1710
+ return (
1711
+ // The flag is set for everything under the anchor, so a nested `image`
1712
+ // token knows not to open one of its own — see {@link ProseImage}.
1713
+ /* @__PURE__ */ jsx(InsideLinkContext, { value: true, children: /* @__PURE__ */ jsx(
1714
+ "a",
1715
+ {
1716
+ href,
1717
+ target: "_blank",
1718
+ rel: "noreferrer noopener",
1719
+ className: "underline underline-offset-2",
1720
+ children: /* @__PURE__ */ jsx(Inline, { tokens: link.tokens })
1721
+ }
1722
+ ) })
1723
+ );
1724
+ }
1725
+ case "image":
1726
+ return /* @__PURE__ */ jsx(ProseImage, { token });
1727
+ default:
1728
+ return /* @__PURE__ */ jsx(Fragment, { children: rawOf(token) });
1729
+ }
1730
+ }
1731
+ function ProseImage({ token }) {
1732
+ const mode = React2.use(ProseImagesContext);
1733
+ const insideLink = React2.use(InsideLinkContext);
1734
+ const src = proseImageUrl(token.href);
1735
+ if (!src) return /* @__PURE__ */ jsx(Fragment, { children: token.text });
1736
+ if (mode === "load") {
1737
+ return /* @__PURE__ */ jsx(
1738
+ "img",
1739
+ {
1740
+ src,
1741
+ alt: token.text,
1742
+ referrerPolicy: "no-referrer",
1743
+ className: "max-w-full rounded-md"
1744
+ }
1745
+ );
1746
+ }
1747
+ if (insideLink) return /* @__PURE__ */ jsx(Fragment, { children: token.text || src });
1748
+ return /* @__PURE__ */ jsx(
1749
+ "a",
1750
+ {
1751
+ href: src,
1752
+ target: "_blank",
1753
+ rel: "noreferrer noopener",
1754
+ className: "underline underline-offset-2",
1755
+ children: token.text || src
1756
+ }
1757
+ );
1758
+ }
1759
+ var InsideLinkContext = React2.createContext(false);
1760
+ function proseImageUrl(href) {
1761
+ const url = viewableUrl(href);
1762
+ return url && /^https?:/.test(url) ? url : void 0;
1763
+ }
1764
+ function safeHref(href) {
1765
+ const trimmed = href.trim();
1766
+ if (/^(https?:|mailto:)/i.test(trimmed)) return trimmed;
1767
+ if (trimmed.startsWith("#")) return trimmed;
1768
+ if (trimmed.startsWith("/")) return viewableUrl(trimmed);
1769
+ return void 0;
1770
+ }
1771
+ function alignOf(align) {
1772
+ return align ? { textAlign: align } : void 0;
1773
+ }
1774
+ function rawOf(token) {
1775
+ return "raw" in token && typeof token.raw === "string" ? token.raw : "";
1776
+ }
1777
+ var ENTITIES = {
1778
+ "&amp;": "&",
1779
+ "&lt;": "<",
1780
+ "&gt;": ">",
1781
+ "&quot;": '"',
1782
+ "&#39;": "'",
1783
+ "&#x27;": "'"
1784
+ };
1785
+ function decode(text) {
1786
+ return text.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g, (entity) => ENTITIES[entity] ?? entity);
1787
+ }
1788
+ function unwrap(field, value) {
1789
+ if (!field.contentKey) return value;
1790
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
1791
+ const unwrapped = ownProp(value, field.contentKey);
1792
+ return unwrapped === void 0 ? value : unwrapped;
1793
+ }
1794
+ function Described({ description, children }) {
1795
+ if (!description) return /* @__PURE__ */ jsx(Fragment, { children });
1796
+ return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
1797
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1798
+ "span",
1799
+ {
1800
+ tabIndex: 0,
1801
+ className: "-mx-1 cursor-help rounded px-1 outline-hidden transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring",
1802
+ children
1803
+ }
1804
+ ) }),
1805
+ /* @__PURE__ */ jsx(TooltipContent, { children: description })
1806
+ ] }) });
1807
+ }
1808
+ function ResultHeader({ field }) {
1809
+ return /* @__PURE__ */ jsx(Label2, { field, describe: true });
1810
+ }
1811
+ function Label2({ field, describe = false }) {
1812
+ const presentation = useFieldPresentation();
1813
+ const isApp = presentation === "app";
1814
+ const labelText = fieldLabel(field.title, field.name, presentation);
1815
+ if (labelText.trim() === "") {
1816
+ return /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) });
1817
+ }
1818
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
1819
+ /* @__PURE__ */ jsx(Described, { description: describe ? field.description : void 0, children: /* @__PURE__ */ jsx(
1820
+ "span",
1821
+ {
1822
+ className: cn(
1823
+ "text-[13px] font-semibold text-foreground",
1824
+ isApp ? "text-[13.5px]" : "font-mono"
1825
+ ),
1826
+ children: labelText
1827
+ }
1828
+ ) }),
1829
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) })
1830
+ ] });
1831
+ }
1832
+ function Absent() {
1833
+ const s = useFieldStrings();
1834
+ return /* @__PURE__ */ jsxs("span", { className: "text-[13px] text-muted-foreground", children: [
1835
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: s.resultAbsent }),
1836
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.resultAbsentDescription })
1837
+ ] });
1838
+ }
1839
+ function stringifyValue(value) {
1840
+ try {
1841
+ const text = JSON.stringify(
1842
+ value,
1843
+ (_key, member) => typeof member === "bigint" ? member.toString() : member
1844
+ );
1845
+ if (text === void 0 || text === "{}" || text === "[]") return void 0;
1846
+ return JSON.stringify(JSON.parse(text), null, 2);
1847
+ } catch {
1848
+ const keys = Array.isArray(value) ? [] : Object.keys(value);
1849
+ return keys.length > 0 ? `{ ${keys.join(", ")} }` : void 0;
1850
+ }
1851
+ }
1852
+ function RawValue({ value, compact }) {
1853
+ const text = stringifyValue(value);
1854
+ if (text === void 0) return /* @__PURE__ */ jsx(Absent, {});
1855
+ return compact ? /* @__PURE__ */ jsx("span", { title: text, className: "truncate font-mono text-[12px] text-foreground", children: text }) : /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto whitespace-pre-wrap wrap-break-word rounded-md border border-border bg-card/40 px-2.5 py-1.5 font-mono text-[12px] leading-relaxed text-foreground", children: text });
1856
+ }
1857
+ function Scalar({ value, compact = false }) {
1858
+ if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
1859
+ if (typeof value === "object") return /* @__PURE__ */ jsx(RawValue, { value, compact });
1860
+ return /* @__PURE__ */ jsx(
1861
+ "span",
1862
+ {
1863
+ className: cn(
1864
+ "text-[13px] leading-relaxed text-foreground",
1865
+ compact ? "whitespace-nowrap" : "whitespace-pre-wrap"
1866
+ ),
1867
+ children: String(value)
1868
+ }
1869
+ );
1870
+ }
1871
+ function Bool({ value, compact = false }) {
1872
+ const s = useFieldStrings();
1873
+ if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
1874
+ if (typeof value !== "boolean") return /* @__PURE__ */ jsx(Scalar, { value, compact });
1875
+ return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: value ? s.yes : s.no });
1876
+ }
1877
+ function DateValue({ value }) {
1878
+ const content = readDateContent(value);
1879
+ if (!content) return /* @__PURE__ */ jsx(Absent, {});
1880
+ return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: formatDateContent(content) });
1881
+ }
1882
+ function EnumValue({
1883
+ field,
1884
+ value,
1885
+ compact
1886
+ }) {
1887
+ const presentation = useFieldPresentation();
1888
+ const shown = typeof value === "string" ? enumLabeler(field.options, presentation)(value) : value;
1889
+ return /* @__PURE__ */ jsx(Scalar, { value: shown, compact });
1890
+ }
1891
+ function cellTitle(column, cell, presentation) {
1892
+ return column.kind === "enum" && typeof cell === "string" ? enumLabeler(column.options, presentation)(cell) : String(cell);
1893
+ }
1894
+ function fileLabel(url, filename, strings) {
1895
+ if (filename) return filename;
1896
+ const encoded = encodedFileSummary(url, strings);
1897
+ if (encoded !== void 0) return encoded;
1898
+ return pathLabel(url);
1899
+ }
1900
+ function pathLabel(url) {
1901
+ const path = url.split(/[?#]/)[0] ?? url;
1902
+ const segment = path.split("/").filter(Boolean).pop();
1903
+ return segment && segment.length > 0 ? segment : url;
1904
+ }
1905
+ function CopyButton({
1906
+ value,
1907
+ provide,
1908
+ label,
1909
+ title
1910
+ }) {
1911
+ const [copied, setCopied] = useState(false);
1912
+ useEffect(() => {
1913
+ if (!copied) return;
1914
+ const timer = window.setTimeout(() => setCopied(false), 1500);
1915
+ return () => window.clearTimeout(timer);
1916
+ }, [copied]);
1917
+ if (typeof navigator === "undefined" || !navigator.clipboard) return null;
1918
+ return /* @__PURE__ */ jsx(
1919
+ "button",
1920
+ {
1921
+ type: "button",
1922
+ onClick: () => {
1923
+ const done = () => setCopied(true);
1924
+ if (!provide) {
1925
+ void navigator.clipboard.writeText(value ?? "").then(done);
1926
+ return;
1927
+ }
1928
+ const text = provide();
1929
+ if (typeof ClipboardItem === "function" && "write" in navigator.clipboard) {
1930
+ void navigator.clipboard.write([
1931
+ new ClipboardItem({
1932
+ "text/plain": text.then((t) => new Blob([t], { type: "text/plain" }))
1933
+ })
1934
+ ]).then(done, () => void text.then((t) => navigator.clipboard.writeText(t)).then(done));
1935
+ return;
1936
+ }
1937
+ void text.then((t) => navigator.clipboard.writeText(t)).then(done);
1938
+ },
1939
+ ...title ? { title } : {},
1940
+ "aria-label": label,
1941
+ className: "shrink-0 rounded p-0.5 text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
1942
+ children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
1943
+ }
1944
+ );
1945
+ }
1946
+ function CopyUrlButton({ url, storageUrl }) {
1947
+ const s = useFieldStrings();
1948
+ const resolveShare = useResolveShareUrl();
1949
+ const provide = async () => {
1950
+ if (resolveShare && storageUrl) {
1951
+ const shared = await resolveShare(storageUrl);
1952
+ if (shared) return shared;
1953
+ }
1954
+ return absoluteUrl(url);
1955
+ };
1956
+ return /* @__PURE__ */ jsx(CopyButton, { provide, label: s.copyUrl, title: url });
1957
+ }
1958
+ function absoluteUrl(url) {
1959
+ if (!/^\/(?!\/)/.test(url) || typeof window === "undefined") return url;
1960
+ return `${window.location.origin}${url}`;
1961
+ }
1962
+ function FileRef({
1963
+ url,
1964
+ storageUrl,
1965
+ mimeType,
1966
+ filename
1967
+ }) {
1968
+ const s = useFieldStrings();
1969
+ const encoded = encodedFileSummary(url, s);
1970
+ const label = filename ? filename : encoded ?? pathLabel(url);
1971
+ const reference = encoded ?? url;
1972
+ const title = mimeType ? `${reference} \xB7 ${mimeType}` : reference;
1973
+ const href = viewableUrl(url);
1974
+ return /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
1975
+ href ? /* @__PURE__ */ jsx(
1976
+ "a",
1977
+ {
1978
+ href,
1979
+ target: "_blank",
1980
+ rel: "noreferrer",
1981
+ title,
1982
+ className: "min-w-0 truncate font-mono text-[12px] text-foreground underline underline-offset-2",
1983
+ children: label
1984
+ }
1985
+ ) : /* @__PURE__ */ jsx(
1986
+ "span",
1987
+ {
1988
+ title,
1989
+ className: "min-w-0 truncate font-mono text-[12px] text-muted-foreground",
1990
+ children: label
1991
+ }
1992
+ ),
1993
+ /* @__PURE__ */ jsx(CopyUrlButton, { url, ...storageUrl ? { storageUrl } : {} })
1994
+ ] });
1995
+ }
1996
+ function paintable(content, resolve) {
1997
+ const resolved = viewableUrl(resolve?.(content.url));
1998
+ if (resolved) return { url: resolved, fromResolver: true };
1999
+ const stated = viewableUrl(content.publicUrl) ?? viewableUrl(content.url);
2000
+ return stated ? { url: stated, fromResolver: false } : void 0;
2001
+ }
2002
+ function paintableUrl(content, resolve) {
2003
+ return paintable(content, resolve)?.url;
2004
+ }
2005
+ var PREVIEWABLE_EXT_RE = /\.(pdf|png|jpe?g|gif|webp|avif|svg)(\?|#|$)/i;
2006
+ var PREVIEWABLE_MIME_RE = /^(application\/pdf|image\/)/i;
2007
+ function frameableUrl(candidate) {
2008
+ if (!candidate) return void 0;
2009
+ if (/^https?:/.test(candidate.url)) return candidate.url;
2010
+ return candidate.fromResolver && candidate.url.startsWith("/") ? candidate.url : void 0;
2011
+ }
2012
+ function previewableUrl(content, resolve) {
2013
+ const url = frameableUrl(paintable(content, resolve));
2014
+ if (!url) return void 0;
2015
+ const named = content.filename ?? url;
2016
+ const renderable = content.mimeType ? PREVIEWABLE_MIME_RE.test(content.mimeType) : PREVIEWABLE_EXT_RE.test(named);
2017
+ return renderable ? url : void 0;
2018
+ }
2019
+ function DocumentPreview({ url, name }) {
2020
+ return /* @__PURE__ */ jsx(
2021
+ "iframe",
2022
+ {
2023
+ src: url,
2024
+ title: name,
2025
+ loading: "lazy",
2026
+ referrerPolicy: "no-referrer",
2027
+ className: "h-112 w-full rounded-lg border border-border bg-card/40"
2028
+ }
2029
+ );
2030
+ }
2031
+ function DocumentValue({ value }) {
2032
+ const s = useFieldStrings();
2033
+ const resolve = useResolveUrl();
2034
+ const [open, setOpen] = useState(false);
2035
+ const content = readDocumentContent(value);
2036
+ if (!content) return /* @__PURE__ */ jsx(Absent, {});
2037
+ const name = content.title ?? content.filename ?? fileLabel(content.url, content.filename, s);
2038
+ const preview = previewableUrl(content, resolve);
2039
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2040
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [
2041
+ /* @__PURE__ */ jsx(FileText, { "aria-hidden": true, className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
2042
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
2043
+ name && /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] text-foreground", children: name }),
2044
+ /* @__PURE__ */ jsx(
2045
+ FileRef,
2046
+ {
2047
+ url: paintableUrl(content, resolve) ?? content.url,
2048
+ storageUrl: content.url,
2049
+ mimeType: content.mimeType,
2050
+ ...content.filename ? { filename: content.filename } : {}
2051
+ }
2052
+ ),
2053
+ content.snippet && /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-[12px] leading-relaxed text-muted-foreground", children: content.snippet })
2054
+ ] }),
2055
+ preview && /* @__PURE__ */ jsxs(
2056
+ "button",
2057
+ {
2058
+ type: "button",
2059
+ onClick: () => setOpen((current) => !current),
2060
+ "aria-expanded": open,
2061
+ className: "flex shrink-0 items-center gap-1.5 rounded-md border border-border px-2 py-1 text-[12px] text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
2062
+ children: [
2063
+ open ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": true, className: "size-3.5" }),
2064
+ s.preview
2065
+ ]
2066
+ }
2067
+ )
2068
+ ] }),
2069
+ preview && open && /* @__PURE__ */ jsx(DocumentPreview, { url: preview, name })
2070
+ ] });
2071
+ }
2072
+ function ImageValue({
2073
+ value,
2074
+ inGallery = false,
2075
+ inRow = false,
2076
+ compact = false
2077
+ }) {
2078
+ const s = useFieldStrings();
2079
+ const resolve = useResolveUrl();
2080
+ const content = readImageContent(value);
2081
+ if (!content) return /* @__PURE__ */ jsx(Absent, {});
2082
+ const src = paintableUrl(content, resolve);
2083
+ return /* @__PURE__ */ jsxs("div", { className: inGallery ? "space-y-1" : "space-y-1.5", children: [
2084
+ src ? (
2085
+ // Wrapped in a link: the picture is a PREVIEW of a file, and clicking a
2086
+ // preview to see the thing it previews is what a reader expects. The
2087
+ // reference underneath is the other half - a picture with no URL beside
2088
+ // it is a result you can look at and cannot use.
2089
+ /* @__PURE__ */ jsx(
2090
+ "a",
2091
+ {
2092
+ href: src,
2093
+ target: "_blank",
2094
+ rel: "noreferrer",
2095
+ title: content.url,
2096
+ className: inGallery || compact ? "inline-block" : "block w-full",
2097
+ children: /* @__PURE__ */ jsx(
2098
+ LoadingImage,
2099
+ {
2100
+ src,
2101
+ alt: content.caption ?? s.preview,
2102
+ className: cn(
2103
+ inGallery && "block aspect-square w-full object-cover",
2104
+ // A cell is one line tall. An image column rendered at the standalone
2105
+ // height turns every row into a picture and the table into a
2106
+ // slideshow, so a cell gets a thumbnail and the row's expansion gets
2107
+ // the picture.
2108
+ compact && "h-10 w-auto rounded border border-border object-cover",
2109
+ // On its own: the full width it is given, capped generously in
2110
+ // height. `max-h-64` was a 256px thumbnail in a panel several
2111
+ // times that tall — a generated report image is the RESULT here,
2112
+ // not a decoration beside one, and it was unreadable at that size.
2113
+ // `object-contain` keeps the aspect ratio when the cap bites.
2114
+ !inGallery && !compact && "max-h-176 w-full rounded-lg border border-border object-contain"
2115
+ )
2116
+ }
2117
+ )
2118
+ }
2119
+ )
2120
+ ) : inRow ? (
2121
+ // The row variant of "nothing to paint": an icon and a name, exactly as
2122
+ // a document row reads, because that is what this has become.
2123
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [
2124
+ /* @__PURE__ */ jsx(ImageOff, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
2125
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
2126
+ FileRef,
2127
+ {
2128
+ url: content.url,
2129
+ mimeType: content.mimeType,
2130
+ ...content.filename ? { filename: content.filename } : {}
2131
+ }
2132
+ ) })
2133
+ ] })
2134
+ ) : inGallery ? (
2135
+ // Nothing to paint, so the tile says what it is rather than dumping a
2136
+ // ninety-character storage reference into a 140px box. This is what a
2137
+ // host with no resolver sees, so it has to be a design and not a
2138
+ // fallback.
2139
+ /* @__PURE__ */ jsxs("div", { className: "flex aspect-square w-full flex-col items-center justify-center gap-1.5 px-2 text-center", children: [
2140
+ /* @__PURE__ */ jsx(ImageOff, { "aria-hidden": true, className: "size-5 text-muted-foreground" }),
2141
+ /* @__PURE__ */ jsx(
2142
+ "span",
2143
+ {
2144
+ title: content.url,
2145
+ className: "w-full truncate font-mono text-[10.5px] text-muted-foreground",
2146
+ children: fileLabel(content.url, content.filename, s)
2147
+ }
2148
+ )
2149
+ ] })
2150
+ ) : /* @__PURE__ */ jsx(
2151
+ FileRef,
2152
+ {
2153
+ url: content.url,
2154
+ mimeType: content.mimeType,
2155
+ ...content.filename ? { filename: content.filename } : {}
2156
+ }
2157
+ ),
2158
+ src && !compact && /* @__PURE__ */ jsx("div", { className: inGallery ? "px-2.5" : void 0, children: /* @__PURE__ */ jsx(
2159
+ FileRef,
2160
+ {
2161
+ url: src,
2162
+ storageUrl: content.url,
2163
+ mimeType: content.mimeType,
2164
+ ...content.filename ? { filename: content.filename } : {}
2165
+ }
2166
+ ) }),
2167
+ content.caption && /* @__PURE__ */ jsx(
2168
+ "p",
2169
+ {
2170
+ className: cn(
2171
+ "text-[12px] leading-relaxed text-muted-foreground",
2172
+ inGallery && "px-2.5 pb-1.5"
2173
+ ),
2174
+ children: content.caption
2175
+ }
2176
+ )
2177
+ ] });
2178
+ }
2179
+ var TABULAR_KINDS = /* @__PURE__ */ new Set(["text", "number", "boolean", "date", "enum"]);
2180
+ function LeafValue({
2181
+ field,
2182
+ value: raw,
2183
+ compact = false
2184
+ }) {
2185
+ const value = unwrap(field, raw);
2186
+ switch (field.kind) {
2187
+ case "list": {
2188
+ const entries = Array.isArray(value) ? value : [];
2189
+ return TABULAR_KINDS.has(field.item.kind) ? /* @__PURE__ */ jsx(ScalarChips, { field: field.item, items: entries }) : /* @__PURE__ */ jsx(ItemCount, { count: entries.length });
2190
+ }
2191
+ case "prose":
2192
+ return compact ? /* @__PURE__ */ jsx(Scalar, { value, compact: true }) : typeof value === "string" && value !== "" ? /* @__PURE__ */ jsx(Markdown, { text: value }) : /* @__PURE__ */ jsx(Scalar, { value });
2193
+ case "boolean":
2194
+ return /* @__PURE__ */ jsx(Bool, { value, compact });
2195
+ case "date":
2196
+ return /* @__PURE__ */ jsx(DateValue, { value });
2197
+ case "document":
2198
+ return /* @__PURE__ */ jsx(DocumentValue, { value });
2199
+ case "image":
2200
+ return /* @__PURE__ */ jsx(ImageValue, { value, compact });
2201
+ case "object":
2202
+ return compact ? /* @__PURE__ */ jsx(RecordSummary, { field, value }) : /* @__PURE__ */ jsx(Scalar, { value });
2203
+ case "enum":
2204
+ return /* @__PURE__ */ jsx(EnumValue, { field, value, compact });
2205
+ default:
2206
+ return /* @__PURE__ */ jsx(Scalar, { value, compact });
2207
+ }
2208
+ }
2209
+ function RecordSummary({ field, value }) {
2210
+ const s = useFieldStrings();
2211
+ if (isNativeDateNode(field)) return /* @__PURE__ */ jsx(DateValue, { value });
2212
+ if (!isRecord(value) || Object.keys(value).length === 0) return /* @__PURE__ */ jsx(Absent, {});
2213
+ const naming = isNativeHtmlNode(field) ? void 0 : recordNameField(field.fields) ?? field.fields.find((member) => member.kind === "prose");
2214
+ if (naming === void 0) {
2215
+ return /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-[12.5px] text-muted-foreground", children: s.fieldsCount(field.fields.length) });
2216
+ }
2217
+ return /* @__PURE__ */ jsx(LeafValue, { field: naming, value: ownProp(value, naming.name), compact: true });
2218
+ }
2219
+ function LoadingImage({ src, alt, className }) {
2220
+ const [state, setState] = useState("loading");
2221
+ const [current, setCurrent] = useState(src);
2222
+ if (current !== src) {
2223
+ setCurrent(src);
2224
+ setState("loading");
2225
+ }
2226
+ return /* @__PURE__ */ jsxs("span", { className: "relative block", children: [
2227
+ state === "loading" && /* @__PURE__ */ jsx(
2228
+ "span",
2229
+ {
2230
+ role: "status",
2231
+ "aria-label": alt,
2232
+ className: "absolute inset-0 flex items-center justify-center rounded-lg border border-border bg-card/40",
2233
+ children: /* @__PURE__ */ jsx(Loader2, { "aria-hidden": true, className: "size-4 animate-spin text-muted-foreground" })
2234
+ }
2235
+ ),
2236
+ /* @__PURE__ */ jsx(
2237
+ "img",
2238
+ {
2239
+ src,
2240
+ alt,
2241
+ referrerPolicy: "no-referrer",
2242
+ onLoad: () => setState("done"),
2243
+ onError: () => setState("done"),
2244
+ className: cn(className, state === "loading" && "min-h-40 opacity-0")
2245
+ }
2246
+ )
2247
+ ] });
2248
+ }
2249
+ function ScalarChips({ field, items }) {
2250
+ return (
2251
+ // `data-chips` is the hook the definition grid's value cell reaches through:
2252
+ // the cell ends its VALUE at the right edge, and a row of tags long enough
2253
+ // to wrap fills the column, so without the hook each line of tags would
2254
+ // start at the left while every value beside it ended at the right. The attribute keeps that one selector out of this component's own
2255
+ // class list - chips are laid out the same way everywhere, and only the
2256
+ // caller knows which edge they should end on.
2257
+ /* @__PURE__ */ jsx("div", { "data-chips": true, className: "flex flex-wrap gap-1.5", children: items.map((item, index) => /* @__PURE__ */ jsx(
2258
+ "span",
2259
+ {
2260
+ className: "rounded-md border border-border bg-card/40 px-2 py-0.5 text-[12.5px] text-foreground",
2261
+ children: /* @__PURE__ */ jsx(LeafValue, { field, value: item })
2262
+ },
2263
+ index
2264
+ )) })
2265
+ );
2266
+ }
2267
+ function ItemCount({ count }) {
2268
+ const s = useFieldStrings();
2269
+ return /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-[12.5px] text-muted-foreground", children: s.itemsCount(count) });
2270
+ }
2271
+ function isInlineColumn(field) {
2272
+ if (field.kind === "list") return TABULAR_KINDS.has(field.item.kind);
2273
+ return TABULAR_KINDS.has(field.kind);
2274
+ }
2275
+ var CELL_LENGTH_LIMIT = 80;
2276
+ var EXPAND_COLUMN = "2rem";
2277
+ function fitsACellWhole(field) {
2278
+ switch (field.kind) {
2279
+ case "boolean":
2280
+ case "date":
2281
+ case "number":
2282
+ case "enum":
2283
+ return true;
2284
+ case "text":
2285
+ return field.maxLength !== void 0 && field.maxLength <= CELL_LENGTH_LIMIT;
2286
+ case "object":
2287
+ return isNativeDateNode(field);
2288
+ case "list":
2289
+ return isInlineColumn(field) && fitsACellWhole(field.item);
2290
+ default:
2291
+ return false;
2292
+ }
2293
+ }
2294
+ function ObjectTable({
2295
+ columns,
2296
+ element,
2297
+ items,
2298
+ label
2299
+ }) {
2300
+ const s = useFieldStrings();
2301
+ const presentation = useFieldPresentation();
2302
+ const budget = useTableColumns();
2303
+ const scrollerRef = useRef(null);
2304
+ const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
2305
+ const [viewportWidth, setViewportWidth] = useState();
2306
+ const { shown, hidden, name } = chooseColumns(columns, budget);
2307
+ const canExpand = hidden > 0 || shown.some((column) => column !== name && !fitsACellWhole(column));
2308
+ useEffect(() => {
2309
+ const scroller = scrollerRef.current;
2310
+ if (!scroller) return;
2311
+ const measure = () => setViewportWidth(scroller.clientWidth);
2312
+ measure();
2313
+ if (typeof ResizeObserver === "undefined") return;
2314
+ const observer = new ResizeObserver(measure);
2315
+ observer.observe(scroller);
2316
+ return () => observer.disconnect();
2317
+ }, []);
2318
+ const toggle = (index) => setExpanded((current) => {
2319
+ const next = new Set(current);
2320
+ if (!next.delete(index)) next.add(index);
2321
+ return next;
2322
+ });
2323
+ return (
2324
+ // `min-w-full` rather than `w-full`, and a floor under each cell: with
2325
+ // `w-full` the table is pinned to the container's width, so twelve columns
2326
+ // do not overflow - they crush, and a reading's date wraps onto four lines.
2327
+ // A floor makes the table wider than the panel instead, which is what the
2328
+ // scroller is for.
2329
+ //
2330
+ // A scrollable region must be reachable by keyboard. A table of values has
2331
+ // nothing to tab to, so the region itself takes focus; without `tabIndex` the
2332
+ // columns past the fold are reachable with a mouse and by no other means,
2333
+ // which the a11y gate catches as `scrollable-region-focusable`. The name
2334
+ // makes the focus stop announce itself rather than being a silent one.
2335
+ //
2336
+ // `group` rather than `region`, and that is not a detail: `region` is a
2337
+ // LANDMARK, so every table would enter the document's landmark list, and two
2338
+ // of them with the same name - which is what a result rendered twice, or a
2339
+ // structure holding two lists, produces - is `landmark-unique`. A group is
2340
+ // named without claiming to be a section of the page.
2341
+ /* @__PURE__ */ jsx(
2342
+ "div",
2343
+ {
2344
+ ref: scrollerRef,
2345
+ role: "group",
2346
+ "aria-label": label,
2347
+ tabIndex: 0,
2348
+ className: "overflow-x-auto rounded-lg border border-border",
2349
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-full border-collapse text-[13px]", children: [
2350
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
2351
+ canExpand && /* @__PURE__ */ jsx("th", { scope: "col", style: { width: EXPAND_COLUMN }, className: "px-1", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.rowDetailsColumn }) }),
2352
+ shown.map((column) => /* @__PURE__ */ jsx(
2353
+ "th",
2354
+ {
2355
+ scope: "col",
2356
+ className: cn(
2357
+ "whitespace-nowrap px-3 py-2 text-[12px] font-semibold text-foreground",
2358
+ presentation === "app" ? void 0 : "font-mono"
2359
+ ),
2360
+ children: /* @__PURE__ */ jsx(Described, { description: column.description, children: fieldLabel(column.title, column.name, presentation) })
2361
+ },
2362
+ column.name
2363
+ ))
2364
+ ] }) }),
2365
+ /* @__PURE__ */ jsx("tbody", { children: items.map((item, index) => {
2366
+ const isOpen = canExpand && expanded.has(index);
2367
+ return /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border/60 last:border-b-0", children: [
2368
+ canExpand && /* @__PURE__ */ jsx("td", { className: "px-1 pt-px align-top", children: /* @__PURE__ */ jsx(
2369
+ "button",
2370
+ {
2371
+ type: "button",
2372
+ onClick: () => toggle(index),
2373
+ "aria-expanded": isOpen,
2374
+ "aria-label": s.toggleRowDetails(index + 1),
2375
+ className: "mt-0.5 flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
2376
+ children: isOpen ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-3.5" })
2377
+ }
2378
+ ) }),
2379
+ isOpen ? /* @__PURE__ */ jsx("td", { colSpan: shown.length, className: "bg-card/40 p-0", children: /* @__PURE__ */ jsx(
2380
+ "div",
2381
+ {
2382
+ className: "sticky left-0 px-3.5 py-3",
2383
+ ...viewportWidth ? {
2384
+ style: {
2385
+ // Minus the chevron column: this cell starts
2386
+ // after it, so pinning it to the FULL visible
2387
+ // width pushes its right edge that far past the
2388
+ // scroller. See `EXPAND_COLUMN`.
2389
+ width: canExpand ? `calc(${viewportWidth}px - ${EXPAND_COLUMN})` : viewportWidth
2390
+ }
2391
+ } : {},
2392
+ children: /* @__PURE__ */ jsx(ResultField, { field: element, value: item, hideLabel: true })
2393
+ }
2394
+ ) }) : shown.map((column) => {
2395
+ const cell = typeof item === "object" && item !== null ? ownProp(item, column.name) : void 0;
2396
+ return /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 align-top", children: /* @__PURE__ */ jsx(
2397
+ "div",
2398
+ {
2399
+ className: cn(
2400
+ "max-w-[44ch]",
2401
+ column === name ? "max-w-none min-w-[16ch] wrap-anywhere [&>span]:whitespace-normal" : column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
2402
+ ),
2403
+ ...typeof cell === "string" || typeof cell === "number" ? { title: cellTitle(column, cell, presentation) } : {},
2404
+ children: /* @__PURE__ */ jsx(LeafValue, { field: column, value: cell, compact: true })
2405
+ }
2406
+ ) }, column.name);
2407
+ })
2408
+ ] }) }, index);
2409
+ }) })
2410
+ ] })
2411
+ }
2412
+ )
2413
+ );
2414
+ }
2415
+ function CompositeValue({ members }) {
2416
+ return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: members.map((member) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2417
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[12px] font-semibold text-foreground", children: member.name }),
2418
+ /* @__PURE__ */ jsx(NativeValue, { value: member.value })
2419
+ ] }, member.name)) });
2420
+ }
2421
+ function NativeValue({ value }) {
2422
+ if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
2423
+ const html = readHtmlContent(value);
2424
+ if (html) return /* @__PURE__ */ jsx(HtmlPreview, { content: html });
2425
+ const date = readDateContent(value);
2426
+ if (date) return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: formatDateContent(date) });
2427
+ const document2 = readDocumentContent(value);
2428
+ if (document2?.url !== void 0) {
2429
+ return /* @__PURE__ */ jsx(
2430
+ FileRef,
2431
+ {
2432
+ url: document2.url,
2433
+ ...document2.mimeType !== void 0 ? { mimeType: document2.mimeType } : {},
2434
+ ...document2.filename !== void 0 ? { filename: document2.filename } : {}
2435
+ }
2436
+ );
2437
+ }
2438
+ if (isRecord(value) && Array.isArray(value.items)) {
2439
+ const items = value.items;
2440
+ if (items.length === 0) return /* @__PURE__ */ jsx(Absent, {});
2441
+ return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60 overflow-hidden rounded-lg border border-border", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "bg-card/40 px-3 py-1.5", children: /* @__PURE__ */ jsx(NativeValue, { value: item }) }, index)) });
2442
+ }
2443
+ if (isRecord(value)) {
2444
+ const text = value.text;
2445
+ if (typeof text === "string") {
2446
+ return text === "" ? /* @__PURE__ */ jsx(Absent, {}) : /* @__PURE__ */ jsx(Markdown, { text });
2447
+ }
2448
+ }
2449
+ if (typeof value === "string") return /* @__PURE__ */ jsx(Markdown, { text: value });
2450
+ if (typeof value !== "object") return /* @__PURE__ */ jsx(Scalar, { value });
2451
+ return /* @__PURE__ */ jsx(RawValue, { value, compact: false });
2452
+ }
2453
+ function isRecord(value) {
2454
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2455
+ }
2456
+ function ScalarLines({ field, items }) {
2457
+ return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60 overflow-hidden rounded-lg border border-border", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "bg-card/40 px-3 py-1.5", children: /* @__PURE__ */ jsx(LeafValue, { field, value: item }) }, index)) });
2458
+ }
2459
+ function ImageGallery({ items }) {
2460
+ const anyViewable = items.some((item) => {
2461
+ const content = readImageContent(item);
2462
+ return content ? !!(viewableUrl(content.publicUrl) ?? viewableUrl(content.url)) : false;
2463
+ });
2464
+ if (!anyViewable) return /* @__PURE__ */ jsx(FileRows, { items, kind: "image" });
2465
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(140px,1fr))] gap-2", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-border bg-card/40", children: /* @__PURE__ */ jsx(ImageValue, { value: item, inGallery: true }) }, index)) });
2466
+ }
2467
+ function FileRows({ items, kind }) {
2468
+ return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border overflow-hidden rounded-lg border border-border", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "bg-card/40 px-3.5 py-2", children: kind === "document" ? /* @__PURE__ */ jsx(DocumentValue, { value: item }) : /* @__PURE__ */ jsx(ImageValue, { value: item, inRow: true }) }, index)) });
2469
+ }
2470
+ function recordColumns(item) {
2471
+ if (item.kind !== "object" || item.fields.length === 0) return void 0;
2472
+ return item.fields;
2473
+ }
2474
+ function recordNameField(fields) {
2475
+ return fields.find((member) => member.kind === "text");
2476
+ }
2477
+ function columnTier(field, name) {
2478
+ if (field === name) return 1;
2479
+ if (field.kind !== "list" && fitsACellWhole(field)) return 2;
2480
+ if (field.kind === "text" || field.kind === "list" && isInlineColumn(field)) return 3;
2481
+ return 4;
2482
+ }
2483
+ function chooseColumns(fields, budget) {
2484
+ const name = recordNameField(fields);
2485
+ if (fields.length <= budget) return { shown: fields, hidden: 0, ...name ? { name } : {} };
2486
+ const kept = new Set(
2487
+ fields.map((field, index) => ({ field, index, tier: columnTier(field, name) })).sort((a, b) => a.tier - b.tier || a.index - b.index).slice(0, budget).map(({ field }) => field)
2488
+ );
2489
+ return {
2490
+ shown: fields.filter((field) => kept.has(field)),
2491
+ hidden: fields.length - kept.size,
2492
+ ...name ? { name } : {}
2493
+ };
2494
+ }
2495
+ function ResultField({ field, value, depth = 0, hideLabel = false }) {
2496
+ const s = useFieldStrings();
2497
+ const unwrapped = unwrap(field, value);
2498
+ const header = hideLabel ? null : /* @__PURE__ */ jsx(Label2, { field, describe: true });
2499
+ const copyText = (field.kind === "text" || field.kind === "prose") && typeof unwrapped === "string" && unwrapped !== "" ? unwrapped : void 0;
2500
+ if (isNativeDateNode(field)) {
2501
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
2502
+ header,
2503
+ /* @__PURE__ */ jsx(DateValue, { value: unwrapped })
2504
+ ] });
2505
+ }
2506
+ if (isNativeHtmlNode(field)) {
2507
+ const content = readHtmlContent(unwrapped);
2508
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2509
+ header,
2510
+ content ? /* @__PURE__ */ jsx(HtmlPreview, { content }) : /* @__PURE__ */ jsx(Absent, {})
2511
+ ] });
2512
+ }
2513
+ if (isNativeCompositeNode(field)) {
2514
+ const members = readCompositeContent(unwrapped);
2515
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2516
+ header,
2517
+ members && members.length > 0 ? /* @__PURE__ */ jsx(CompositeValue, { members }) : /* @__PURE__ */ jsx(Absent, {})
2518
+ ] });
2519
+ }
2520
+ switch (field.kind) {
2521
+ case "object": {
2522
+ const memberValue = (child) => typeof unwrapped === "object" && unwrapped !== null ? ownProp(unwrapped, child.name) : void 0;
2523
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2524
+ header,
2525
+ /* @__PURE__ */ jsx(
2526
+ "div",
2527
+ {
2528
+ className: cn(
2529
+ // A definition grid, not a stack. Label above value spends two
2530
+ // lines on every field, so a structure of ten is twenty lines of
2531
+ // alternating label and answer with nothing aligned. Beside each
2532
+ // other they take one line and the values line up, which is what
2533
+ // makes a record scannable.
2534
+ //
2535
+ // The label column is CAPPED at 40%, and that is not cosmetic. A
2536
+ // bare `auto` sizes to the widest label in the grid - label text
2537
+ // plus its concept pill - so one long field name starved every
2538
+ // value beside it, and inside a 400px panel a paragraph came out
2539
+ // as a ten-line ribbon in a 130px column. `min-content` keeps a
2540
+ // short label short; the cap stops any label taking more than its
2541
+ // share, whatever the panel width turns out to be.
2542
+ "grid grid-cols-[minmax(min-content,40%)_minmax(0,1fr)] items-baseline gap-x-3 gap-y-1.5",
2543
+ // A list item already sits in its own bordered row; nesting a
2544
+ // second card inside it draws a box in a box for no information
2545
+ // gained.
2546
+ !hideLabel && "rounded-lg border border-border bg-card/40 px-3.5 py-3"
2547
+ ),
2548
+ children: field.fields.map(
2549
+ (child) => (
2550
+ // Only a SHORT value shares a line. Prose needs the full width, and
2551
+ // so does anything with chrome of its own - a table, a gallery, a
2552
+ // frame - which beside a label would be squeezed into whatever the
2553
+ // longest label left over.
2554
+ isInlineColumn(child) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
2555
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx(Label2, { field: child, describe: true }) }),
2556
+ /* @__PURE__ */ jsx("div", { className: "flex min-w-0 justify-end **:data-chips:justify-end", children: /* @__PURE__ */ jsx("div", { className: "min-w-0 text-left wrap-break-word", children: /* @__PURE__ */ jsx(LeafValue, { field: child, value: memberValue(child) }) }) })
2557
+ ] }, child.name) : /* @__PURE__ */ jsx("div", { className: "col-span-2 min-w-0", children: /* @__PURE__ */ jsx(ResultField, { field: child, value: memberValue(child), depth: depth + 1 }) }, child.name)
2558
+ )
2559
+ )
2560
+ }
2561
+ )
2562
+ ] });
2563
+ }
2564
+ case "list": {
2565
+ const items = Array.isArray(unwrapped) ? unwrapped : [];
2566
+ const columns = recordColumns(field.item);
2567
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2568
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
2569
+ header,
2570
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: s.itemsCount(items.length) })
2571
+ ] }),
2572
+ items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[13px] italic text-muted-foreground", children: s.noItemsYet }) : isNativeValueNode(field.item) ? (
2573
+ // A list of dates, pages or composites is a list of VALUES, each read
2574
+ // by its own arm - one per line. Their nodes are `object` (or, for a
2575
+ // composite, `unknown`), so the branches below would tabulate a
2576
+ // date's `date` and `time`, print a page's markup source down a
2577
+ // column named by it, and show a composite as JSON. The arm keyed by
2578
+ // concept is the one that knows what each entry is.
2579
+ /* @__PURE__ */ jsx("div", { className: "divide-y divide-border/60 overflow-hidden rounded-lg border border-border", children: items.map((item, index) => /* @__PURE__ */ jsx("div", { className: "bg-card/40 px-3 py-1.5", children: /* @__PURE__ */ jsx(ResultField, { field: field.item, value: item, depth: depth + 1, hideLabel: true }) }, index)) })
2580
+ ) : columns ? (
2581
+ // Same shape every row: the labels are column headers, not per-row
2582
+ // labels. See `ObjectTable`.
2583
+ /* @__PURE__ */ jsx(
2584
+ ObjectTable,
2585
+ {
2586
+ columns,
2587
+ element: field.item,
2588
+ items,
2589
+ label: field.title ?? humanizeFieldName(field.name)
2590
+ }
2591
+ )
2592
+ ) : TABULAR_KINDS.has(field.item.kind) ? /* @__PURE__ */ jsx(ScalarChips, { field: field.item, items }) : field.item.kind === "prose" || field.item.kind === "unknown" ? (
2593
+ // `unknown` shares the LINES layout, and that is a reading of the
2594
+ // standard rather than a convenience. `unknown` is the deliberate
2595
+ // escape hatch: the producer could not map the node honestly and
2596
+ // said so, which is what an untyped `type = "list"` field derives
2597
+ // to. A card per entry would spend a bordered box and an index on
2598
+ // each value to say nothing extra - the card earns its place around
2599
+ // a STRUCTURE, and this is precisely the node we have been told is
2600
+ // not known to be one. Lines cost nothing and read the same whether
2601
+ // the entries turn out to be words or objects, because `LeafValue`
2602
+ // falls through to `RawValue` either way.
2603
+ /* @__PURE__ */ jsx(ScalarLines, { field: field.item, items })
2604
+ ) : field.item.kind === "image" ? /* @__PURE__ */ jsx(ImageGallery, { items }) : field.item.kind === "document" ? /* @__PURE__ */ jsx(FileRows, { items, kind: "document" }) : (
2605
+ // Nested, or carrying prose: a card per entry is what a table cell
2606
+ // cannot hold. The index label earns its place here, where the
2607
+ // entries are structures rather than values.
2608
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: items.map((item, index) => /* @__PURE__ */ jsxs(
2609
+ "div",
2610
+ {
2611
+ className: "space-y-2 rounded-lg border border-border bg-card/40 px-3.5 py-3",
2612
+ children: [
2613
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground", children: index + 1 }),
2614
+ /* @__PURE__ */ jsx(ResultField, { field: field.item, value: item, depth: depth + 1, hideLabel: true })
2615
+ ]
2616
+ },
2617
+ index
2618
+ )) })
2619
+ )
2620
+ ] });
2621
+ }
2622
+ case "boolean":
2623
+ case "date":
2624
+ case "document":
2625
+ case "image":
2626
+ case "text":
2627
+ case "prose":
2628
+ case "number":
2629
+ case "enum":
2630
+ case "unknown":
2631
+ return /* @__PURE__ */ jsxs("div", { className: field.kind === "image" ? "space-y-1.5" : "space-y-0.5", children: [
2632
+ copyText === void 0 ? header : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
2633
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
2634
+ /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText })
2635
+ ] }),
2636
+ /* @__PURE__ */ jsx(LeafValue, { field, value })
2637
+ ] });
2638
+ }
2639
+ return null;
2640
+ }
2641
+
2642
+ // src/react/download-stuff.ts
2643
+ function saveBlob(blob, filename) {
2644
+ const href = URL.createObjectURL(blob);
2645
+ const anchor = document.createElement("a");
2646
+ anchor.href = href;
2647
+ anchor.download = filename;
2648
+ document.body.appendChild(anchor);
2649
+ anchor.click();
2650
+ anchor.remove();
2651
+ setTimeout(() => URL.revokeObjectURL(href), 0);
2652
+ }
2653
+ function mimeForExtension(extension) {
2654
+ return extension === "html" ? "text/html;charset=utf-8" : "text/plain;charset=utf-8";
2655
+ }
2656
+ function extensionOf(url, filename) {
2657
+ const named = filename?.match(/\.([A-Za-z0-9]{1,8})$/)?.[1];
2658
+ if (named) return named.toLowerCase();
2659
+ const path = url.split(/[?#]/)[0] ?? url;
2660
+ const match = path.match(/\.([A-Za-z0-9]{1,8})$/)?.[1];
2661
+ return match ? match.toLowerCase() : "";
2662
+ }
2663
+ function fileNameFor(file, base) {
2664
+ if (file.filename) return file.filename;
2665
+ const extension = file.extension ?? extensionOf(file.url ?? "", file.filename);
2666
+ const stem = `${base}-${file.path.replace(/\./g, "-")}`;
2667
+ return extension ? `${stem}.${extension}` : stem;
2668
+ }
2669
+ async function downloadStuff({
2670
+ field,
2671
+ value,
2672
+ baseName,
2673
+ resolveUrl
2674
+ }) {
2675
+ const files = collectStuffFiles(field, value);
2676
+ const isBareFile = files.length === 1 && files[0]?.path === field.name;
2677
+ let deliveredAFile = false;
2678
+ for (const file of files) {
2679
+ const name = fileNameFor(file, baseName);
2680
+ if (file.text !== void 0) {
2681
+ saveBlob(new Blob([file.text], { type: mimeForExtension(file.extension) }), name);
2682
+ deliveredAFile = true;
2683
+ continue;
2684
+ }
2685
+ const href = viewableUrl(resolveUrl?.(file.url ?? "")) ?? viewableUrl(file.publicUrl) ?? viewableUrl(file.url);
2686
+ if (!href) continue;
2687
+ try {
2688
+ const response = await fetch(href);
2689
+ if (!response.ok) throw new Error(String(response.status));
2690
+ saveBlob(await response.blob(), name);
2691
+ } catch {
2692
+ window.open(href, "_blank", "noopener");
2693
+ }
2694
+ deliveredAFile = true;
2695
+ }
2696
+ if (isBareFile && deliveredAFile) return;
2697
+ saveBlob(
2698
+ new Blob([JSON.stringify(value, null, 2)], { type: "application/json;charset=utf-8" }),
2699
+ `${baseName}.json`
2700
+ );
2701
+ }
2702
+ var JSON_TOKEN_RE = /("(?:\\.|[^"\\])*"\s*:)|("(?:\\.|[^"\\])*")|(\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;
2703
+ function highlight(json) {
2704
+ const out = [];
2705
+ let last = 0;
2706
+ for (const match of json.matchAll(JSON_TOKEN_RE)) {
2707
+ const [token, key, str, literal] = match;
2708
+ const at = match.index;
2709
+ if (at > last) {
2710
+ out.push(
2711
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: json.slice(last, at) }, `p${last}`)
2712
+ );
2713
+ }
2714
+ out.push(
2715
+ /* @__PURE__ */ jsx(
2716
+ "span",
2717
+ {
2718
+ className: key ? "text-muted-foreground" : str ? "text-foreground" : "font-semibold text-foreground",
2719
+ children: token
2720
+ },
2721
+ at
2722
+ )
2723
+ );
2724
+ last = at + token.length;
2725
+ }
2726
+ if (last < json.length) {
2727
+ out.push(
2728
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: json.slice(last) }, "tail")
2729
+ );
2730
+ }
2731
+ return out;
2732
+ }
2733
+ function JsonView({ value }) {
2734
+ const s = useFieldStrings();
2735
+ const [copied, setCopied] = useState(false);
2736
+ useEffect(() => {
2737
+ if (!copied) return;
2738
+ const timer = window.setTimeout(() => setCopied(false), 1500);
2739
+ return () => window.clearTimeout(timer);
2740
+ }, [copied]);
2741
+ const text = value === null || value === void 0 ? void 0 : typeof value === "object" ? stringifyValue(value) : JSON.stringify(value);
2742
+ if (text === void 0) {
2743
+ return /* @__PURE__ */ jsx(Absent, {});
2744
+ }
2745
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
2746
+ typeof navigator !== "undefined" && navigator.clipboard && /* @__PURE__ */ jsx(
2747
+ "button",
2748
+ {
2749
+ type: "button",
2750
+ onClick: () => {
2751
+ void navigator.clipboard.writeText(text).then(() => setCopied(true));
2752
+ },
2753
+ "aria-label": s.copyJson,
2754
+ className: "absolute right-2 top-2 rounded border border-border bg-card p-1 text-muted-foreground hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
2755
+ children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
2756
+ }
2757
+ ),
2758
+ /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto rounded-lg border border-border bg-card/40 px-3.5 py-3 pr-12 font-mono text-[12px] leading-relaxed", children: highlight(text) })
2759
+ ] });
2760
+ }
2761
+ function StuffViewer({
2762
+ field,
2763
+ value,
2764
+ name,
2765
+ defaultView = "rendered",
2766
+ downloadBaseName,
2767
+ hideDownload = false,
2768
+ className
2769
+ }) {
2770
+ const s = useFieldStrings();
2771
+ const named = name ? { ...field, name } : field;
2772
+ const [view, setView] = useState(defaultView);
2773
+ const [saving, setSaving] = useState(false);
2774
+ const resolveUrl = useResolveUrl();
2775
+ const handleDownload = useCallback(async () => {
2776
+ setSaving(true);
2777
+ try {
2778
+ await downloadStuff({
2779
+ field,
2780
+ value,
2781
+ baseName: downloadBaseName ?? named.name ?? "result",
2782
+ resolveUrl
2783
+ });
2784
+ } finally {
2785
+ setSaving(false);
2786
+ }
2787
+ }, [field, named.name, value, downloadBaseName, resolveUrl]);
2788
+ const views = [
2789
+ { id: "rendered", label: s.viewRendered },
2790
+ { id: "json", label: s.viewJson }
2791
+ ];
2792
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
2793
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-x-4 gap-y-1", children: [
2794
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-1", children: /* @__PURE__ */ jsx(ResultHeader, { field: named }) }),
2795
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
2796
+ !hideDownload && /* @__PURE__ */ jsxs(
2797
+ "button",
2798
+ {
2799
+ type: "button",
2800
+ onClick: () => void handleDownload(),
2801
+ disabled: saving,
2802
+ "aria-label": s.download,
2803
+ className: "inline-flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-[12px] text-muted-foreground hover:text-foreground focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 disabled:opacity-60",
2804
+ children: [
2805
+ saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }),
2806
+ saving ? s.downloading : s.download
2807
+ ]
2808
+ }
2809
+ ),
2810
+ /* @__PURE__ */ jsx(
2811
+ "div",
2812
+ {
2813
+ role: "group",
2814
+ "aria-label": s.resultViewGroup,
2815
+ className: "flex rounded-md border border-border p-0.5",
2816
+ children: views.map(({ id, label }) => /* @__PURE__ */ jsx(
2817
+ "button",
2818
+ {
2819
+ type: "button",
2820
+ onClick: () => setView(id),
2821
+ "aria-pressed": view === id,
2822
+ className: cn(
2823
+ "rounded px-2 py-0.5 text-[12px] focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1",
2824
+ view === id ? "bg-card font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
2825
+ ),
2826
+ children: label
2827
+ },
2828
+ id
2829
+ ))
2830
+ }
2831
+ )
2832
+ ] })
2833
+ ] }),
2834
+ view === "json" ? /* @__PURE__ */ jsx(JsonView, { value }) : /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: true })
2835
+ ] });
2836
+ }
2837
+
2838
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, HtmlPreview, ImageField, JsonView, ListField, Markdown, NumberField, ObjectField, OptionalToggle, ProseField, ResultEnvProvider, ResultField, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, cn, downloadStuff, fieldControlClass, humanizeFieldName, useFieldDomId, useFieldPresentation, useFieldStrings, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
2839
+ //# sourceMappingURL=chunk-G6BWZUGV.js.map
2840
+ //# sourceMappingURL=chunk-G6BWZUGV.js.map