@pipelex/mthds-form 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/{chunk-6XD6UZSU.js → chunk-25I6JDIK.js} +2 -2
- package/dist/chunk-25I6JDIK.js.map +1 -0
- package/dist/chunk-2JWRG53S.js +33 -0
- package/dist/chunk-2JWRG53S.js.map +1 -0
- package/dist/chunk-45C2MKK6.js +94 -0
- package/dist/chunk-45C2MKK6.js.map +1 -0
- package/dist/chunk-4BSBBG2T.js +80 -0
- package/dist/chunk-4BSBBG2T.js.map +1 -0
- package/dist/{chunk-7BMOPXDY.js → chunk-GCXV76WU.js} +17 -9
- package/dist/chunk-GCXV76WU.js.map +1 -0
- package/dist/chunk-IIU3YK4O.js +16 -0
- package/dist/chunk-IIU3YK4O.js.map +1 -0
- package/dist/chunk-J4L32LD6.js +137 -0
- package/dist/chunk-J4L32LD6.js.map +1 -0
- package/dist/{chunk-KFZ5MZOM.js → chunk-LBFZXYCY.js} +3 -3
- package/dist/{chunk-KFZ5MZOM.js.map → chunk-LBFZXYCY.js.map} +1 -1
- package/dist/{chunk-O4O3QNVC.js → chunk-OD3VYXUY.js} +13 -3
- package/dist/chunk-OD3VYXUY.js.map +1 -0
- package/dist/{chunk-XEPD5VJQ.js → chunk-R7B7ID25.js} +8 -8
- package/dist/chunk-R7B7ID25.js.map +1 -0
- package/dist/{chunk-WXBSM2CE.js → chunk-T4NSAGOS.js} +8 -8
- package/dist/chunk-T4NSAGOS.js.map +1 -0
- package/dist/{chunk-HARYEWNG.js → chunk-XYQ7NXQZ.js} +2 -2
- package/dist/chunk-XYQ7NXQZ.js.map +1 -0
- package/dist/chunk-YEVVR6FE.js +66 -0
- package/dist/chunk-YEVVR6FE.js.map +1 -0
- package/dist/core/contracts.d.ts +27 -56
- package/dist/core/contracts.js +1 -1
- package/dist/core/derive.d.ts +79 -11
- package/dist/core/derive.js +4 -4
- package/dist/core/descriptor.d.ts +70 -26
- package/dist/core/descriptor.js +1 -1
- package/dist/core/file-formats.d.ts +102 -0
- package/dist/core/file-formats.js +3 -0
- package/dist/core/file-formats.js.map +1 -0
- package/dist/core/gate.d.ts +1 -1
- package/dist/core/gate.js +7 -8
- package/dist/core/gating-fields.d.ts +38 -0
- package/dist/core/gating-fields.js +6 -0
- package/dist/core/gating-fields.js.map +1 -0
- package/dist/core/index.d.ts +8 -3
- package/dist/core/index.js +14 -10
- package/dist/core/native-content.d.ts +219 -0
- package/dist/core/native-content.js +4 -0
- package/dist/core/{native-concepts.js.map → native-content.js.map} +1 -1
- package/dist/core/normalize-schema.js +2 -2
- package/dist/core/output-form.d.ts +45 -0
- package/dist/core/output-form.js +5 -0
- package/dist/core/output-form.js.map +1 -0
- package/dist/core/readiness.d.ts +2 -0
- package/dist/core/schema-utils.d.ts +13 -1
- package/dist/core/schema-utils.js +1 -1
- package/dist/core/stuff-files.d.ts +52 -0
- package/dist/core/stuff-files.js +5 -0
- package/dist/core/stuff-files.js.map +1 -0
- package/dist/core/values.d.ts +10 -3
- package/dist/core/values.js +7 -7
- package/dist/core/wire-format.js +3 -4
- package/dist/react/index.d.ts +278 -1
- package/dist/react/index.js +1378 -29
- package/dist/react/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +17 -3
- package/dist/chunk-6XD6UZSU.js.map +0 -1
- package/dist/chunk-7BMOPXDY.js.map +0 -1
- package/dist/chunk-HARYEWNG.js.map +0 -1
- package/dist/chunk-NMT7BRLO.js +0 -40
- package/dist/chunk-NMT7BRLO.js.map +0 -1
- package/dist/chunk-O4O3QNVC.js.map +0 -1
- package/dist/chunk-VMLXO5JS.js +0 -132
- package/dist/chunk-VMLXO5JS.js.map +0 -1
- package/dist/chunk-WXBSM2CE.js.map +0 -1
- package/dist/chunk-XEPD5VJQ.js.map +0 -1
- package/dist/core/native-concepts.d.ts +0 -78
- package/dist/core/native-concepts.js +0 -3
package/dist/react/index.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { collectStuffFiles } from '../chunk-4BSBBG2T.js';
|
|
3
|
+
import { isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isViewableUrl, readDateContent, formatDateContent, readImageContent, readDocumentContent } from '../chunk-45C2MKK6.js';
|
|
4
|
+
import { conceptCategory } from '../chunk-25I6JDIK.js';
|
|
5
|
+
import { isAcceptedFile, acceptMapForKind } from '../chunk-2JWRG53S.js';
|
|
2
6
|
import { isFilled } from '../chunk-PM55AHRO.js';
|
|
3
|
-
import { ownProp } from '../chunk-V3OV2XQT.js';
|
|
4
7
|
import { toStoredDateValue, toDateInputValue } from '../chunk-GKV77RAS.js';
|
|
8
|
+
import { ownProp } from '../chunk-V3OV2XQT.js';
|
|
5
9
|
import { clsx } from 'clsx';
|
|
6
10
|
import { twMerge } from 'tailwind-merge';
|
|
7
|
-
import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image, FileText, CalendarDays, Type, Loader2, Upload, Link2, ImageOff, EyeOff, Eye, X, Minus, Plus, Trash2 } from 'lucide-react';
|
|
11
|
+
import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image, FileText, CalendarDays, Type, Loader2, Upload, Link2, ImageOff, EyeOff, Eye, X, Minus, Plus, Trash2, Copy, Download, ChevronRight } from 'lucide-react';
|
|
8
12
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
13
|
import * as React2 from 'react';
|
|
10
|
-
import { createContext, use, useMemo, useContext, useState, useRef, useCallback, useEffect } from 'react';
|
|
14
|
+
import { createContext, use, useId, useMemo, useContext, useState, useRef, useCallback, useEffect, Fragment as Fragment$1, useLayoutEffect } from 'react';
|
|
11
15
|
import * as SwitchPrimitives from '@radix-ui/react-switch';
|
|
12
16
|
import { useDropzone } from 'react-dropzone';
|
|
13
17
|
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
14
18
|
import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
|
|
15
19
|
import * as TogglePrimitive from '@radix-ui/react-toggle';
|
|
16
20
|
import { cva } from 'class-variance-authority';
|
|
21
|
+
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
|
22
|
+
import { marked } from 'marked';
|
|
17
23
|
|
|
18
24
|
function cn(...inputs) {
|
|
19
25
|
return twMerge(clsx(inputs));
|
|
@@ -85,9 +91,24 @@ var Switch = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
85
91
|
}
|
|
86
92
|
));
|
|
87
93
|
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
94
|
+
var FieldDomIdContext = createContext(null);
|
|
95
|
+
function FieldDomIdProvider({
|
|
96
|
+
prefix,
|
|
97
|
+
children
|
|
98
|
+
}) {
|
|
99
|
+
const generated = useId();
|
|
100
|
+
return /* @__PURE__ */ jsx(FieldDomIdContext, { value: prefix ?? generated, children });
|
|
101
|
+
}
|
|
102
|
+
function useFieldDomId(path) {
|
|
103
|
+
const scoped = use(FieldDomIdContext);
|
|
104
|
+
const local = useId();
|
|
105
|
+
const prefix = scoped ?? local;
|
|
106
|
+
return prefix === "" ? path : `${prefix}-${path}`;
|
|
107
|
+
}
|
|
88
108
|
function BooleanField({ field, value, onChange, id, disabled }) {
|
|
89
109
|
const presentation = useFieldPresentation();
|
|
90
110
|
const isApp = presentation === "app";
|
|
111
|
+
const domId = useFieldDomId(id);
|
|
91
112
|
return /* @__PURE__ */ jsxs(
|
|
92
113
|
"div",
|
|
93
114
|
{
|
|
@@ -101,7 +122,7 @@ function BooleanField({ field, value, onChange, id, disabled }) {
|
|
|
101
122
|
/* @__PURE__ */ jsx(
|
|
102
123
|
"label",
|
|
103
124
|
{
|
|
104
|
-
htmlFor:
|
|
125
|
+
htmlFor: domId,
|
|
105
126
|
className: cn(
|
|
106
127
|
"text-[13px] font-medium leading-none text-foreground",
|
|
107
128
|
isApp ? "text-[13.5px]" : "font-mono"
|
|
@@ -116,7 +137,7 @@ function BooleanField({ field, value, onChange, id, disabled }) {
|
|
|
116
137
|
/* @__PURE__ */ jsx(
|
|
117
138
|
Switch,
|
|
118
139
|
{
|
|
119
|
-
id,
|
|
140
|
+
id: domId,
|
|
120
141
|
checked: value ?? false,
|
|
121
142
|
onCheckedChange: onChange,
|
|
122
143
|
disabled,
|
|
@@ -149,6 +170,23 @@ var DEFAULT_FIELD_STRINGS = {
|
|
|
149
170
|
preview: "Preview",
|
|
150
171
|
removeFileAria: "Remove file",
|
|
151
172
|
previewUnavailablePdf: "Preview unavailable - open to view.",
|
|
173
|
+
// U+002D HYPHEN-MINUS, deliberately: not an en dash, not an em dash.
|
|
174
|
+
resultAbsent: "-",
|
|
175
|
+
resultAbsentDescription: "Not provided",
|
|
176
|
+
resultUndescribed: "No output descriptor for this pipe \u2014 showing the raw value.",
|
|
177
|
+
toggleRowDetails: (index) => `Show or hide the details of row ${index}`,
|
|
178
|
+
rowDetailsColumn: "Details",
|
|
179
|
+
copyUrl: "Copy the URL",
|
|
180
|
+
copyText: "Copy the text",
|
|
181
|
+
download: "Download",
|
|
182
|
+
downloading: "Downloading\u2026",
|
|
183
|
+
viewRendered: "Result",
|
|
184
|
+
viewJson: "JSON",
|
|
185
|
+
copyJson: "Copy the JSON",
|
|
186
|
+
resultViewGroup: "Result view",
|
|
187
|
+
yes: "Yes",
|
|
188
|
+
no: "No",
|
|
189
|
+
unsupportedFileType: (accept) => `That file type is not supported. Accepted formats: ${accept}.`,
|
|
152
190
|
hideOptionalFields: "Hide optional fields",
|
|
153
191
|
hideOptionalInputs: "Hide optional inputs",
|
|
154
192
|
optionalFieldsCount: (count) => count === 1 ? "1 optional field" : `${count} optional fields`,
|
|
@@ -180,13 +218,13 @@ function FieldShell({
|
|
|
180
218
|
const s = useFieldStrings();
|
|
181
219
|
const presentation = useFieldPresentation();
|
|
182
220
|
const isApp = presentation === "app";
|
|
183
|
-
const
|
|
221
|
+
const Label3 = htmlFor ? "label" : "div";
|
|
184
222
|
const labelText = fieldLabel(title, name, presentation);
|
|
185
223
|
const showLabel = labelText.trim() !== "";
|
|
186
224
|
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
|
|
187
225
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
|
|
188
226
|
showLabel && /* @__PURE__ */ jsx(
|
|
189
|
-
|
|
227
|
+
Label3,
|
|
190
228
|
{
|
|
191
229
|
...htmlFor ? { htmlFor } : {},
|
|
192
230
|
className: cn(
|
|
@@ -197,7 +235,7 @@ function FieldShell({
|
|
|
197
235
|
}
|
|
198
236
|
),
|
|
199
237
|
!isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef, category }),
|
|
200
|
-
!required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground
|
|
238
|
+
!required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
|
|
201
239
|
] }),
|
|
202
240
|
description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: description }),
|
|
203
241
|
isApp && error ? /* @__PURE__ */ jsx("div", { className: "rounded-lg ring-1 ring-destructive/70 ring-offset-4 ring-offset-background", children }) : children,
|
|
@@ -214,6 +252,7 @@ var fieldControlClass = cn(
|
|
|
214
252
|
"aria-[invalid=true]:border-destructive aria-[invalid=true]:ring-destructive/30"
|
|
215
253
|
);
|
|
216
254
|
function DateField({ field, value, onChange, id, error, disabled }) {
|
|
255
|
+
const domId = useFieldDomId(id);
|
|
217
256
|
return /* @__PURE__ */ jsx(
|
|
218
257
|
FieldShell,
|
|
219
258
|
{
|
|
@@ -224,11 +263,11 @@ function DateField({ field, value, onChange, id, error, disabled }) {
|
|
|
224
263
|
description: field.description,
|
|
225
264
|
required: field.required,
|
|
226
265
|
error,
|
|
227
|
-
htmlFor:
|
|
266
|
+
htmlFor: domId,
|
|
228
267
|
children: /* @__PURE__ */ jsx(
|
|
229
268
|
"input",
|
|
230
269
|
{
|
|
231
|
-
id,
|
|
270
|
+
id: domId,
|
|
232
271
|
type: "date",
|
|
233
272
|
value: toDateInputValue(value),
|
|
234
273
|
disabled,
|
|
@@ -243,9 +282,7 @@ function DateField({ field, value, onChange, id, error, disabled }) {
|
|
|
243
282
|
var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
|
|
244
283
|
var PDF_EXT_RE = /\.pdf(\?|$)/i;
|
|
245
284
|
var DATA_URL_MIME_RE = /^data:([^;,]+)/i;
|
|
246
|
-
|
|
247
|
-
return !!url && (/^https?:/i.test(url) || /^data:/i.test(url) || /^blob:/i.test(url));
|
|
248
|
-
}
|
|
285
|
+
var isDirectlyViewable = isViewableUrl;
|
|
249
286
|
function dataUrlMime(url) {
|
|
250
287
|
return DATA_URL_MIME_RE.exec(url)?.[1]?.toLowerCase();
|
|
251
288
|
}
|
|
@@ -268,6 +305,7 @@ function FileField({
|
|
|
268
305
|
category
|
|
269
306
|
}) {
|
|
270
307
|
const s = useFieldStrings();
|
|
308
|
+
const domId = useFieldDomId(id);
|
|
271
309
|
const [showUrl, setShowUrl] = useState(false);
|
|
272
310
|
const [previewOpen, setPreviewOpen] = useState(false);
|
|
273
311
|
const [localPreview, setLocalPreview] = useState(null);
|
|
@@ -279,18 +317,30 @@ function FileField({
|
|
|
279
317
|
setLocalPreview(next);
|
|
280
318
|
}, []);
|
|
281
319
|
useEffect(() => () => setLocal(null), [setLocal]);
|
|
320
|
+
const [rejected, setRejected] = useState(null);
|
|
282
321
|
const handleFile = useCallback(
|
|
283
322
|
(file) => {
|
|
323
|
+
if (!isAcceptedFile(field.kind, file)) {
|
|
324
|
+
setRejected(file.name);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
setRejected(null);
|
|
284
328
|
setLocal({ objectUrl: URL.createObjectURL(file), type: file.type });
|
|
285
329
|
onDropFile(file);
|
|
286
330
|
},
|
|
287
|
-
[setLocal, onDropFile]
|
|
331
|
+
[field.kind, setLocal, onDropFile]
|
|
288
332
|
);
|
|
289
333
|
const busy = disabled || uploading;
|
|
290
334
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
291
335
|
maxFiles: 1,
|
|
292
336
|
multiple: false,
|
|
293
337
|
disabled: busy,
|
|
338
|
+
// Filters the OS picker so a wrong file is hard to choose in the first
|
|
339
|
+
// place. It is NOT the enforcement: a drag-and-drop bypasses the picker
|
|
340
|
+
// entirely, and a browser reporting an empty `File.type` slips through
|
|
341
|
+
// react-dropzone's own matcher. `handleFile` is where the answer is
|
|
342
|
+
// decided; this is the affordance in front of it.
|
|
343
|
+
accept: acceptMapForKind(field.kind),
|
|
294
344
|
// The tab stop belongs on the INPUT, not on this div - see the root element
|
|
295
345
|
// below. Without this, react-dropzone puts `tabIndex: 0` and its own key
|
|
296
346
|
// handlers on a `role="presentation"` div, and the element a keyboard or
|
|
@@ -299,12 +349,21 @@ function FileField({
|
|
|
299
349
|
onDrop: (files) => {
|
|
300
350
|
const file = files[0];
|
|
301
351
|
if (file) handleFile(file);
|
|
352
|
+
},
|
|
353
|
+
// react-dropzone rejects a file its own matcher refuses without ever calling
|
|
354
|
+
// `onDrop`, so without this a wrong drop is silently swallowed - the control
|
|
355
|
+
// simply does nothing, which reads as a broken dropzone rather than a
|
|
356
|
+
// refused file.
|
|
357
|
+
onDropRejected: (rejections) => {
|
|
358
|
+
const name = rejections[0]?.file.name;
|
|
359
|
+
if (name) setRejected(name);
|
|
302
360
|
}
|
|
303
361
|
});
|
|
304
362
|
const clear = useCallback(() => {
|
|
305
363
|
setLocal(null);
|
|
306
364
|
setResolved(null);
|
|
307
365
|
setPreviewOpen(false);
|
|
366
|
+
setRejected(null);
|
|
308
367
|
onChange(void 0);
|
|
309
368
|
}, [setLocal, onChange]);
|
|
310
369
|
const localIsCurrent = !!localPreview && (localPreview.boundUrl === void 0 || localPreview.boundUrl === value?.url);
|
|
@@ -350,7 +409,7 @@ function FileField({
|
|
|
350
409
|
description: field.description,
|
|
351
410
|
required: field.required,
|
|
352
411
|
error,
|
|
353
|
-
htmlFor:
|
|
412
|
+
htmlFor: domId,
|
|
354
413
|
children: [
|
|
355
414
|
hasFile && !uploading ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
356
415
|
/* @__PURE__ */ jsx(
|
|
@@ -388,7 +447,7 @@ function FileField({
|
|
|
388
447
|
"input",
|
|
389
448
|
{
|
|
390
449
|
...getInputProps({ tabIndex: 0 }),
|
|
391
|
-
id,
|
|
450
|
+
id: domId,
|
|
392
451
|
disabled: busy,
|
|
393
452
|
"aria-invalid": !!error
|
|
394
453
|
}
|
|
@@ -407,6 +466,12 @@ function FileField({
|
|
|
407
466
|
}
|
|
408
467
|
)
|
|
409
468
|
),
|
|
469
|
+
rejected && !busy && /* @__PURE__ */ jsxs("p", { role: "alert", className: "text-[12px] text-destructive", children: [
|
|
470
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: rejected }),
|
|
471
|
+
" \u2014",
|
|
472
|
+
" ",
|
|
473
|
+
s.unsupportedFileType(field.accept ?? "")
|
|
474
|
+
] }),
|
|
410
475
|
!showUrl ? /* @__PURE__ */ jsxs(
|
|
411
476
|
"button",
|
|
412
477
|
{
|
|
@@ -682,6 +747,7 @@ var CLEAR_VALUE = "__none__";
|
|
|
682
747
|
function EnumField({ field, value, onChange, id, error, disabled }) {
|
|
683
748
|
const s = useFieldStrings();
|
|
684
749
|
const isSegmented = field.options.length <= 4 && field.options.every((o) => o.length <= 16);
|
|
750
|
+
const domId = useFieldDomId(id);
|
|
685
751
|
return /* @__PURE__ */ jsx(
|
|
686
752
|
FieldShell,
|
|
687
753
|
{
|
|
@@ -692,7 +758,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
|
|
|
692
758
|
description: field.description,
|
|
693
759
|
required: field.required,
|
|
694
760
|
error,
|
|
695
|
-
htmlFor: isSegmented ? void 0 :
|
|
761
|
+
htmlFor: isSegmented ? void 0 : domId,
|
|
696
762
|
children: isSegmented ? (
|
|
697
763
|
// `type="single"` gives the roving tabindex and arrow-key navigation a
|
|
698
764
|
// radiogroup owes its user, and emits `''` when the active item is
|
|
@@ -728,7 +794,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
|
|
|
728
794
|
disabled,
|
|
729
795
|
onValueChange: (next) => onChange(next === CLEAR_VALUE ? void 0 : next),
|
|
730
796
|
children: [
|
|
731
|
-
/* @__PURE__ */ jsx(SelectTrigger, { id, "aria-invalid": !!error, className: "h-11", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: s.selectPlaceholder }) }),
|
|
797
|
+
/* @__PURE__ */ jsx(SelectTrigger, { id: domId, "aria-invalid": !!error, className: "h-11", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: s.selectPlaceholder }) }),
|
|
732
798
|
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
733
799
|
/* @__PURE__ */ jsx(SelectItem, { value: CLEAR_VALUE, children: s.selectPlaceholder }),
|
|
734
800
|
field.options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option }, option))
|
|
@@ -741,6 +807,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
|
|
|
741
807
|
}
|
|
742
808
|
function NumberField({ field, value, onChange, id, error, disabled }) {
|
|
743
809
|
const s = useFieldStrings();
|
|
810
|
+
const domId = useFieldDomId(id);
|
|
744
811
|
const step = field.integer ? 1 : 0.1;
|
|
745
812
|
const clamp = (n) => {
|
|
746
813
|
let next = n;
|
|
@@ -760,12 +827,12 @@ function NumberField({ field, value, onChange, id, error, disabled }) {
|
|
|
760
827
|
description: field.description ?? rangeHint,
|
|
761
828
|
required: field.required,
|
|
762
829
|
error,
|
|
763
|
-
htmlFor:
|
|
830
|
+
htmlFor: domId,
|
|
764
831
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-1.5", children: [
|
|
765
832
|
/* @__PURE__ */ jsx(
|
|
766
833
|
"input",
|
|
767
834
|
{
|
|
768
|
-
id,
|
|
835
|
+
id: domId,
|
|
769
836
|
type: "number",
|
|
770
837
|
inputMode: field.integer ? "numeric" : "decimal",
|
|
771
838
|
value: value ?? "",
|
|
@@ -811,6 +878,7 @@ function StepButton({
|
|
|
811
878
|
}
|
|
812
879
|
function TextField({ field, value, onChange, id, error, disabled }) {
|
|
813
880
|
const s = useFieldStrings();
|
|
881
|
+
const domId = useFieldDomId(id);
|
|
814
882
|
return /* @__PURE__ */ jsx(
|
|
815
883
|
FieldShell,
|
|
816
884
|
{
|
|
@@ -821,11 +889,11 @@ function TextField({ field, value, onChange, id, error, disabled }) {
|
|
|
821
889
|
description: field.description,
|
|
822
890
|
required: field.required,
|
|
823
891
|
error,
|
|
824
|
-
htmlFor:
|
|
892
|
+
htmlFor: domId,
|
|
825
893
|
children: /* @__PURE__ */ jsx(
|
|
826
894
|
"input",
|
|
827
895
|
{
|
|
828
|
-
id,
|
|
896
|
+
id: domId,
|
|
829
897
|
type: "text",
|
|
830
898
|
value: value ?? "",
|
|
831
899
|
disabled,
|
|
@@ -840,6 +908,7 @@ function TextField({ field, value, onChange, id, error, disabled }) {
|
|
|
840
908
|
}
|
|
841
909
|
function ProseField({ field, value, onChange, id, error, disabled }) {
|
|
842
910
|
const s = useFieldStrings();
|
|
911
|
+
const domId = useFieldDomId(id);
|
|
843
912
|
return /* @__PURE__ */ jsx(
|
|
844
913
|
FieldShell,
|
|
845
914
|
{
|
|
@@ -850,11 +919,11 @@ function ProseField({ field, value, onChange, id, error, disabled }) {
|
|
|
850
919
|
description: field.description,
|
|
851
920
|
required: field.required,
|
|
852
921
|
error,
|
|
853
|
-
htmlFor:
|
|
922
|
+
htmlFor: domId,
|
|
854
923
|
children: /* @__PURE__ */ jsx(
|
|
855
924
|
"textarea",
|
|
856
925
|
{
|
|
857
|
-
id,
|
|
926
|
+
id: domId,
|
|
858
927
|
value: value ?? "",
|
|
859
928
|
disabled,
|
|
860
929
|
"aria-invalid": !!error,
|
|
@@ -907,7 +976,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
|
|
|
907
976
|
}
|
|
908
977
|
),
|
|
909
978
|
!isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" }),
|
|
910
|
-
!field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground
|
|
979
|
+
!field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
|
|
911
980
|
] }),
|
|
912
981
|
field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
|
|
913
982
|
/* @__PURE__ */ jsxs(
|
|
@@ -976,7 +1045,7 @@ function ListField({ field, value, onChange, id, error, env }) {
|
|
|
976
1045
|
),
|
|
977
1046
|
!isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "list" }),
|
|
978
1047
|
/* @__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) }),
|
|
979
|
-
!field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground
|
|
1048
|
+
!field.required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
|
|
980
1049
|
] }),
|
|
981
1050
|
field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
|
|
982
1051
|
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
@@ -1073,6 +1142,7 @@ function emptyValue(field) {
|
|
|
1073
1142
|
}
|
|
1074
1143
|
function UnknownField({ field, value, onChange, id, error, disabled }) {
|
|
1075
1144
|
const s = useFieldStrings();
|
|
1145
|
+
const domId = useFieldDomId(id);
|
|
1076
1146
|
return /* @__PURE__ */ jsx(
|
|
1077
1147
|
FieldShell,
|
|
1078
1148
|
{
|
|
@@ -1083,11 +1153,11 @@ function UnknownField({ field, value, onChange, id, error, disabled }) {
|
|
|
1083
1153
|
description: field.description ?? s.jsonHint,
|
|
1084
1154
|
required: field.required,
|
|
1085
1155
|
error,
|
|
1086
|
-
htmlFor:
|
|
1156
|
+
htmlFor: domId,
|
|
1087
1157
|
children: /* @__PURE__ */ jsx(
|
|
1088
1158
|
"textarea",
|
|
1089
1159
|
{
|
|
1090
|
-
id,
|
|
1160
|
+
id: domId,
|
|
1091
1161
|
value: value ?? "",
|
|
1092
1162
|
disabled,
|
|
1093
1163
|
"aria-invalid": !!error,
|
|
@@ -1243,7 +1313,1286 @@ function FieldRenderer({ field, value, onChange, id, error, env }) {
|
|
|
1243
1313
|
);
|
|
1244
1314
|
}
|
|
1245
1315
|
}
|
|
1316
|
+
var ResultEnvContext = createContext({});
|
|
1317
|
+
function ResultEnvProvider({
|
|
1318
|
+
resolveUrl,
|
|
1319
|
+
resolveShareUrl,
|
|
1320
|
+
children
|
|
1321
|
+
}) {
|
|
1322
|
+
const env = useMemo(() => ({ resolveUrl, resolveShareUrl }), [resolveUrl, resolveShareUrl]);
|
|
1323
|
+
return /* @__PURE__ */ jsx(ResultEnvContext, { value: env, children });
|
|
1324
|
+
}
|
|
1325
|
+
function useResolvedUrl(url) {
|
|
1326
|
+
return use(ResultEnvContext).resolveUrl?.(url) ?? url;
|
|
1327
|
+
}
|
|
1328
|
+
function useResolveUrl() {
|
|
1329
|
+
return use(ResultEnvContext).resolveUrl;
|
|
1330
|
+
}
|
|
1331
|
+
function useResolveShareUrl() {
|
|
1332
|
+
return use(ResultEnvContext).resolveShareUrl;
|
|
1333
|
+
}
|
|
1334
|
+
var TooltipProvider = TooltipPrimitive.Provider;
|
|
1335
|
+
var TooltipRoot = TooltipPrimitive.Root;
|
|
1336
|
+
var TooltipTrigger = TooltipPrimitive.Trigger;
|
|
1337
|
+
var TooltipContent = React2.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
1338
|
+
TooltipPrimitive.Content,
|
|
1339
|
+
{
|
|
1340
|
+
ref,
|
|
1341
|
+
sideOffset,
|
|
1342
|
+
className: cn(
|
|
1343
|
+
"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",
|
|
1344
|
+
"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",
|
|
1345
|
+
"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",
|
|
1346
|
+
className
|
|
1347
|
+
),
|
|
1348
|
+
...props
|
|
1349
|
+
}
|
|
1350
|
+
) }));
|
|
1351
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
|
1352
|
+
var DEFAULT_IMG_SRC = "data: https:";
|
|
1353
|
+
function frameStyles(color, mutedColor, borderColor, font) {
|
|
1354
|
+
return `
|
|
1355
|
+
:root { color-scheme: inherit; }
|
|
1356
|
+
body {
|
|
1357
|
+
margin: 0;
|
|
1358
|
+
color: ${color};
|
|
1359
|
+
font-family: ${font};
|
|
1360
|
+
font-size: 13px;
|
|
1361
|
+
line-height: 1.55;
|
|
1362
|
+
background: transparent;
|
|
1363
|
+
overflow-wrap: anywhere;
|
|
1364
|
+
}
|
|
1365
|
+
:where(h1, h2, h3, h4, h5, h6) { margin: 0 0 0.4em; line-height: 1.25; }
|
|
1366
|
+
:where(h1) { font-size: 1.4em; }
|
|
1367
|
+
:where(h2) { font-size: 1.2em; }
|
|
1368
|
+
:where(h3) { font-size: 1.05em; }
|
|
1369
|
+
:where(p, ul, ol, table, pre, blockquote) { margin: 0 0 0.75em; }
|
|
1370
|
+
:where(ul, ol) { padding-inline-start: 1.25em; }
|
|
1371
|
+
:where(table) { border-collapse: collapse; width: 100%; }
|
|
1372
|
+
:where(th, td) {
|
|
1373
|
+
border: 1px solid ${borderColor};
|
|
1374
|
+
padding: 4px 8px;
|
|
1375
|
+
text-align: left;
|
|
1376
|
+
vertical-align: top;
|
|
1377
|
+
}
|
|
1378
|
+
:where(th) { font-weight: 600; }
|
|
1379
|
+
:where(caption) { caption-side: top; color: ${mutedColor}; padding-bottom: 4px; text-align: left; }
|
|
1380
|
+
:where(a) { color: inherit; }
|
|
1381
|
+
:where(code, pre) { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.92em; }
|
|
1382
|
+
:where(pre) { white-space: pre-wrap; }
|
|
1383
|
+
:where(blockquote) { border-inline-start: 2px solid ${borderColor}; margin-inline-start: 0; padding-inline-start: 0.75em; color: ${mutedColor}; }
|
|
1384
|
+
:where(img) { max-width: 100%; height: auto; }
|
|
1385
|
+
:where(hr) { border: 0; border-top: 1px solid ${borderColor}; }
|
|
1386
|
+
:where(*:last-child) { margin-bottom: 0; }
|
|
1387
|
+
`;
|
|
1388
|
+
}
|
|
1389
|
+
function frameDocument(content, styles, imgSrc) {
|
|
1390
|
+
const body = content.cssClass ? (
|
|
1391
|
+
// The class the value states, honoured the way the runtime's own HTML
|
|
1392
|
+
// rendering honours it: as a wrapper, not as something merged into ours.
|
|
1393
|
+
`<div class="${escapeAttribute(content.cssClass)}">${content.innerHtml}</div>`
|
|
1394
|
+
) : content.innerHtml;
|
|
1395
|
+
return [
|
|
1396
|
+
'<!doctype html><html><head><meta charset="utf-8">',
|
|
1397
|
+
`<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src ${imgSrc}; base-uri 'none'; form-action 'none'">`,
|
|
1398
|
+
`<style>${styles}</style></head><body>${body}</body></html>`
|
|
1399
|
+
].join("");
|
|
1400
|
+
}
|
|
1401
|
+
function escapeAttribute(value) {
|
|
1402
|
+
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
1403
|
+
}
|
|
1404
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
|
|
1405
|
+
function HtmlPreview({
|
|
1406
|
+
content,
|
|
1407
|
+
maxHeight = 1400,
|
|
1408
|
+
imgSrc = DEFAULT_IMG_SRC
|
|
1409
|
+
}) {
|
|
1410
|
+
const hostRef = useRef(null);
|
|
1411
|
+
const frameRef = useRef(null);
|
|
1412
|
+
const [doc, setDoc] = useState(null);
|
|
1413
|
+
const [height, setHeight] = useState(0);
|
|
1414
|
+
useIsomorphicLayoutEffect(() => {
|
|
1415
|
+
const host = hostRef.current;
|
|
1416
|
+
if (!host) return;
|
|
1417
|
+
const computed = getComputedStyle(host);
|
|
1418
|
+
setDoc(
|
|
1419
|
+
frameDocument(
|
|
1420
|
+
content,
|
|
1421
|
+
frameStyles(
|
|
1422
|
+
computed.color,
|
|
1423
|
+
computed.getPropertyValue("--muted-foreground").trim() || computed.color,
|
|
1424
|
+
computed.borderColor || computed.color,
|
|
1425
|
+
computed.fontFamily
|
|
1426
|
+
),
|
|
1427
|
+
imgSrc
|
|
1428
|
+
)
|
|
1429
|
+
);
|
|
1430
|
+
}, [content, imgSrc]);
|
|
1431
|
+
const measure = () => {
|
|
1432
|
+
const body = frameRef.current?.contentDocument?.body;
|
|
1433
|
+
if (body) setHeight(body.scrollHeight);
|
|
1434
|
+
};
|
|
1435
|
+
useEffect(() => {
|
|
1436
|
+
if (!doc || !hostRef.current || typeof ResizeObserver === "undefined") return;
|
|
1437
|
+
const observer = new ResizeObserver(measure);
|
|
1438
|
+
observer.observe(hostRef.current);
|
|
1439
|
+
return () => observer.disconnect();
|
|
1440
|
+
}, [doc]);
|
|
1441
|
+
return /* @__PURE__ */ jsx(
|
|
1442
|
+
"div",
|
|
1443
|
+
{
|
|
1444
|
+
ref: hostRef,
|
|
1445
|
+
className: "overflow-hidden rounded-lg border border-border bg-card/40 px-3.5 py-3 text-[13px] text-foreground",
|
|
1446
|
+
children: doc === null ? null : /* @__PURE__ */ jsx(
|
|
1447
|
+
"iframe",
|
|
1448
|
+
{
|
|
1449
|
+
ref: frameRef,
|
|
1450
|
+
title: content.cssClass ? `HTML result (${content.cssClass})` : "HTML result",
|
|
1451
|
+
srcDoc: doc,
|
|
1452
|
+
onLoad: measure,
|
|
1453
|
+
sandbox: "allow-same-origin",
|
|
1454
|
+
style: {
|
|
1455
|
+
display: "block",
|
|
1456
|
+
width: "100%",
|
|
1457
|
+
border: 0,
|
|
1458
|
+
height: height === 0 ? void 0 : Math.min(height, maxHeight),
|
|
1459
|
+
maxHeight
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
)
|
|
1463
|
+
}
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
var LEX_OPTIONS = { gfm: true, breaks: true };
|
|
1467
|
+
function Markdown({ text, className }) {
|
|
1468
|
+
const tokens = React2.useMemo(() => marked.lexer(text, LEX_OPTIONS), [text]);
|
|
1469
|
+
return /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) });
|
|
1470
|
+
}
|
|
1471
|
+
var HEADING_CLASS = {
|
|
1472
|
+
1: "text-[16px] font-semibold",
|
|
1473
|
+
2: "text-[14.5px] font-semibold",
|
|
1474
|
+
3: "text-[13.5px] font-semibold",
|
|
1475
|
+
4: "text-[13px] font-semibold",
|
|
1476
|
+
5: "text-[13px] font-semibold",
|
|
1477
|
+
6: "text-[13px] font-semibold"
|
|
1478
|
+
};
|
|
1479
|
+
function Blocks({ tokens }) {
|
|
1480
|
+
return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(Block, { token }, index)) });
|
|
1481
|
+
}
|
|
1482
|
+
function Block({ token }) {
|
|
1483
|
+
switch (token.type) {
|
|
1484
|
+
case "space":
|
|
1485
|
+
return null;
|
|
1486
|
+
case "heading": {
|
|
1487
|
+
const heading = token;
|
|
1488
|
+
const level = Math.min(Math.max(heading.depth, 1), 6);
|
|
1489
|
+
const Tag = `h${level}`;
|
|
1490
|
+
return /* @__PURE__ */ jsx(Tag, { className: cn("mt-3 first:mt-0", HEADING_CLASS[level]), children: /* @__PURE__ */ jsx(Inline, { tokens: heading.tokens }) });
|
|
1491
|
+
}
|
|
1492
|
+
case "paragraph":
|
|
1493
|
+
return /* @__PURE__ */ jsx("p", { children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
|
|
1494
|
+
// A `text` token in BLOCK position, which is what a tight list's items are
|
|
1495
|
+
// made of: the lexer wraps a loose item's content in a paragraph and a
|
|
1496
|
+
// tight one's in a bare `text`. Without this arm it fell through to the raw
|
|
1497
|
+
// fallback below and a bulleted `**Type:** …` rendered with its asterisks
|
|
1498
|
+
// showing - typeset everywhere except inside a list, which is where model
|
|
1499
|
+
// output puts most of its emphasis.
|
|
1500
|
+
case "text":
|
|
1501
|
+
return /* @__PURE__ */ jsx(Inline, { tokens: token.tokens ?? [token] });
|
|
1502
|
+
case "list": {
|
|
1503
|
+
const list = token;
|
|
1504
|
+
const Tag = list.ordered ? "ol" : "ul";
|
|
1505
|
+
return /* @__PURE__ */ jsx(
|
|
1506
|
+
Tag,
|
|
1507
|
+
{
|
|
1508
|
+
className: cn("space-y-1 ps-5", list.ordered ? "list-decimal" : "list-disc"),
|
|
1509
|
+
...list.ordered && list.start !== 1 ? { start: Number(list.start) } : {},
|
|
1510
|
+
children: list.items.map((item, index) => /* @__PURE__ */ jsxs("li", { className: "marker:text-muted-foreground", children: [
|
|
1511
|
+
item.task ? /* @__PURE__ */ jsx(
|
|
1512
|
+
"input",
|
|
1513
|
+
{
|
|
1514
|
+
type: "checkbox",
|
|
1515
|
+
checked: item.checked ?? false,
|
|
1516
|
+
disabled: true,
|
|
1517
|
+
readOnly: true,
|
|
1518
|
+
className: "me-1.5 align-middle"
|
|
1519
|
+
}
|
|
1520
|
+
) : null,
|
|
1521
|
+
/* @__PURE__ */ jsx(Blocks, { tokens: item.tokens })
|
|
1522
|
+
] }, index))
|
|
1523
|
+
}
|
|
1524
|
+
);
|
|
1525
|
+
}
|
|
1526
|
+
case "code": {
|
|
1527
|
+
const code = token;
|
|
1528
|
+
return (
|
|
1529
|
+
// WRAPPED, not scrolled. A code block in a side panel is usually not
|
|
1530
|
+
// code at all: it is a model that opened a fence and did not close it,
|
|
1531
|
+
// or closed one it never opened, and the rest of a report ends up
|
|
1532
|
+
// inside it. Scrolling that sideways hides the text behind a scrollbar
|
|
1533
|
+
// for no gain — the lines are prose, and prose wraps. Real code loses
|
|
1534
|
+
// its column alignment and keeps every character, which is the better
|
|
1535
|
+
// half of that trade at this width.
|
|
1536
|
+
/* @__PURE__ */ jsx("pre", { className: "whitespace-pre-wrap break-words rounded-md border border-border bg-card/40 p-2.5 text-[12px]", children: /* @__PURE__ */ jsx("code", { children: code.text }) })
|
|
1537
|
+
);
|
|
1538
|
+
}
|
|
1539
|
+
case "blockquote":
|
|
1540
|
+
return /* @__PURE__ */ jsx("blockquote", { className: "border-s-2 border-border ps-3 text-muted-foreground", children: /* @__PURE__ */ jsx(Blocks, { tokens: token.tokens }) });
|
|
1541
|
+
case "hr":
|
|
1542
|
+
return /* @__PURE__ */ jsx("hr", { className: "border-border" });
|
|
1543
|
+
case "table": {
|
|
1544
|
+
const table = token;
|
|
1545
|
+
return (
|
|
1546
|
+
// The same overflow rule the result tables follow: a wide table scrolls
|
|
1547
|
+
// inside its own box rather than widening the panel around it.
|
|
1548
|
+
/* @__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: [
|
|
1549
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { className: "border-b border-border bg-card/40", children: table.header.map((cell, index) => /* @__PURE__ */ jsx(
|
|
1550
|
+
"th",
|
|
1551
|
+
{
|
|
1552
|
+
scope: "col",
|
|
1553
|
+
className: "px-2.5 py-1.5 text-start font-semibold",
|
|
1554
|
+
style: alignOf(table.align[index]),
|
|
1555
|
+
children: /* @__PURE__ */ jsx(Inline, { tokens: cell.tokens })
|
|
1556
|
+
},
|
|
1557
|
+
index
|
|
1558
|
+
)) }) }),
|
|
1559
|
+
/* @__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(
|
|
1560
|
+
"td",
|
|
1561
|
+
{
|
|
1562
|
+
className: "px-2.5 py-1.5 align-top",
|
|
1563
|
+
style: alignOf(table.align[cellIndex]),
|
|
1564
|
+
children: /* @__PURE__ */ jsx(Inline, { tokens: cell.tokens })
|
|
1565
|
+
},
|
|
1566
|
+
cellIndex
|
|
1567
|
+
)) }, rowIndex)) })
|
|
1568
|
+
] }) })
|
|
1569
|
+
);
|
|
1570
|
+
}
|
|
1571
|
+
// `html` and anything this version of the lexer grows that we have no arm
|
|
1572
|
+
// for: shown as its own source text. Rendering markup we have decided not to
|
|
1573
|
+
// execute as the characters it is made of is the honest fallback - it never
|
|
1574
|
+
// silently drops content, and it never injects any.
|
|
1575
|
+
default:
|
|
1576
|
+
return /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap", children: rawOf(token) });
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
function Inline({ tokens }) {
|
|
1580
|
+
if (!tokens) return null;
|
|
1581
|
+
return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(InlineToken, { token }, index)) });
|
|
1582
|
+
}
|
|
1583
|
+
function InlineToken({ token }) {
|
|
1584
|
+
switch (token.type) {
|
|
1585
|
+
case "text": {
|
|
1586
|
+
const text = token;
|
|
1587
|
+
return text.tokens ? /* @__PURE__ */ jsx(Inline, { tokens: text.tokens }) : /* @__PURE__ */ jsx(Fragment, { children: decode(text.text) });
|
|
1588
|
+
}
|
|
1589
|
+
case "escape":
|
|
1590
|
+
return /* @__PURE__ */ jsx(Fragment, { children: token.text });
|
|
1591
|
+
case "strong":
|
|
1592
|
+
return /* @__PURE__ */ jsx("strong", { className: "font-semibold", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
|
|
1593
|
+
case "em":
|
|
1594
|
+
return /* @__PURE__ */ jsx("em", { className: "italic", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
|
|
1595
|
+
case "del":
|
|
1596
|
+
return /* @__PURE__ */ jsx("del", { className: "line-through", children: /* @__PURE__ */ jsx(Inline, { tokens: token.tokens }) });
|
|
1597
|
+
case "codespan":
|
|
1598
|
+
return /* @__PURE__ */ jsx("code", { className: "rounded border border-border bg-card/40 px-1 py-0.5 text-[0.92em]", children: decode(token.text) });
|
|
1599
|
+
case "br":
|
|
1600
|
+
return /* @__PURE__ */ jsx("br", {});
|
|
1601
|
+
case "link": {
|
|
1602
|
+
const link = token;
|
|
1603
|
+
const href = safeHref(link.href);
|
|
1604
|
+
if (!href) return /* @__PURE__ */ jsx(Inline, { tokens: link.tokens });
|
|
1605
|
+
return /* @__PURE__ */ jsx(
|
|
1606
|
+
"a",
|
|
1607
|
+
{
|
|
1608
|
+
href,
|
|
1609
|
+
target: "_blank",
|
|
1610
|
+
rel: "noreferrer noopener",
|
|
1611
|
+
className: "underline underline-offset-2",
|
|
1612
|
+
children: /* @__PURE__ */ jsx(Inline, { tokens: link.tokens })
|
|
1613
|
+
}
|
|
1614
|
+
);
|
|
1615
|
+
}
|
|
1616
|
+
case "image": {
|
|
1617
|
+
const image = token;
|
|
1618
|
+
const src = safeHref(image.href);
|
|
1619
|
+
if (!src) return /* @__PURE__ */ jsx(Fragment, { children: image.text });
|
|
1620
|
+
return /* @__PURE__ */ jsx("img", { src, alt: image.text, className: "max-w-full rounded-md" });
|
|
1621
|
+
}
|
|
1622
|
+
default:
|
|
1623
|
+
return /* @__PURE__ */ jsx(Fragment, { children: rawOf(token) });
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function safeHref(href) {
|
|
1627
|
+
const trimmed = href.trim();
|
|
1628
|
+
if (/^(https?:|mailto:)/i.test(trimmed)) return trimmed;
|
|
1629
|
+
if (/^[#/]/.test(trimmed)) return trimmed;
|
|
1630
|
+
return void 0;
|
|
1631
|
+
}
|
|
1632
|
+
function alignOf(align) {
|
|
1633
|
+
return align ? { textAlign: align } : void 0;
|
|
1634
|
+
}
|
|
1635
|
+
function rawOf(token) {
|
|
1636
|
+
return "raw" in token && typeof token.raw === "string" ? token.raw : "";
|
|
1637
|
+
}
|
|
1638
|
+
var ENTITIES = {
|
|
1639
|
+
"&": "&",
|
|
1640
|
+
"<": "<",
|
|
1641
|
+
">": ">",
|
|
1642
|
+
""": '"',
|
|
1643
|
+
"'": "'",
|
|
1644
|
+
"'": "'"
|
|
1645
|
+
};
|
|
1646
|
+
function decode(text) {
|
|
1647
|
+
return text.replace(/&(?:amp|lt|gt|quot|#39|#x27);/g, (entity) => ENTITIES[entity] ?? entity);
|
|
1648
|
+
}
|
|
1649
|
+
function unwrap(field, value) {
|
|
1650
|
+
if (!field.contentKey) return value;
|
|
1651
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return value;
|
|
1652
|
+
const unwrapped = ownProp(value, field.contentKey);
|
|
1653
|
+
return unwrapped === void 0 ? value : unwrapped;
|
|
1654
|
+
}
|
|
1655
|
+
function Described({ description, children }) {
|
|
1656
|
+
if (!description) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
1657
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs(TooltipRoot, { children: [
|
|
1658
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1659
|
+
"span",
|
|
1660
|
+
{
|
|
1661
|
+
tabIndex: 0,
|
|
1662
|
+
className: "-mx-1 cursor-help rounded px-1 outline-none transition-colors hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring",
|
|
1663
|
+
children
|
|
1664
|
+
}
|
|
1665
|
+
) }),
|
|
1666
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: description })
|
|
1667
|
+
] }) });
|
|
1668
|
+
}
|
|
1669
|
+
function ResultHeader({ field }) {
|
|
1670
|
+
return /* @__PURE__ */ jsx(Label2, { field, describe: true });
|
|
1671
|
+
}
|
|
1672
|
+
function Label2({ field, describe = false }) {
|
|
1673
|
+
const presentation = useFieldPresentation();
|
|
1674
|
+
const isApp = presentation === "app";
|
|
1675
|
+
const labelText = fieldLabel(field.title, field.name, presentation);
|
|
1676
|
+
if (labelText.trim() === "") {
|
|
1677
|
+
return /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) });
|
|
1678
|
+
}
|
|
1679
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
|
|
1680
|
+
/* @__PURE__ */ jsx(Described, { description: describe ? field.description : void 0, children: /* @__PURE__ */ jsx(
|
|
1681
|
+
"span",
|
|
1682
|
+
{
|
|
1683
|
+
className: cn(
|
|
1684
|
+
"text-[13px] font-semibold text-foreground",
|
|
1685
|
+
isApp ? "text-[13.5px]" : "font-mono"
|
|
1686
|
+
),
|
|
1687
|
+
children: labelText
|
|
1688
|
+
}
|
|
1689
|
+
) }),
|
|
1690
|
+
!isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) })
|
|
1691
|
+
] });
|
|
1692
|
+
}
|
|
1693
|
+
function Absent() {
|
|
1694
|
+
const s = useFieldStrings();
|
|
1695
|
+
return /* @__PURE__ */ jsxs("span", { className: "text-[13px] text-muted-foreground", children: [
|
|
1696
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, children: s.resultAbsent }),
|
|
1697
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: s.resultAbsentDescription })
|
|
1698
|
+
] });
|
|
1699
|
+
}
|
|
1700
|
+
function stringifyValue(value) {
|
|
1701
|
+
try {
|
|
1702
|
+
const text = JSON.stringify(
|
|
1703
|
+
value,
|
|
1704
|
+
(_key, member) => typeof member === "bigint" ? member.toString() : member
|
|
1705
|
+
);
|
|
1706
|
+
if (text === void 0 || text === "{}" || text === "[]") return void 0;
|
|
1707
|
+
return JSON.stringify(JSON.parse(text), null, 2);
|
|
1708
|
+
} catch {
|
|
1709
|
+
const keys = Array.isArray(value) ? [] : Object.keys(value);
|
|
1710
|
+
return keys.length > 0 ? `{ ${keys.join(", ")} }` : void 0;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
function RawValue({ value, compact }) {
|
|
1714
|
+
const text = stringifyValue(value);
|
|
1715
|
+
if (text === void 0) return /* @__PURE__ */ jsx(Absent, {});
|
|
1716
|
+
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 break-words rounded-md border border-border bg-card/40 px-2.5 py-1.5 font-mono text-[12px] leading-relaxed text-foreground", children: text });
|
|
1717
|
+
}
|
|
1718
|
+
function Scalar({ value, compact = false }) {
|
|
1719
|
+
if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
|
|
1720
|
+
if (typeof value === "object") return /* @__PURE__ */ jsx(RawValue, { value, compact });
|
|
1721
|
+
return /* @__PURE__ */ jsx(
|
|
1722
|
+
"span",
|
|
1723
|
+
{
|
|
1724
|
+
className: cn(
|
|
1725
|
+
"text-[13px] leading-relaxed text-foreground",
|
|
1726
|
+
compact ? "whitespace-nowrap" : "whitespace-pre-wrap"
|
|
1727
|
+
),
|
|
1728
|
+
children: String(value)
|
|
1729
|
+
}
|
|
1730
|
+
);
|
|
1731
|
+
}
|
|
1732
|
+
function Bool({ value, compact = false }) {
|
|
1733
|
+
const s = useFieldStrings();
|
|
1734
|
+
if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
|
|
1735
|
+
if (typeof value !== "boolean") return /* @__PURE__ */ jsx(Scalar, { value, compact });
|
|
1736
|
+
return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: value ? s.yes : s.no });
|
|
1737
|
+
}
|
|
1738
|
+
function DateValue({ value }) {
|
|
1739
|
+
const content = readDateContent(value);
|
|
1740
|
+
if (!content) return /* @__PURE__ */ jsx(Absent, {});
|
|
1741
|
+
return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: formatDateContent(content) });
|
|
1742
|
+
}
|
|
1743
|
+
function fileLabel(url, filename) {
|
|
1744
|
+
if (filename) return filename;
|
|
1745
|
+
const path = url.split(/[?#]/)[0] ?? url;
|
|
1746
|
+
const segment = path.split("/").filter(Boolean).pop();
|
|
1747
|
+
return segment && segment.length > 0 ? segment : url;
|
|
1748
|
+
}
|
|
1749
|
+
function CopyButton({
|
|
1750
|
+
value,
|
|
1751
|
+
provide,
|
|
1752
|
+
label,
|
|
1753
|
+
title
|
|
1754
|
+
}) {
|
|
1755
|
+
const [copied, setCopied] = useState(false);
|
|
1756
|
+
if (typeof navigator === "undefined" || !navigator.clipboard) return null;
|
|
1757
|
+
return /* @__PURE__ */ jsx(
|
|
1758
|
+
"button",
|
|
1759
|
+
{
|
|
1760
|
+
type: "button",
|
|
1761
|
+
onClick: () => {
|
|
1762
|
+
const done = () => {
|
|
1763
|
+
setCopied(true);
|
|
1764
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
1765
|
+
};
|
|
1766
|
+
if (!provide) {
|
|
1767
|
+
void navigator.clipboard.writeText(value ?? "").then(done);
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const text = provide();
|
|
1771
|
+
if (typeof ClipboardItem === "function" && "write" in navigator.clipboard) {
|
|
1772
|
+
void navigator.clipboard.write([
|
|
1773
|
+
new ClipboardItem({
|
|
1774
|
+
"text/plain": text.then((t) => new Blob([t], { type: "text/plain" }))
|
|
1775
|
+
})
|
|
1776
|
+
]).then(done, () => void text.then((t) => navigator.clipboard.writeText(t)).then(done));
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
void text.then((t) => navigator.clipboard.writeText(t)).then(done);
|
|
1780
|
+
},
|
|
1781
|
+
...title ? { title } : {},
|
|
1782
|
+
"aria-label": label,
|
|
1783
|
+
className: "shrink-0 rounded p-0.5 text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1",
|
|
1784
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
|
|
1785
|
+
}
|
|
1786
|
+
);
|
|
1787
|
+
}
|
|
1788
|
+
function CopyUrlButton({ url, storageUrl }) {
|
|
1789
|
+
const s = useFieldStrings();
|
|
1790
|
+
const resolveShare = useResolveShareUrl();
|
|
1791
|
+
const provide = async () => {
|
|
1792
|
+
if (resolveShare && storageUrl) {
|
|
1793
|
+
const shared = await resolveShare(storageUrl);
|
|
1794
|
+
if (shared) return shared;
|
|
1795
|
+
}
|
|
1796
|
+
return absoluteUrl(url);
|
|
1797
|
+
};
|
|
1798
|
+
return /* @__PURE__ */ jsx(CopyButton, { provide, label: s.copyUrl, title: url });
|
|
1799
|
+
}
|
|
1800
|
+
function absoluteUrl(url) {
|
|
1801
|
+
if (!/^\/(?!\/)/.test(url) || typeof window === "undefined") return url;
|
|
1802
|
+
return `${window.location.origin}${url}`;
|
|
1803
|
+
}
|
|
1804
|
+
function FileRef({
|
|
1805
|
+
url,
|
|
1806
|
+
storageUrl,
|
|
1807
|
+
mimeType,
|
|
1808
|
+
filename
|
|
1809
|
+
}) {
|
|
1810
|
+
const label = fileLabel(url, filename);
|
|
1811
|
+
const title = mimeType ? `${url} \xB7 ${mimeType}` : url;
|
|
1812
|
+
return /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
|
|
1813
|
+
isViewableUrl(url) ? /* @__PURE__ */ jsx(
|
|
1814
|
+
"a",
|
|
1815
|
+
{
|
|
1816
|
+
href: url,
|
|
1817
|
+
target: "_blank",
|
|
1818
|
+
rel: "noreferrer",
|
|
1819
|
+
title,
|
|
1820
|
+
className: "min-w-0 truncate font-mono text-[12px] text-foreground underline underline-offset-2",
|
|
1821
|
+
children: label
|
|
1822
|
+
}
|
|
1823
|
+
) : /* @__PURE__ */ jsx(
|
|
1824
|
+
"span",
|
|
1825
|
+
{
|
|
1826
|
+
title,
|
|
1827
|
+
className: "min-w-0 truncate font-mono text-[12px] text-muted-foreground",
|
|
1828
|
+
children: label
|
|
1829
|
+
}
|
|
1830
|
+
),
|
|
1831
|
+
/* @__PURE__ */ jsx(CopyUrlButton, { url, ...storageUrl ? { storageUrl } : {} })
|
|
1832
|
+
] });
|
|
1833
|
+
}
|
|
1834
|
+
function paintableUrl(content, resolve) {
|
|
1835
|
+
const resolved = resolve?.(content.url);
|
|
1836
|
+
if (isViewableUrl(resolved)) return resolved;
|
|
1837
|
+
if (isViewableUrl(content.publicUrl)) return content.publicUrl;
|
|
1838
|
+
return isViewableUrl(content.url) ? content.url : void 0;
|
|
1839
|
+
}
|
|
1840
|
+
var PREVIEWABLE_EXT_RE = /\.(pdf|png|jpe?g|gif|webp|avif|svg)(\?|#|$)/i;
|
|
1841
|
+
var PREVIEWABLE_MIME_RE = /^(application\/pdf|image\/)/i;
|
|
1842
|
+
function previewableUrl(content, resolve) {
|
|
1843
|
+
const url = paintableUrl(content, resolve);
|
|
1844
|
+
if (!url) return void 0;
|
|
1845
|
+
const named = content.filename ?? url;
|
|
1846
|
+
const renderable = content.mimeType ? PREVIEWABLE_MIME_RE.test(content.mimeType) : PREVIEWABLE_EXT_RE.test(named);
|
|
1847
|
+
return renderable ? url : void 0;
|
|
1848
|
+
}
|
|
1849
|
+
function DocumentPreview({ url, name }) {
|
|
1850
|
+
return /* @__PURE__ */ jsx(
|
|
1851
|
+
"iframe",
|
|
1852
|
+
{
|
|
1853
|
+
src: url,
|
|
1854
|
+
title: name,
|
|
1855
|
+
loading: "lazy",
|
|
1856
|
+
referrerPolicy: "no-referrer",
|
|
1857
|
+
className: "h-[28rem] w-full rounded-lg border border-border bg-card/40"
|
|
1858
|
+
}
|
|
1859
|
+
);
|
|
1860
|
+
}
|
|
1861
|
+
function DocumentValue({ value }) {
|
|
1862
|
+
const s = useFieldStrings();
|
|
1863
|
+
const resolve = useResolveUrl();
|
|
1864
|
+
const [open, setOpen] = useState(false);
|
|
1865
|
+
const content = readDocumentContent(value);
|
|
1866
|
+
if (!content) return /* @__PURE__ */ jsx(Absent, {});
|
|
1867
|
+
const name = content.title ?? content.filename ?? fileLabel(content.url, content.filename);
|
|
1868
|
+
const preview = previewableUrl(content, resolve);
|
|
1869
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1870
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [
|
|
1871
|
+
/* @__PURE__ */ jsx(FileText, { "aria-hidden": true, className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
1872
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
|
|
1873
|
+
name && /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] text-foreground", children: name }),
|
|
1874
|
+
/* @__PURE__ */ jsx(
|
|
1875
|
+
FileRef,
|
|
1876
|
+
{
|
|
1877
|
+
url: paintableUrl(content, resolve) ?? content.url,
|
|
1878
|
+
storageUrl: content.url,
|
|
1879
|
+
mimeType: content.mimeType,
|
|
1880
|
+
...content.filename ? { filename: content.filename } : {}
|
|
1881
|
+
}
|
|
1882
|
+
),
|
|
1883
|
+
content.snippet && /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-[12px] leading-relaxed text-muted-foreground", children: content.snippet })
|
|
1884
|
+
] }),
|
|
1885
|
+
preview && /* @__PURE__ */ jsxs(
|
|
1886
|
+
"button",
|
|
1887
|
+
{
|
|
1888
|
+
type: "button",
|
|
1889
|
+
onClick: () => setOpen((current) => !current),
|
|
1890
|
+
"aria-expanded": open,
|
|
1891
|
+
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 focus-visible:outline-2 focus-visible:outline-offset-1",
|
|
1892
|
+
children: [
|
|
1893
|
+
open ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": true, className: "size-3.5" }),
|
|
1894
|
+
s.preview
|
|
1895
|
+
]
|
|
1896
|
+
}
|
|
1897
|
+
)
|
|
1898
|
+
] }),
|
|
1899
|
+
preview && open && /* @__PURE__ */ jsx(DocumentPreview, { url: preview, name })
|
|
1900
|
+
] });
|
|
1901
|
+
}
|
|
1902
|
+
function ImageValue({
|
|
1903
|
+
value,
|
|
1904
|
+
inGallery = false,
|
|
1905
|
+
inRow = false,
|
|
1906
|
+
compact = false
|
|
1907
|
+
}) {
|
|
1908
|
+
const s = useFieldStrings();
|
|
1909
|
+
const resolve = useResolveUrl();
|
|
1910
|
+
const content = readImageContent(value);
|
|
1911
|
+
if (!content) return /* @__PURE__ */ jsx(Absent, {});
|
|
1912
|
+
const src = paintableUrl(content, resolve);
|
|
1913
|
+
return /* @__PURE__ */ jsxs("div", { className: inGallery ? "space-y-1" : "space-y-1.5", children: [
|
|
1914
|
+
src ? (
|
|
1915
|
+
// Wrapped in a link: the picture is a PREVIEW of a file, and clicking a
|
|
1916
|
+
// preview to see the thing it previews is what a reader expects. The
|
|
1917
|
+
// reference underneath is the other half - a picture with no URL beside
|
|
1918
|
+
// it is a result you can look at and cannot use.
|
|
1919
|
+
/* @__PURE__ */ jsx(
|
|
1920
|
+
"a",
|
|
1921
|
+
{
|
|
1922
|
+
href: src,
|
|
1923
|
+
target: "_blank",
|
|
1924
|
+
rel: "noreferrer",
|
|
1925
|
+
title: content.url,
|
|
1926
|
+
className: inGallery || compact ? "inline-block" : "block w-full",
|
|
1927
|
+
children: /* @__PURE__ */ jsx(
|
|
1928
|
+
LoadingImage,
|
|
1929
|
+
{
|
|
1930
|
+
src,
|
|
1931
|
+
alt: content.caption ?? s.preview,
|
|
1932
|
+
className: cn(
|
|
1933
|
+
inGallery && "block aspect-square w-full object-cover",
|
|
1934
|
+
// A cell is one line tall. An image column rendered at the standalone
|
|
1935
|
+
// height turns every row into a picture and the table into a
|
|
1936
|
+
// slideshow, so a cell gets a thumbnail and the row's expansion gets
|
|
1937
|
+
// the picture.
|
|
1938
|
+
compact && "h-10 w-auto rounded border border-border object-cover",
|
|
1939
|
+
// On its own: the full width it is given, capped generously in
|
|
1940
|
+
// height. `max-h-64` was a 256px thumbnail in a panel several
|
|
1941
|
+
// times that tall — a generated report image is the RESULT here,
|
|
1942
|
+
// not a decoration beside one, and it was unreadable at that size.
|
|
1943
|
+
// `object-contain` keeps the aspect ratio when the cap bites.
|
|
1944
|
+
!inGallery && !compact && "max-h-[44rem] w-full rounded-lg border border-border object-contain"
|
|
1945
|
+
)
|
|
1946
|
+
}
|
|
1947
|
+
)
|
|
1948
|
+
}
|
|
1949
|
+
)
|
|
1950
|
+
) : inRow ? (
|
|
1951
|
+
// The row variant of "nothing to paint": an icon and a name, exactly as
|
|
1952
|
+
// a document row reads, because that is what this has become.
|
|
1953
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2.5", children: [
|
|
1954
|
+
/* @__PURE__ */ jsx(ImageOff, { "aria-hidden": true, className: "size-4 shrink-0 text-muted-foreground" }),
|
|
1955
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
|
|
1956
|
+
FileRef,
|
|
1957
|
+
{
|
|
1958
|
+
url: content.url,
|
|
1959
|
+
mimeType: content.mimeType,
|
|
1960
|
+
...content.filename ? { filename: content.filename } : {}
|
|
1961
|
+
}
|
|
1962
|
+
) })
|
|
1963
|
+
] })
|
|
1964
|
+
) : inGallery ? (
|
|
1965
|
+
// Nothing to paint, so the tile says what it is rather than dumping a
|
|
1966
|
+
// ninety-character storage reference into a 140px box. This is what a
|
|
1967
|
+
// host with no resolver sees, so it has to be a design and not a
|
|
1968
|
+
// fallback.
|
|
1969
|
+
/* @__PURE__ */ jsxs("div", { className: "flex aspect-square w-full flex-col items-center justify-center gap-1.5 px-2 text-center", children: [
|
|
1970
|
+
/* @__PURE__ */ jsx(ImageOff, { "aria-hidden": true, className: "size-5 text-muted-foreground" }),
|
|
1971
|
+
/* @__PURE__ */ jsx(
|
|
1972
|
+
"span",
|
|
1973
|
+
{
|
|
1974
|
+
title: content.url,
|
|
1975
|
+
className: "w-full truncate font-mono text-[10.5px] text-muted-foreground",
|
|
1976
|
+
children: fileLabel(content.url, content.filename)
|
|
1977
|
+
}
|
|
1978
|
+
)
|
|
1979
|
+
] })
|
|
1980
|
+
) : /* @__PURE__ */ jsx(
|
|
1981
|
+
FileRef,
|
|
1982
|
+
{
|
|
1983
|
+
url: content.url,
|
|
1984
|
+
mimeType: content.mimeType,
|
|
1985
|
+
...content.filename ? { filename: content.filename } : {}
|
|
1986
|
+
}
|
|
1987
|
+
),
|
|
1988
|
+
src && !compact && /* @__PURE__ */ jsx("div", { className: inGallery ? "px-2.5" : void 0, children: /* @__PURE__ */ jsx(
|
|
1989
|
+
FileRef,
|
|
1990
|
+
{
|
|
1991
|
+
url: paintableUrl(content, resolve) ?? content.url,
|
|
1992
|
+
storageUrl: content.url,
|
|
1993
|
+
mimeType: content.mimeType,
|
|
1994
|
+
...content.filename ? { filename: content.filename } : {}
|
|
1995
|
+
}
|
|
1996
|
+
) }),
|
|
1997
|
+
content.caption && /* @__PURE__ */ jsx(
|
|
1998
|
+
"p",
|
|
1999
|
+
{
|
|
2000
|
+
className: cn(
|
|
2001
|
+
"text-[12px] leading-relaxed text-muted-foreground",
|
|
2002
|
+
inGallery && "px-2.5 pb-1.5"
|
|
2003
|
+
),
|
|
2004
|
+
children: content.caption
|
|
2005
|
+
}
|
|
2006
|
+
)
|
|
2007
|
+
] });
|
|
2008
|
+
}
|
|
2009
|
+
var TABULAR_KINDS = /* @__PURE__ */ new Set(["text", "number", "boolean", "date", "enum"]);
|
|
2010
|
+
function LeafValue({
|
|
2011
|
+
field,
|
|
2012
|
+
value: raw,
|
|
2013
|
+
compact = false
|
|
2014
|
+
}) {
|
|
2015
|
+
const value = unwrap(field, raw);
|
|
2016
|
+
switch (field.kind) {
|
|
2017
|
+
case "list": {
|
|
2018
|
+
const entries = Array.isArray(value) ? value : [];
|
|
2019
|
+
return TABULAR_KINDS.has(field.item.kind) ? /* @__PURE__ */ jsx(ScalarChips, { field: field.item, items: entries }) : /* @__PURE__ */ jsx(ItemCount, { count: entries.length });
|
|
2020
|
+
}
|
|
2021
|
+
case "prose":
|
|
2022
|
+
return compact ? /* @__PURE__ */ jsx(Scalar, { value, compact: true }) : typeof value === "string" && value !== "" ? /* @__PURE__ */ jsx(Markdown, { text: value }) : /* @__PURE__ */ jsx(Scalar, { value });
|
|
2023
|
+
case "boolean":
|
|
2024
|
+
return /* @__PURE__ */ jsx(Bool, { value, compact });
|
|
2025
|
+
case "date":
|
|
2026
|
+
return /* @__PURE__ */ jsx(DateValue, { value });
|
|
2027
|
+
case "document":
|
|
2028
|
+
return /* @__PURE__ */ jsx(DocumentValue, { value });
|
|
2029
|
+
case "image":
|
|
2030
|
+
return /* @__PURE__ */ jsx(ImageValue, { value, compact });
|
|
2031
|
+
default:
|
|
2032
|
+
return /* @__PURE__ */ jsx(Scalar, { value, compact });
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
function LoadingImage({ src, alt, className }) {
|
|
2036
|
+
const [state, setState] = useState("loading");
|
|
2037
|
+
const [current, setCurrent] = useState(src);
|
|
2038
|
+
if (current !== src) {
|
|
2039
|
+
setCurrent(src);
|
|
2040
|
+
setState("loading");
|
|
2041
|
+
}
|
|
2042
|
+
return /* @__PURE__ */ jsxs("span", { className: "relative block", children: [
|
|
2043
|
+
state === "loading" && /* @__PURE__ */ jsx(
|
|
2044
|
+
"span",
|
|
2045
|
+
{
|
|
2046
|
+
role: "status",
|
|
2047
|
+
"aria-label": alt,
|
|
2048
|
+
className: "absolute inset-0 flex items-center justify-center rounded-lg border border-border bg-card/40",
|
|
2049
|
+
children: /* @__PURE__ */ jsx(Loader2, { "aria-hidden": true, className: "size-4 animate-spin text-muted-foreground" })
|
|
2050
|
+
}
|
|
2051
|
+
),
|
|
2052
|
+
/* @__PURE__ */ jsx(
|
|
2053
|
+
"img",
|
|
2054
|
+
{
|
|
2055
|
+
src,
|
|
2056
|
+
alt,
|
|
2057
|
+
onLoad: () => setState("done"),
|
|
2058
|
+
onError: () => setState("done"),
|
|
2059
|
+
className: cn(className, state === "loading" && "min-h-40 opacity-0")
|
|
2060
|
+
}
|
|
2061
|
+
)
|
|
2062
|
+
] });
|
|
2063
|
+
}
|
|
2064
|
+
function ScalarChips({ field, items }) {
|
|
2065
|
+
return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: items.map((item, index) => /* @__PURE__ */ jsx(
|
|
2066
|
+
"span",
|
|
2067
|
+
{
|
|
2068
|
+
className: "rounded-md border border-border bg-card/40 px-2 py-0.5 text-[12.5px] text-foreground",
|
|
2069
|
+
children: /* @__PURE__ */ jsx(LeafValue, { field, value: item })
|
|
2070
|
+
},
|
|
2071
|
+
index
|
|
2072
|
+
)) });
|
|
2073
|
+
}
|
|
2074
|
+
function ItemCount({ count }) {
|
|
2075
|
+
const s = useFieldStrings();
|
|
2076
|
+
return /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-[12.5px] text-muted-foreground", children: s.itemsCount(count) });
|
|
2077
|
+
}
|
|
2078
|
+
function isInlineColumn(field) {
|
|
2079
|
+
if (field.kind === "list") return TABULAR_KINDS.has(field.item.kind);
|
|
2080
|
+
return TABULAR_KINDS.has(field.kind);
|
|
2081
|
+
}
|
|
2082
|
+
var CELL_LENGTH_LIMIT = 80;
|
|
2083
|
+
function fitsACellWhole(field) {
|
|
2084
|
+
switch (field.kind) {
|
|
2085
|
+
case "boolean":
|
|
2086
|
+
case "date":
|
|
2087
|
+
case "number":
|
|
2088
|
+
case "enum":
|
|
2089
|
+
return true;
|
|
2090
|
+
case "text":
|
|
2091
|
+
return field.maxLength !== void 0 && field.maxLength <= CELL_LENGTH_LIMIT;
|
|
2092
|
+
case "list":
|
|
2093
|
+
return fitsACellWhole(field.item);
|
|
2094
|
+
default:
|
|
2095
|
+
return false;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
function ObjectTable({
|
|
2099
|
+
columns,
|
|
2100
|
+
element,
|
|
2101
|
+
items,
|
|
2102
|
+
label
|
|
2103
|
+
}) {
|
|
2104
|
+
const s = useFieldStrings();
|
|
2105
|
+
const presentation = useFieldPresentation();
|
|
2106
|
+
const scrollerRef = useRef(null);
|
|
2107
|
+
const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
|
|
2108
|
+
const [viewportWidth, setViewportWidth] = useState();
|
|
2109
|
+
const canExpand = columns.some((column) => !fitsACellWhole(column));
|
|
2110
|
+
useEffect(() => {
|
|
2111
|
+
const scroller = scrollerRef.current;
|
|
2112
|
+
if (!scroller) return;
|
|
2113
|
+
const measure = () => setViewportWidth(scroller.clientWidth);
|
|
2114
|
+
measure();
|
|
2115
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
2116
|
+
const observer = new ResizeObserver(measure);
|
|
2117
|
+
observer.observe(scroller);
|
|
2118
|
+
return () => observer.disconnect();
|
|
2119
|
+
}, []);
|
|
2120
|
+
const toggle = (index) => setExpanded((current) => {
|
|
2121
|
+
const next = new Set(current);
|
|
2122
|
+
if (!next.delete(index)) next.add(index);
|
|
2123
|
+
return next;
|
|
2124
|
+
});
|
|
2125
|
+
return (
|
|
2126
|
+
// `min-w-full` rather than `w-full`, and a floor under each cell: with
|
|
2127
|
+
// `w-full` the table is pinned to the container's width, so twelve columns
|
|
2128
|
+
// do not overflow - they crush, and a reading's date wraps onto four lines.
|
|
2129
|
+
// A floor makes the table wider than the panel instead, which is what the
|
|
2130
|
+
// scroller is for.
|
|
2131
|
+
//
|
|
2132
|
+
// A scrollable region must be reachable by keyboard. A table of values has
|
|
2133
|
+
// nothing to tab to, so the region itself takes focus; without `tabIndex` the
|
|
2134
|
+
// columns past the fold are reachable with a mouse and by no other means,
|
|
2135
|
+
// which the a11y gate catches as `scrollable-region-focusable`. The name
|
|
2136
|
+
// makes the focus stop announce itself rather than being a silent one.
|
|
2137
|
+
//
|
|
2138
|
+
// `group` rather than `region`, and that is not a detail: `region` is a
|
|
2139
|
+
// LANDMARK, so every table would enter the document's landmark list, and two
|
|
2140
|
+
// of them with the same name - which is what a result rendered twice, or a
|
|
2141
|
+
// structure holding two lists, produces - is `landmark-unique`. A group is
|
|
2142
|
+
// named without claiming to be a section of the page.
|
|
2143
|
+
/* @__PURE__ */ jsx(
|
|
2144
|
+
"div",
|
|
2145
|
+
{
|
|
2146
|
+
ref: scrollerRef,
|
|
2147
|
+
role: "group",
|
|
2148
|
+
"aria-label": label,
|
|
2149
|
+
tabIndex: 0,
|
|
2150
|
+
className: "overflow-x-auto rounded-lg border border-border",
|
|
2151
|
+
children: /* @__PURE__ */ jsxs("table", { className: "min-w-full border-collapse text-[13px]", children: [
|
|
2152
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-card/40 text-left", children: [
|
|
2153
|
+
canExpand && /* @__PURE__ */ jsx("th", { scope: "col", className: "w-8 px-1", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.rowDetailsColumn }) }),
|
|
2154
|
+
columns.map((column) => /* @__PURE__ */ jsx(
|
|
2155
|
+
"th",
|
|
2156
|
+
{
|
|
2157
|
+
scope: "col",
|
|
2158
|
+
className: cn(
|
|
2159
|
+
"whitespace-nowrap px-3 py-2 text-[12px] font-semibold text-foreground",
|
|
2160
|
+
presentation === "app" ? void 0 : "font-mono"
|
|
2161
|
+
),
|
|
2162
|
+
children: /* @__PURE__ */ jsx(Described, { description: column.description, children: fieldLabel(column.title, column.name, presentation) })
|
|
2163
|
+
},
|
|
2164
|
+
column.name
|
|
2165
|
+
))
|
|
2166
|
+
] }) }),
|
|
2167
|
+
/* @__PURE__ */ jsx("tbody", { children: items.map((item, index) => {
|
|
2168
|
+
const isOpen = expanded.has(index);
|
|
2169
|
+
return /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border/60 last:border-b-0", children: [
|
|
2170
|
+
canExpand && /* @__PURE__ */ jsx("td", { className: "px-1 align-top", children: /* @__PURE__ */ jsx(
|
|
2171
|
+
"button",
|
|
2172
|
+
{
|
|
2173
|
+
type: "button",
|
|
2174
|
+
onClick: () => toggle(index),
|
|
2175
|
+
"aria-expanded": isOpen,
|
|
2176
|
+
"aria-label": s.toggleRowDetails(index + 1),
|
|
2177
|
+
className: "mt-0.5 flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-card hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1",
|
|
2178
|
+
children: isOpen ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-3.5" })
|
|
2179
|
+
}
|
|
2180
|
+
) }),
|
|
2181
|
+
isOpen ? /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "bg-card/40 p-0", children: /* @__PURE__ */ jsx(
|
|
2182
|
+
"div",
|
|
2183
|
+
{
|
|
2184
|
+
className: "sticky left-0 px-3.5 py-3",
|
|
2185
|
+
...viewportWidth ? { style: { width: viewportWidth } } : {},
|
|
2186
|
+
children: /* @__PURE__ */ jsx(ResultField, { field: element, value: item, hideLabel: true })
|
|
2187
|
+
}
|
|
2188
|
+
) }) : columns.map((column) => {
|
|
2189
|
+
const cell = typeof item === "object" && item !== null ? ownProp(item, column.name) : void 0;
|
|
2190
|
+
return /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 align-top", children: /* @__PURE__ */ jsx(
|
|
2191
|
+
"div",
|
|
2192
|
+
{
|
|
2193
|
+
className: cn(
|
|
2194
|
+
"max-w-[44ch]",
|
|
2195
|
+
column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
|
|
2196
|
+
),
|
|
2197
|
+
...typeof cell === "string" || typeof cell === "number" ? { title: String(cell) } : {},
|
|
2198
|
+
children: /* @__PURE__ */ jsx(LeafValue, { field: column, value: cell, compact: true })
|
|
2199
|
+
}
|
|
2200
|
+
) }, column.name);
|
|
2201
|
+
})
|
|
2202
|
+
] }) }, index);
|
|
2203
|
+
}) })
|
|
2204
|
+
] })
|
|
2205
|
+
}
|
|
2206
|
+
)
|
|
2207
|
+
);
|
|
2208
|
+
}
|
|
2209
|
+
function CompositeValue({ members }) {
|
|
2210
|
+
return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: members.map((member) => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2211
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[12px] font-semibold text-foreground", children: member.name }),
|
|
2212
|
+
/* @__PURE__ */ jsx(NativeValue, { value: member.value })
|
|
2213
|
+
] }, member.name)) });
|
|
2214
|
+
}
|
|
2215
|
+
function NativeValue({ value }) {
|
|
2216
|
+
if (value === null || value === void 0 || value === "") return /* @__PURE__ */ jsx(Absent, {});
|
|
2217
|
+
const html = readHtmlContent(value);
|
|
2218
|
+
if (html) return /* @__PURE__ */ jsx(HtmlPreview, { content: html });
|
|
2219
|
+
const date = readDateContent(value);
|
|
2220
|
+
if (date) return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: formatDateContent(date) });
|
|
2221
|
+
const document2 = readDocumentContent(value);
|
|
2222
|
+
if (document2?.url !== void 0) {
|
|
2223
|
+
return /* @__PURE__ */ jsx(
|
|
2224
|
+
FileRef,
|
|
2225
|
+
{
|
|
2226
|
+
url: document2.url,
|
|
2227
|
+
...document2.mimeType !== void 0 ? { mimeType: document2.mimeType } : {},
|
|
2228
|
+
...document2.filename !== void 0 ? { filename: document2.filename } : {}
|
|
2229
|
+
}
|
|
2230
|
+
);
|
|
2231
|
+
}
|
|
2232
|
+
if (isRecord(value) && Array.isArray(value.items)) {
|
|
2233
|
+
const items = value.items;
|
|
2234
|
+
if (items.length === 0) return /* @__PURE__ */ jsx(Absent, {});
|
|
2235
|
+
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)) });
|
|
2236
|
+
}
|
|
2237
|
+
if (isRecord(value)) {
|
|
2238
|
+
const text = value.text;
|
|
2239
|
+
if (typeof text === "string") {
|
|
2240
|
+
return text === "" ? /* @__PURE__ */ jsx(Absent, {}) : /* @__PURE__ */ jsx(Markdown, { text });
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (typeof value === "string") return /* @__PURE__ */ jsx(Markdown, { text: value });
|
|
2244
|
+
if (typeof value !== "object") return /* @__PURE__ */ jsx(Scalar, { value });
|
|
2245
|
+
return /* @__PURE__ */ jsx(RawValue, { value, compact: false });
|
|
2246
|
+
}
|
|
2247
|
+
function isRecord(value) {
|
|
2248
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2249
|
+
}
|
|
2250
|
+
function ScalarLines({ field, items }) {
|
|
2251
|
+
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)) });
|
|
2252
|
+
}
|
|
2253
|
+
function ImageGallery({ items }) {
|
|
2254
|
+
const anyViewable = items.some((item) => {
|
|
2255
|
+
const content = readImageContent(item);
|
|
2256
|
+
return content ? isViewableUrl(content.publicUrl) || isViewableUrl(content.url) : false;
|
|
2257
|
+
});
|
|
2258
|
+
if (!anyViewable) return /* @__PURE__ */ jsx(FileRows, { items, kind: "image" });
|
|
2259
|
+
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)) });
|
|
2260
|
+
}
|
|
2261
|
+
function FileRows({ items, kind }) {
|
|
2262
|
+
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)) });
|
|
2263
|
+
}
|
|
2264
|
+
function tableColumns(item) {
|
|
2265
|
+
if (item.kind !== "object" || item.fields.length === 0) return void 0;
|
|
2266
|
+
return item.fields;
|
|
2267
|
+
}
|
|
2268
|
+
function ResultField({ field, value, depth = 0, hideLabel = false }) {
|
|
2269
|
+
const s = useFieldStrings();
|
|
2270
|
+
const unwrapped = unwrap(field, value);
|
|
2271
|
+
const header = hideLabel ? null : /* @__PURE__ */ jsx(Label2, { field, describe: true });
|
|
2272
|
+
const copyText = (field.kind === "text" || field.kind === "prose") && typeof unwrapped === "string" && unwrapped !== "" ? unwrapped : void 0;
|
|
2273
|
+
if (isNativeDateNode(field)) {
|
|
2274
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
2275
|
+
header,
|
|
2276
|
+
/* @__PURE__ */ jsx(DateValue, { value: unwrapped })
|
|
2277
|
+
] });
|
|
2278
|
+
}
|
|
2279
|
+
if (isNativeHtmlNode(field)) {
|
|
2280
|
+
const content = readHtmlContent(unwrapped);
|
|
2281
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2282
|
+
header,
|
|
2283
|
+
content ? /* @__PURE__ */ jsx(HtmlPreview, { content }) : /* @__PURE__ */ jsx(Absent, {})
|
|
2284
|
+
] });
|
|
2285
|
+
}
|
|
2286
|
+
if (isNativeCompositeNode(field)) {
|
|
2287
|
+
const members = readCompositeContent(unwrapped);
|
|
2288
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2289
|
+
header,
|
|
2290
|
+
members && members.length > 0 ? /* @__PURE__ */ jsx(CompositeValue, { members }) : /* @__PURE__ */ jsx(Absent, {})
|
|
2291
|
+
] });
|
|
2292
|
+
}
|
|
2293
|
+
switch (field.kind) {
|
|
2294
|
+
case "object": {
|
|
2295
|
+
const memberValue = (child) => typeof unwrapped === "object" && unwrapped !== null ? ownProp(unwrapped, child.name) : void 0;
|
|
2296
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2297
|
+
header,
|
|
2298
|
+
/* @__PURE__ */ jsx(
|
|
2299
|
+
"div",
|
|
2300
|
+
{
|
|
2301
|
+
className: cn(
|
|
2302
|
+
// A definition grid, not a stack. Label above value spends two
|
|
2303
|
+
// lines on every field, so a structure of ten is twenty lines of
|
|
2304
|
+
// alternating label and answer with nothing aligned. Beside each
|
|
2305
|
+
// other they take one line and the values line up, which is what
|
|
2306
|
+
// makes a record scannable.
|
|
2307
|
+
//
|
|
2308
|
+
// The label column is CAPPED at 40%, and that is not cosmetic. A
|
|
2309
|
+
// bare `auto` sizes to the widest label in the grid - label text
|
|
2310
|
+
// plus its concept pill - so one long field name starved every
|
|
2311
|
+
// value beside it, and inside a 400px panel a paragraph came out
|
|
2312
|
+
// as a ten-line ribbon in a 130px column. `min-content` keeps a
|
|
2313
|
+
// short label short; the cap stops any label taking more than its
|
|
2314
|
+
// share, whatever the panel width turns out to be.
|
|
2315
|
+
"grid grid-cols-[minmax(min-content,40%)_minmax(0,1fr)] items-baseline gap-x-3 gap-y-1.5",
|
|
2316
|
+
// A list item already sits in its own bordered row; nesting a
|
|
2317
|
+
// second card inside it draws a box in a box for no information
|
|
2318
|
+
// gained.
|
|
2319
|
+
!hideLabel && "rounded-lg border border-border bg-card/40 px-3.5 py-3"
|
|
2320
|
+
),
|
|
2321
|
+
children: field.fields.map(
|
|
2322
|
+
(child) => (
|
|
2323
|
+
// Only a SHORT value shares a line. Prose needs the full width, and
|
|
2324
|
+
// so does anything with chrome of its own - a table, a gallery, a
|
|
2325
|
+
// frame - which beside a label would be squeezed into whatever the
|
|
2326
|
+
// longest label left over.
|
|
2327
|
+
isInlineColumn(child) ? /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
2328
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx(Label2, { field: child, describe: true }) }),
|
|
2329
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0", children: /* @__PURE__ */ jsx(LeafValue, { field: child, value: memberValue(child) }) })
|
|
2330
|
+
] }, 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)
|
|
2331
|
+
)
|
|
2332
|
+
)
|
|
2333
|
+
}
|
|
2334
|
+
)
|
|
2335
|
+
] });
|
|
2336
|
+
}
|
|
2337
|
+
case "list": {
|
|
2338
|
+
const items = Array.isArray(unwrapped) ? unwrapped : [];
|
|
2339
|
+
const columns = tableColumns(field.item);
|
|
2340
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2341
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
|
|
2342
|
+
header,
|
|
2343
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: s.itemsCount(items.length) })
|
|
2344
|
+
] }),
|
|
2345
|
+
items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[13px] italic text-muted-foreground", children: s.noItemsYet }) : columns ? (
|
|
2346
|
+
// Same shape every row: the labels are column headers, not per-row
|
|
2347
|
+
// labels. See `ObjectTable`.
|
|
2348
|
+
/* @__PURE__ */ jsx(
|
|
2349
|
+
ObjectTable,
|
|
2350
|
+
{
|
|
2351
|
+
columns,
|
|
2352
|
+
element: field.item,
|
|
2353
|
+
items,
|
|
2354
|
+
label: field.title ?? humanizeFieldName(field.name)
|
|
2355
|
+
}
|
|
2356
|
+
)
|
|
2357
|
+
) : TABULAR_KINDS.has(field.item.kind) ? /* @__PURE__ */ jsx(ScalarChips, { field: field.item, items }) : field.item.kind === "prose" || field.item.kind === "unknown" ? (
|
|
2358
|
+
// `unknown` shares the LINES layout, and that is a reading of the
|
|
2359
|
+
// standard rather than a convenience. `unknown` is the deliberate
|
|
2360
|
+
// escape hatch: the producer could not map the node honestly and
|
|
2361
|
+
// said so, which is what an untyped `type = "list"` field derives
|
|
2362
|
+
// to. A card per entry would spend a bordered box and an index on
|
|
2363
|
+
// each value to say nothing extra - the card earns its place around
|
|
2364
|
+
// a STRUCTURE, and this is precisely the node we have been told is
|
|
2365
|
+
// not known to be one. Lines cost nothing and read the same whether
|
|
2366
|
+
// the entries turn out to be words or objects, because `LeafValue`
|
|
2367
|
+
// falls through to `RawValue` either way.
|
|
2368
|
+
/* @__PURE__ */ jsx(ScalarLines, { field: field.item, items })
|
|
2369
|
+
) : field.item.kind === "image" ? /* @__PURE__ */ jsx(ImageGallery, { items }) : field.item.kind === "document" ? /* @__PURE__ */ jsx(FileRows, { items, kind: "document" }) : (
|
|
2370
|
+
// Nested, or carrying prose: a card per entry is what a table cell
|
|
2371
|
+
// cannot hold. The index label earns its place here, where the
|
|
2372
|
+
// entries are structures rather than values.
|
|
2373
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: items.map((item, index) => /* @__PURE__ */ jsxs(
|
|
2374
|
+
"div",
|
|
2375
|
+
{
|
|
2376
|
+
className: "space-y-2 rounded-lg border border-border bg-card/40 px-3.5 py-3",
|
|
2377
|
+
children: [
|
|
2378
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] text-muted-foreground", children: index + 1 }),
|
|
2379
|
+
/* @__PURE__ */ jsx(ResultField, { field: field.item, value: item, depth: depth + 1, hideLabel: true })
|
|
2380
|
+
]
|
|
2381
|
+
},
|
|
2382
|
+
index
|
|
2383
|
+
)) })
|
|
2384
|
+
)
|
|
2385
|
+
] });
|
|
2386
|
+
}
|
|
2387
|
+
case "boolean":
|
|
2388
|
+
case "date":
|
|
2389
|
+
case "document":
|
|
2390
|
+
case "image":
|
|
2391
|
+
case "text":
|
|
2392
|
+
case "prose":
|
|
2393
|
+
case "number":
|
|
2394
|
+
case "enum":
|
|
2395
|
+
case "unknown":
|
|
2396
|
+
return /* @__PURE__ */ jsxs("div", { className: field.kind === "image" ? "space-y-1.5" : "space-y-0.5", children: [
|
|
2397
|
+
copyText === void 0 ? header : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
2398
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
|
|
2399
|
+
/* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText })
|
|
2400
|
+
] }),
|
|
2401
|
+
/* @__PURE__ */ jsx(LeafValue, { field, value })
|
|
2402
|
+
] });
|
|
2403
|
+
}
|
|
2404
|
+
return null;
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
// src/react/download-stuff.ts
|
|
2408
|
+
function saveBlob(blob, filename) {
|
|
2409
|
+
const href = URL.createObjectURL(blob);
|
|
2410
|
+
const anchor = document.createElement("a");
|
|
2411
|
+
anchor.href = href;
|
|
2412
|
+
anchor.download = filename;
|
|
2413
|
+
document.body.appendChild(anchor);
|
|
2414
|
+
anchor.click();
|
|
2415
|
+
anchor.remove();
|
|
2416
|
+
setTimeout(() => URL.revokeObjectURL(href), 0);
|
|
2417
|
+
}
|
|
2418
|
+
function mimeForExtension(extension) {
|
|
2419
|
+
return extension === "html" ? "text/html;charset=utf-8" : "text/plain;charset=utf-8";
|
|
2420
|
+
}
|
|
2421
|
+
function extensionOf(url, filename) {
|
|
2422
|
+
const named = filename?.match(/\.([A-Za-z0-9]{1,8})$/)?.[1];
|
|
2423
|
+
if (named) return named.toLowerCase();
|
|
2424
|
+
const path = url.split(/[?#]/)[0] ?? url;
|
|
2425
|
+
const match = path.match(/\.([A-Za-z0-9]{1,8})$/)?.[1];
|
|
2426
|
+
return match ? match.toLowerCase() : "";
|
|
2427
|
+
}
|
|
2428
|
+
function fileNameFor(file, base) {
|
|
2429
|
+
if (file.filename) return file.filename;
|
|
2430
|
+
const extension = file.extension ?? extensionOf(file.url ?? "", file.filename);
|
|
2431
|
+
const stem = `${base}-${file.path.replace(/\./g, "-")}`;
|
|
2432
|
+
return extension ? `${stem}.${extension}` : stem;
|
|
2433
|
+
}
|
|
2434
|
+
async function downloadStuff({
|
|
2435
|
+
field,
|
|
2436
|
+
value,
|
|
2437
|
+
baseName,
|
|
2438
|
+
resolveUrl
|
|
2439
|
+
}) {
|
|
2440
|
+
const files = collectStuffFiles(field, value);
|
|
2441
|
+
const isBareFile = files.length === 1 && files[0]?.path === field.name;
|
|
2442
|
+
for (const file of files) {
|
|
2443
|
+
const name = fileNameFor(file, baseName);
|
|
2444
|
+
if (file.text !== void 0) {
|
|
2445
|
+
saveBlob(new Blob([file.text], { type: mimeForExtension(file.extension) }), name);
|
|
2446
|
+
continue;
|
|
2447
|
+
}
|
|
2448
|
+
const href = resolveUrl?.(file.url ?? "") ?? file.publicUrl ?? file.url;
|
|
2449
|
+
if (!href) continue;
|
|
2450
|
+
try {
|
|
2451
|
+
const response = await fetch(href);
|
|
2452
|
+
if (!response.ok) throw new Error(String(response.status));
|
|
2453
|
+
saveBlob(await response.blob(), name);
|
|
2454
|
+
} catch {
|
|
2455
|
+
window.open(href, "_blank", "noopener");
|
|
2456
|
+
}
|
|
2457
|
+
}
|
|
2458
|
+
if (isBareFile) return;
|
|
2459
|
+
saveBlob(
|
|
2460
|
+
new Blob([JSON.stringify(value, null, 2)], { type: "application/json;charset=utf-8" }),
|
|
2461
|
+
`${baseName}.json`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
var JSON_TOKEN_RE = /("(?:\\.|[^"\\])*"\s*:)|("(?:\\.|[^"\\])*")|(\b(?:true|false|null)\b|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;
|
|
2465
|
+
function highlight(json) {
|
|
2466
|
+
const out = [];
|
|
2467
|
+
let last = 0;
|
|
2468
|
+
for (const match of json.matchAll(JSON_TOKEN_RE)) {
|
|
2469
|
+
const [token, key, str, literal] = match;
|
|
2470
|
+
const at = match.index;
|
|
2471
|
+
if (at > last) {
|
|
2472
|
+
out.push(
|
|
2473
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: json.slice(last, at) }, `p${last}`)
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
out.push(
|
|
2477
|
+
/* @__PURE__ */ jsx(
|
|
2478
|
+
"span",
|
|
2479
|
+
{
|
|
2480
|
+
className: key ? "text-muted-foreground" : str ? "text-foreground" : "font-semibold text-foreground",
|
|
2481
|
+
children: token
|
|
2482
|
+
},
|
|
2483
|
+
at
|
|
2484
|
+
)
|
|
2485
|
+
);
|
|
2486
|
+
last = at + token.length;
|
|
2487
|
+
}
|
|
2488
|
+
if (last < json.length) {
|
|
2489
|
+
out.push(
|
|
2490
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: json.slice(last) }, "tail")
|
|
2491
|
+
);
|
|
2492
|
+
}
|
|
2493
|
+
return out;
|
|
2494
|
+
}
|
|
2495
|
+
function JsonView({ value }) {
|
|
2496
|
+
const s = useFieldStrings();
|
|
2497
|
+
const [copied, setCopied] = useState(false);
|
|
2498
|
+
const text = value === null || value === void 0 ? void 0 : typeof value === "object" ? stringifyValue(value) : JSON.stringify(value);
|
|
2499
|
+
if (text === void 0) {
|
|
2500
|
+
return /* @__PURE__ */ jsx(Absent, {});
|
|
2501
|
+
}
|
|
2502
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2503
|
+
typeof navigator !== "undefined" && navigator.clipboard && /* @__PURE__ */ jsx(
|
|
2504
|
+
"button",
|
|
2505
|
+
{
|
|
2506
|
+
type: "button",
|
|
2507
|
+
onClick: () => {
|
|
2508
|
+
void navigator.clipboard.writeText(text).then(() => {
|
|
2509
|
+
setCopied(true);
|
|
2510
|
+
window.setTimeout(() => setCopied(false), 1500);
|
|
2511
|
+
});
|
|
2512
|
+
},
|
|
2513
|
+
"aria-label": s.copyJson,
|
|
2514
|
+
className: "absolute right-2 top-2 rounded border border-border bg-card p-1 text-muted-foreground hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1",
|
|
2515
|
+
children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
|
|
2516
|
+
}
|
|
2517
|
+
),
|
|
2518
|
+
/* @__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) })
|
|
2519
|
+
] });
|
|
2520
|
+
}
|
|
2521
|
+
function StuffViewer({
|
|
2522
|
+
field,
|
|
2523
|
+
value,
|
|
2524
|
+
defaultView = "rendered",
|
|
2525
|
+
downloadBaseName,
|
|
2526
|
+
hideDownload = false,
|
|
2527
|
+
className
|
|
2528
|
+
}) {
|
|
2529
|
+
const s = useFieldStrings();
|
|
2530
|
+
const [view, setView] = useState(defaultView);
|
|
2531
|
+
const [saving, setSaving] = useState(false);
|
|
2532
|
+
const resolveUrl = useResolveUrl();
|
|
2533
|
+
const handleDownload = useCallback(async () => {
|
|
2534
|
+
setSaving(true);
|
|
2535
|
+
try {
|
|
2536
|
+
await downloadStuff({
|
|
2537
|
+
field,
|
|
2538
|
+
value,
|
|
2539
|
+
baseName: downloadBaseName ?? field.name ?? "result",
|
|
2540
|
+
resolveUrl
|
|
2541
|
+
});
|
|
2542
|
+
} finally {
|
|
2543
|
+
setSaving(false);
|
|
2544
|
+
}
|
|
2545
|
+
}, [field, value, downloadBaseName, resolveUrl]);
|
|
2546
|
+
const views = [
|
|
2547
|
+
{ id: "rendered", label: s.viewRendered },
|
|
2548
|
+
{ id: "json", label: s.viewJson }
|
|
2549
|
+
];
|
|
2550
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
|
|
2551
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-start justify-between gap-x-4 gap-y-1", children: [
|
|
2552
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-1", children: /* @__PURE__ */ jsx(ResultHeader, { field }) }),
|
|
2553
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
2554
|
+
!hideDownload && /* @__PURE__ */ jsxs(
|
|
2555
|
+
"button",
|
|
2556
|
+
{
|
|
2557
|
+
type: "button",
|
|
2558
|
+
onClick: () => void handleDownload(),
|
|
2559
|
+
disabled: saving,
|
|
2560
|
+
"aria-label": s.download,
|
|
2561
|
+
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 focus-visible:outline-2 focus-visible:outline-offset-1 disabled:opacity-60",
|
|
2562
|
+
children: [
|
|
2563
|
+
saving ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }),
|
|
2564
|
+
saving ? s.downloading : s.download
|
|
2565
|
+
]
|
|
2566
|
+
}
|
|
2567
|
+
),
|
|
2568
|
+
/* @__PURE__ */ jsx(
|
|
2569
|
+
"div",
|
|
2570
|
+
{
|
|
2571
|
+
role: "group",
|
|
2572
|
+
"aria-label": s.resultViewGroup,
|
|
2573
|
+
className: "flex rounded-md border border-border p-0.5",
|
|
2574
|
+
children: views.map(({ id, label }) => /* @__PURE__ */ jsx(
|
|
2575
|
+
"button",
|
|
2576
|
+
{
|
|
2577
|
+
type: "button",
|
|
2578
|
+
onClick: () => setView(id),
|
|
2579
|
+
"aria-pressed": view === id,
|
|
2580
|
+
className: cn(
|
|
2581
|
+
"rounded px-2 py-0.5 text-[12px] focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-1",
|
|
2582
|
+
view === id ? "bg-card font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
2583
|
+
),
|
|
2584
|
+
children: label
|
|
2585
|
+
},
|
|
2586
|
+
id
|
|
2587
|
+
))
|
|
2588
|
+
}
|
|
2589
|
+
)
|
|
2590
|
+
] })
|
|
2591
|
+
] }),
|
|
2592
|
+
view === "json" ? /* @__PURE__ */ jsx(JsonView, { value }) : /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: true })
|
|
2593
|
+
] });
|
|
2594
|
+
}
|
|
1246
2595
|
|
|
1247
|
-
export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EnumField, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, ImageField, ListField, NumberField, ObjectField, OptionalToggle, ProseField, TextField, UnknownField, fieldControlClass, humanizeFieldName, useFieldPresentation, useFieldStrings };
|
|
2596
|
+
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, StuffViewer, TextField, UnknownField, downloadStuff, fieldControlClass, humanizeFieldName, useFieldDomId, useFieldPresentation, useFieldStrings, useResolveShareUrl, useResolveUrl, useResolvedUrl };
|
|
1248
2597
|
//# sourceMappingURL=index.js.map
|
|
1249
2598
|
//# sourceMappingURL=index.js.map
|