@pipelex/mthds-form 0.9.0 → 0.10.1

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 (50) hide show
  1. package/README.md +3 -0
  2. package/dist/{chunk-S4CB423P.js → chunk-4JQAJT7C.js} +3 -3
  3. package/dist/{chunk-S4CB423P.js.map → chunk-4JQAJT7C.js.map} +1 -1
  4. package/dist/{chunk-YABRYTQ4.js → chunk-4KGQ65TQ.js} +3 -3
  5. package/dist/{chunk-YABRYTQ4.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-25I6JDIK.js → chunk-FABY3DEY.js} +2 -2
  9. package/dist/chunk-FABY3DEY.js.map +1 -0
  10. package/dist/{chunk-AZZAAGNX.js → chunk-TID7ZN63.js} +7 -3
  11. package/dist/chunk-TID7ZN63.js.map +1 -0
  12. package/dist/{chunk-KG4IYFWI.js → chunk-W56WIIVC.js} +174 -51
  13. package/dist/chunk-W56WIIVC.js.map +1 -0
  14. package/dist/chunk-XCO6GTEC.js +38 -0
  15. package/dist/chunk-XCO6GTEC.js.map +1 -0
  16. package/dist/{chunk-KIEOLP2L.js → chunk-ZB4RYUKV.js} +4 -4
  17. package/dist/chunk-ZB4RYUKV.js.map +1 -0
  18. package/dist/core/derive.d.ts +1 -0
  19. package/dist/core/derive.js +2 -2
  20. package/dist/core/descriptor.d.ts +13 -2
  21. package/dist/core/descriptor.js +1 -1
  22. package/dist/core/file-formats.d.ts +34 -7
  23. package/dist/core/file-formats.js +1 -1
  24. package/dist/core/gating-fields.d.ts +1 -0
  25. package/dist/core/index.d.ts +2 -1
  26. package/dist/core/index.js +7 -6
  27. package/dist/core/narrow-file-formats.d.ts +50 -0
  28. package/dist/core/narrow-file-formats.js +4 -0
  29. package/dist/core/narrow-file-formats.js.map +1 -0
  30. package/dist/core/native-content.d.ts +20 -11
  31. package/dist/core/native-content.js +1 -1
  32. package/dist/core/readiness.d.ts +1 -0
  33. package/dist/core/stuff-files.d.ts +1 -0
  34. package/dist/core/stuff-files.js +2 -2
  35. package/dist/core/values.d.ts +1 -0
  36. package/dist/core/values.js +3 -3
  37. package/dist/generative/index.d.ts +105 -12
  38. package/dist/generative/index.js +79 -157
  39. package/dist/generative/index.js.map +1 -1
  40. package/dist/{ui-designer.mthds → layout-design.mthds} +177 -29
  41. package/dist/react/index.d.ts +66 -10
  42. package/dist/react/index.js +5 -5
  43. package/dist/styles.css +1 -1
  44. package/package.json +3 -3
  45. package/dist/chunk-25I6JDIK.js.map +0 -1
  46. package/dist/chunk-2JWRG53S.js +0 -33
  47. package/dist/chunk-2JWRG53S.js.map +0 -1
  48. package/dist/chunk-AZZAAGNX.js.map +0 -1
  49. package/dist/chunk-KG4IYFWI.js.map +0 -1
  50. package/dist/chunk-KIEOLP2L.js.map +0 -1
@@ -1,7 +1,7 @@
1
- import { collectStuffFiles } from './chunk-S4CB423P.js';
2
- import { viewableUrl, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, readDataUrl, readDateContent, formatDateContent, readImageContent, readDocumentContent } from './chunk-AZZAAGNX.js';
3
- import { conceptCategory } from './chunk-25I6JDIK.js';
4
- import { isAcceptedFile, acceptMapForKind, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-2JWRG53S.js';
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
5
  import { isFilled } from './chunk-PM55AHRO.js';
6
6
  import { toStoredDateValue, toDateInputValue } from './chunk-GKV77RAS.js';
7
7
  import { ownProp } from './chunk-V3OV2XQT.js';
@@ -66,6 +66,39 @@ function humanizeFieldName(name) {
66
66
  if (words === "") return name;
67
67
  return words.charAt(0).toUpperCase() + words.slice(1);
68
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();
69
102
  function fieldLabel(title, name, presentation) {
70
103
  if (title !== void 0) return title;
71
104
  return presentation === "app" ? humanizeFieldName(name) : name;
@@ -170,8 +203,9 @@ var DEFAULT_FIELD_STRINGS = {
170
203
  dropToUpload: "Drop to upload",
171
204
  dropOrBrowse: "Drop a file or click to browse",
172
205
  pasteUrlInstead: "paste a URL instead",
173
- urlPlaceholder: "https://\u2026 or pipelex-storage://\u2026",
174
- uploadedFile: "Uploaded file",
206
+ urlPlaceholder: "https://\u2026",
207
+ fileUrlAria: (label) => label ? `Link to the file for ${label}` : "Link to the file",
208
+ uploadedFile: "Attached file",
175
209
  encodedFileSummary: (format, bytes) => `${format} \xB7 ${formatBytes(bytes)}`,
176
210
  preview: "Preview",
177
211
  removeFileAria: "Remove file",
@@ -192,7 +226,7 @@ var DEFAULT_FIELD_STRINGS = {
192
226
  resultViewGroup: "Result view",
193
227
  yes: "Yes",
194
228
  no: "No",
195
- unsupportedFileType: (accept) => `That file type is not supported. Accepted formats: ${accept}.`,
229
+ unsupportedFileType: (formats) => `That file type is not supported. Accepted formats: ${formats}.`,
196
230
  hideOptionalFields: "Hide optional fields",
197
231
  hideOptionalInputs: "Hide optional inputs",
198
232
  optionalFieldsCount: (count) => count === 1 ? "1 optional field" : `${count} optional fields`,
@@ -296,6 +330,11 @@ function encodedFileSummary(url, strings) {
296
330
  var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
297
331
  var PDF_EXT_RE = /\.pdf(\?|$)/i;
298
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
+ }
299
338
  function dataUrlMime(url) {
300
339
  return DATA_URL_MIME_RE.exec(url)?.[1]?.toLowerCase();
301
340
  }
@@ -318,8 +357,10 @@ function FileField({
318
357
  category
319
358
  }) {
320
359
  const s = useFieldStrings();
360
+ const presentation = useFieldPresentation();
321
361
  const domId = useFieldDomId(id);
322
362
  const [showUrl, setShowUrl] = useState(false);
363
+ const [typedUrl, setTypedUrl] = useState(null);
323
364
  const [previewOpen, setPreviewOpen] = useState(false);
324
365
  const [localPreview, setLocalPreview] = useState(null);
325
366
  const [resolved, setResolved] = useState(null);
@@ -331,9 +372,12 @@ function FileField({
331
372
  }, []);
332
373
  useEffect(() => () => setLocal(null), [setLocal]);
333
374
  const [rejected, setRejected] = useState(null);
375
+ const formats = field.formats;
376
+ const hint = acceptLabel(formats);
377
+ const accept = useMemo(() => acceptMap(formats), [formats]);
334
378
  const handleFile = useCallback(
335
379
  (file) => {
336
- if (!isAcceptedFile(field.kind, file)) {
380
+ if (!isAcceptedFile(formats, file)) {
337
381
  setRejected(file.name);
338
382
  return;
339
383
  }
@@ -341,7 +385,7 @@ function FileField({
341
385
  setLocal({ objectUrl: URL.createObjectURL(file), type: file.type });
342
386
  onDropFile(file);
343
387
  },
344
- [field.kind, setLocal, onDropFile]
388
+ [formats, setLocal, onDropFile]
345
389
  );
346
390
  const busy = disabled || uploading;
347
391
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
@@ -353,7 +397,7 @@ function FileField({
353
397
  // entirely, and a browser reporting an empty `File.type` slips through
354
398
  // react-dropzone's own matcher. `handleFile` is where the answer is
355
399
  // decided; this is the affordance in front of it.
356
- accept: acceptMapForKind(field.kind),
400
+ accept,
357
401
  // The tab stop belongs on the INPUT, not on this div - see the root element
358
402
  // below. Without this, react-dropzone puts `tabIndex: 0` and its own key
359
403
  // handlers on a `role="presentation"` div, and the element a keyboard or
@@ -403,17 +447,23 @@ function FileField({
403
447
  if (!previewOpen || !storageUri || localIsCurrent || !resolveUrl) return;
404
448
  let cancelled = false;
405
449
  void resolveUrl(storageUri).then((src) => {
406
- if (!cancelled) setResolved(src ? { uri: storageUri, src } : null);
450
+ if (!cancelled) setResolved({ uri: storageUri, src: src || null });
407
451
  }).catch(() => {
408
- if (!cancelled) setResolved(null);
452
+ if (!cancelled) setResolved({ uri: storageUri, src: null });
409
453
  });
410
454
  return () => {
411
455
  cancelled = true;
412
456
  };
413
457
  }, [previewOpen, storageUri, localIsCurrent, resolveUrl]);
414
- const resolvedSrc = resolved && resolved.uri === storageUri ? viewableUrl(resolved.src) : void 0;
458
+ const resolvedSrc = resolved && resolved.uri === storageUri ? viewableUrl(resolved.src ?? void 0) : void 0;
415
459
  const previewSrc = (localIsCurrent ? localPreview?.objectUrl : void 0) ?? directSrc ?? resolvedSrc;
416
- const previewPending = !previewSrc && !!storageUri && resolved?.uri !== storageUri;
460
+ const previewPending = !previewSrc && !!storageUri && !!resolveUrl && resolved?.uri !== storageUri;
461
+ const togglePreview = () => {
462
+ if (!previewOpen) setResolved((r) => r?.src === null ? null : r);
463
+ setPreviewOpen((v) => !v);
464
+ };
465
+ const urlValue = value?.url ?? "";
466
+ const urlText = presentation === "studio" || isReadableLink(urlValue) || urlValue === typedUrl ? urlValue : "";
417
467
  return /* @__PURE__ */ jsxs(
418
468
  FieldShell,
419
469
  {
@@ -431,11 +481,13 @@ function FileField({
431
481
  FileChip,
432
482
  {
433
483
  value,
484
+ formats,
485
+ presentation,
434
486
  disabled,
435
487
  onClear: clear,
436
488
  canPreview,
437
489
  previewOpen,
438
- onTogglePreview: () => setPreviewOpen((v) => !v)
490
+ onTogglePreview: togglePreview
439
491
  }
440
492
  ),
441
493
  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" }) }) : (
@@ -480,7 +532,7 @@ function FileField({
480
532
  /* @__PURE__ */ jsx(Upload, { className: "h-5 w-5 text-muted-foreground", strokeWidth: 1.75 }),
481
533
  /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
482
534
  /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropToUpload : s.dropOrBrowse }),
483
- field.accept && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: field.accept })
535
+ hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
484
536
  ] })
485
537
  ] })
486
538
  ]
@@ -489,9 +541,8 @@ function FileField({
489
541
  ),
490
542
  rejected && !busy && /* @__PURE__ */ jsxs("p", { role: "alert", className: "text-[12px] text-destructive", children: [
491
543
  /* @__PURE__ */ jsx("span", { className: "font-mono", children: rejected }),
492
- " \u2014",
493
- " ",
494
- s.unsupportedFileType(field.accept ?? "")
544
+ " \u2014 ",
545
+ s.unsupportedFileType(hint)
495
546
  ] }),
496
547
  !showUrl ? /* @__PURE__ */ jsxs(
497
548
  "button",
@@ -510,11 +561,13 @@ function FileField({
510
561
  {
511
562
  type: "text",
512
563
  autoFocus: true,
513
- value: value?.url ?? "",
564
+ value: urlText,
514
565
  disabled: busy,
566
+ "aria-label": s.fileUrlAria(fieldLabel(field.title, field.name, presentation)),
515
567
  placeholder: s.urlPlaceholder,
516
568
  onChange: (e) => {
517
569
  setLocal(null);
570
+ setTypedUrl(e.target.value);
518
571
  onChange(e.target.value ? { url: e.target.value } : void 0);
519
572
  },
520
573
  className: cn(fieldControlClass, "h-9 px-3 font-mono text-[12px]")
@@ -556,8 +609,17 @@ function PdfPreview({ src }) {
556
609
  }
557
610
  );
558
611
  }
612
+ function fileChipSubtitle(url, filename, formats, presentation, strings) {
613
+ if (url === void 0) return void 0;
614
+ const summary = encodedFileSummary(url, strings);
615
+ if (summary !== void 0) return summary;
616
+ if (presentation === "studio" || isReadableLink(url)) return url;
617
+ return filename ? formatOfFilename(formats, filename)?.label : void 0;
618
+ }
559
619
  function FileChip({
560
620
  value,
621
+ formats,
622
+ presentation,
561
623
  disabled,
562
624
  onClear,
563
625
  canPreview,
@@ -566,15 +628,16 @@ function FileChip({
566
628
  }) {
567
629
  const s = useFieldStrings();
568
630
  const url = value?.url;
631
+ const filename = value?.filename;
569
632
  const subtitle = useMemo(
570
- () => url === void 0 ? void 0 : encodedFileSummary(url, s) ?? url,
571
- [url, s]
633
+ () => fileChipSubtitle(url, filename, formats, presentation, s),
634
+ [url, filename, formats, presentation, s]
572
635
  );
573
636
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 rounded-md border border-border bg-input px-3 py-2.5", children: [
574
637
  /* @__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" }) }),
575
638
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
576
639
  /* @__PURE__ */ jsx("p", { className: "truncate text-[13px] font-medium text-foreground", children: value?.filename ?? s.uploadedFile }),
577
- /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[10.5px] text-muted-foreground", children: subtitle })
640
+ subtitle && /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[10.5px] text-muted-foreground", children: subtitle })
578
641
  ] }),
579
642
  canPreview && /* @__PURE__ */ jsxs(
580
643
  "button",
@@ -773,7 +836,9 @@ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
773
836
  var CLEAR_VALUE = "__none__";
774
837
  function EnumField({ field, value, onChange, id, error, disabled }) {
775
838
  const s = useFieldStrings();
776
- const isSegmented = field.options.length <= 4 && field.options.every((o) => o.length <= 16);
839
+ const presentation = useFieldPresentation();
840
+ const labelOf = enumLabeler(field.options, presentation);
841
+ const isSegmented = field.options.length <= 4 && field.options.every((o) => labelOf(o).length <= 16);
777
842
  const domId = useFieldDomId(id);
778
843
  return /* @__PURE__ */ jsx(
779
844
  FieldShell,
@@ -797,7 +862,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
797
862
  value: value ?? "",
798
863
  onValueChange: (next) => onChange(next === "" ? void 0 : next),
799
864
  disabled,
800
- "aria-label": field.title ?? field.name,
865
+ "aria-label": fieldLabel(field.title, field.name, presentation),
801
866
  className: "flex flex-wrap justify-start gap-1.5",
802
867
  children: field.options.map((option) => /* @__PURE__ */ jsxs(
803
868
  ToggleGroupItem,
@@ -807,7 +872,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
807
872
  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",
808
873
  children: [
809
874
  value === option && /* @__PURE__ */ jsx(Check, { className: "size-3.5 text-primary", strokeWidth: 2.5, "aria-hidden": "true" }),
810
- option
875
+ labelOf(option)
811
876
  ]
812
877
  },
813
878
  option
@@ -824,7 +889,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
824
889
  /* @__PURE__ */ jsx(SelectTrigger, { id: domId, "aria-invalid": !!error, className: "h-11", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: s.selectPlaceholder }) }),
825
890
  /* @__PURE__ */ jsxs(SelectContent, { children: [
826
891
  /* @__PURE__ */ jsx(SelectItem, { value: CLEAR_VALUE, children: s.selectPlaceholder }),
827
- field.options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: option }, option))
892
+ field.options.map((option) => /* @__PURE__ */ jsx(SelectItem, { value: option, children: labelOf(option) }, option))
828
893
  ] })
829
894
  ]
830
895
  }
@@ -1345,11 +1410,12 @@ function ResultEnvProvider({
1345
1410
  resolveUrl,
1346
1411
  resolveShareUrl,
1347
1412
  proseImages,
1413
+ tableColumns,
1348
1414
  children
1349
1415
  }) {
1350
1416
  const env = useMemo(
1351
- () => ({ resolveUrl, resolveShareUrl, proseImages }),
1352
- [resolveUrl, resolveShareUrl, proseImages]
1417
+ () => ({ resolveUrl, resolveShareUrl, proseImages, tableColumns }),
1418
+ [resolveUrl, resolveShareUrl, proseImages, tableColumns]
1353
1419
  );
1354
1420
  return /* @__PURE__ */ jsx(ResultEnvContext, { value: env, children });
1355
1421
  }
@@ -1365,6 +1431,12 @@ function useResolveShareUrl() {
1365
1431
  function useProseImages() {
1366
1432
  return use(ResultEnvContext).proseImages ?? "link";
1367
1433
  }
1434
+ var DEFAULT_TABLE_COLUMNS = 5;
1435
+ function useTableColumns() {
1436
+ const budget = use(ResultEnvContext).tableColumns;
1437
+ if (budget === void 0 || Number.isNaN(budget)) return DEFAULT_TABLE_COLUMNS;
1438
+ return Math.max(1, Math.floor(budget));
1439
+ }
1368
1440
  var TooltipProvider = TooltipPrimitive.Provider;
1369
1441
  var TooltipRoot = TooltipPrimitive.Root;
1370
1442
  var TooltipTrigger = TooltipPrimitive.Trigger;
@@ -1811,6 +1883,18 @@ function DateValue({ value }) {
1811
1883
  if (!content) return /* @__PURE__ */ jsx(Absent, {});
1812
1884
  return /* @__PURE__ */ jsx("span", { className: "text-[13px] text-foreground", children: formatDateContent(content) });
1813
1885
  }
1886
+ function EnumValue({
1887
+ field,
1888
+ value,
1889
+ compact
1890
+ }) {
1891
+ const presentation = useFieldPresentation();
1892
+ const shown = typeof value === "string" ? enumLabeler(field.options, presentation)(value) : value;
1893
+ return /* @__PURE__ */ jsx(Scalar, { value: shown, compact });
1894
+ }
1895
+ function cellTitle(column, cell, presentation) {
1896
+ return column.kind === "enum" && typeof cell === "string" ? enumLabeler(column.options, presentation)(cell) : String(cell);
1897
+ }
1814
1898
  function fileLabel(url, filename, strings) {
1815
1899
  if (filename) return filename;
1816
1900
  const encoded = encodedFileSummary(url, strings);
@@ -1829,16 +1913,18 @@ function CopyButton({
1829
1913
  title
1830
1914
  }) {
1831
1915
  const [copied, setCopied] = useState(false);
1916
+ useEffect(() => {
1917
+ if (!copied) return;
1918
+ const timer = window.setTimeout(() => setCopied(false), 1500);
1919
+ return () => window.clearTimeout(timer);
1920
+ }, [copied]);
1832
1921
  if (typeof navigator === "undefined" || !navigator.clipboard) return null;
1833
1922
  return /* @__PURE__ */ jsx(
1834
1923
  "button",
1835
1924
  {
1836
1925
  type: "button",
1837
1926
  onClick: () => {
1838
- const done = () => {
1839
- setCopied(true);
1840
- window.setTimeout(() => setCopied(false), 1500);
1841
- };
1927
+ const done = () => setCopied(true);
1842
1928
  if (!provide) {
1843
1929
  void navigator.clipboard.writeText(value ?? "").then(done);
1844
1930
  return;
@@ -2118,6 +2204,8 @@ function LeafValue({
2118
2204
  return /* @__PURE__ */ jsx(ImageValue, { value, compact });
2119
2205
  case "object":
2120
2206
  return compact ? /* @__PURE__ */ jsx(RecordSummary, { field, value }) : /* @__PURE__ */ jsx(Scalar, { value });
2207
+ case "enum":
2208
+ return /* @__PURE__ */ jsx(EnumValue, { field, value, compact });
2121
2209
  default:
2122
2210
  return /* @__PURE__ */ jsx(Scalar, { value, compact });
2123
2211
  }
@@ -2126,7 +2214,7 @@ function RecordSummary({ field, value }) {
2126
2214
  const s = useFieldStrings();
2127
2215
  if (isNativeDateNode(field)) return /* @__PURE__ */ jsx(DateValue, { value });
2128
2216
  if (!isRecord(value) || Object.keys(value).length === 0) return /* @__PURE__ */ jsx(Absent, {});
2129
- const naming = isNativeHtmlNode(field) ? void 0 : field.fields.find((member) => member.kind === "text") ?? field.fields.find((member) => member.kind === "prose");
2217
+ const naming = isNativeHtmlNode(field) ? void 0 : recordNameField(field.fields) ?? field.fields.find((member) => member.kind === "prose");
2130
2218
  if (naming === void 0) {
2131
2219
  return /* @__PURE__ */ jsx("span", { className: "whitespace-nowrap text-[12.5px] text-muted-foreground", children: s.fieldsCount(field.fields.length) });
2132
2220
  }
@@ -2199,8 +2287,10 @@ function fitsACellWhole(field) {
2199
2287
  return true;
2200
2288
  case "text":
2201
2289
  return field.maxLength !== void 0 && field.maxLength <= CELL_LENGTH_LIMIT;
2290
+ case "object":
2291
+ return isNativeDateNode(field);
2202
2292
  case "list":
2203
- return fitsACellWhole(field.item);
2293
+ return isInlineColumn(field) && fitsACellWhole(field.item);
2204
2294
  default:
2205
2295
  return false;
2206
2296
  }
@@ -2213,10 +2303,12 @@ function ObjectTable({
2213
2303
  }) {
2214
2304
  const s = useFieldStrings();
2215
2305
  const presentation = useFieldPresentation();
2306
+ const budget = useTableColumns();
2216
2307
  const scrollerRef = useRef(null);
2217
2308
  const [expanded, setExpanded] = useState(() => /* @__PURE__ */ new Set());
2218
2309
  const [viewportWidth, setViewportWidth] = useState();
2219
- const canExpand = columns.some((column) => !fitsACellWhole(column));
2310
+ const { shown, hidden, name } = chooseColumns(columns, budget);
2311
+ const canExpand = hidden > 0 || shown.some((column) => column !== name && !fitsACellWhole(column));
2220
2312
  useEffect(() => {
2221
2313
  const scroller = scrollerRef.current;
2222
2314
  if (!scroller) return;
@@ -2261,7 +2353,7 @@ function ObjectTable({
2261
2353
  children: /* @__PURE__ */ jsxs("table", { className: "w-full min-w-full border-collapse text-[13px]", children: [
2262
2354
  /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
2263
2355
  canExpand && /* @__PURE__ */ jsx("th", { scope: "col", style: { width: EXPAND_COLUMN }, className: "px-1", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.rowDetailsColumn }) }),
2264
- columns.map((column) => /* @__PURE__ */ jsx(
2356
+ shown.map((column) => /* @__PURE__ */ jsx(
2265
2357
  "th",
2266
2358
  {
2267
2359
  scope: "col",
@@ -2275,7 +2367,7 @@ function ObjectTable({
2275
2367
  ))
2276
2368
  ] }) }),
2277
2369
  /* @__PURE__ */ jsx("tbody", { children: items.map((item, index) => {
2278
- const isOpen = expanded.has(index);
2370
+ const isOpen = canExpand && expanded.has(index);
2279
2371
  return /* @__PURE__ */ jsx(Fragment$1, { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border/60 last:border-b-0", children: [
2280
2372
  canExpand && /* @__PURE__ */ jsx("td", { className: "px-1 pt-px align-top", children: /* @__PURE__ */ jsx(
2281
2373
  "button",
@@ -2288,7 +2380,7 @@ function ObjectTable({
2288
2380
  children: isOpen ? /* @__PURE__ */ jsx(ChevronDown, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-3.5" })
2289
2381
  }
2290
2382
  ) }),
2291
- isOpen ? /* @__PURE__ */ jsx("td", { colSpan: columns.length, className: "bg-card/40 p-0", children: /* @__PURE__ */ jsx(
2383
+ isOpen ? /* @__PURE__ */ jsx("td", { colSpan: shown.length, className: "bg-card/40 p-0", children: /* @__PURE__ */ jsx(
2292
2384
  "div",
2293
2385
  {
2294
2386
  className: "sticky left-0 px-3.5 py-3",
@@ -2303,16 +2395,16 @@ function ObjectTable({
2303
2395
  } : {},
2304
2396
  children: /* @__PURE__ */ jsx(ResultField, { field: element, value: item, hideLabel: true })
2305
2397
  }
2306
- ) }) : columns.map((column) => {
2398
+ ) }) : shown.map((column) => {
2307
2399
  const cell = typeof item === "object" && item !== null ? ownProp(item, column.name) : void 0;
2308
2400
  return /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 align-top", children: /* @__PURE__ */ jsx(
2309
2401
  "div",
2310
2402
  {
2311
2403
  className: cn(
2312
2404
  "max-w-[44ch]",
2313
- column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
2405
+ column === name ? "max-w-none min-w-[16ch] wrap-anywhere [&>span]:whitespace-normal" : column.kind === "list" && isInlineColumn(column) ? "min-w-[16ch]" : "truncate"
2314
2406
  ),
2315
- ...typeof cell === "string" || typeof cell === "number" ? { title: String(cell) } : {},
2407
+ ...typeof cell === "string" || typeof cell === "number" ? { title: cellTitle(column, cell, presentation) } : {},
2316
2408
  children: /* @__PURE__ */ jsx(LeafValue, { field: column, value: cell, compact: true })
2317
2409
  }
2318
2410
  ) }, column.name);
@@ -2379,10 +2471,31 @@ function ImageGallery({ items }) {
2379
2471
  function FileRows({ items, kind }) {
2380
2472
  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)) });
2381
2473
  }
2382
- function tableColumns(item) {
2474
+ function recordColumns(item) {
2383
2475
  if (item.kind !== "object" || item.fields.length === 0) return void 0;
2384
2476
  return item.fields;
2385
2477
  }
2478
+ function recordNameField(fields) {
2479
+ return fields.find((member) => member.kind === "text");
2480
+ }
2481
+ function columnTier(field, name) {
2482
+ if (field === name) return 1;
2483
+ if (field.kind !== "list" && fitsACellWhole(field)) return 2;
2484
+ if (field.kind === "text" || field.kind === "list" && isInlineColumn(field)) return 3;
2485
+ return 4;
2486
+ }
2487
+ function chooseColumns(fields, budget) {
2488
+ const name = recordNameField(fields);
2489
+ if (fields.length <= budget) return { shown: fields, hidden: 0, ...name ? { name } : {} };
2490
+ const kept = new Set(
2491
+ 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)
2492
+ );
2493
+ return {
2494
+ shown: fields.filter((field) => kept.has(field)),
2495
+ hidden: fields.length - kept.size,
2496
+ ...name ? { name } : {}
2497
+ };
2498
+ }
2386
2499
  function ResultField({ field, value, depth = 0, hideLabel = false }) {
2387
2500
  const s = useFieldStrings();
2388
2501
  const unwrapped = unwrap(field, value);
@@ -2454,13 +2567,21 @@ function ResultField({ field, value, depth = 0, hideLabel = false }) {
2454
2567
  }
2455
2568
  case "list": {
2456
2569
  const items = Array.isArray(unwrapped) ? unwrapped : [];
2457
- const columns = tableColumns(field.item);
2570
+ const columns = recordColumns(field.item);
2458
2571
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2459
2572
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2", children: [
2460
2573
  header,
2461
2574
  /* @__PURE__ */ jsx("span", { className: "font-mono text-[10.5px] text-muted-foreground", children: s.itemsCount(items.length) })
2462
2575
  ] }),
2463
- items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[13px] italic text-muted-foreground", children: s.noItemsYet }) : columns ? (
2576
+ items.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-[13px] italic text-muted-foreground", children: s.noItemsYet }) : isNativeValueNode(field.item) ? (
2577
+ // A list of dates, pages or composites is a list of VALUES, each read
2578
+ // by its own arm - one per line. Their nodes are `object` (or, for a
2579
+ // composite, `unknown`), so the branches below would tabulate a
2580
+ // date's `date` and `time`, print a page's markup source down a
2581
+ // column named by it, and show a composite as JSON. The arm keyed by
2582
+ // concept is the one that knows what each entry is.
2583
+ /* @__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)) })
2584
+ ) : columns ? (
2464
2585
  // Same shape every row: the labels are column headers, not per-row
2465
2586
  // labels. See `ObjectTable`.
2466
2587
  /* @__PURE__ */ jsx(
@@ -2616,6 +2737,11 @@ function highlight(json) {
2616
2737
  function JsonView({ value }) {
2617
2738
  const s = useFieldStrings();
2618
2739
  const [copied, setCopied] = useState(false);
2740
+ useEffect(() => {
2741
+ if (!copied) return;
2742
+ const timer = window.setTimeout(() => setCopied(false), 1500);
2743
+ return () => window.clearTimeout(timer);
2744
+ }, [copied]);
2619
2745
  const text = value === null || value === void 0 ? void 0 : typeof value === "object" ? stringifyValue(value) : JSON.stringify(value);
2620
2746
  if (text === void 0) {
2621
2747
  return /* @__PURE__ */ jsx(Absent, {});
@@ -2626,10 +2752,7 @@ function JsonView({ value }) {
2626
2752
  {
2627
2753
  type: "button",
2628
2754
  onClick: () => {
2629
- void navigator.clipboard.writeText(text).then(() => {
2630
- setCopied(true);
2631
- window.setTimeout(() => setCopied(false), 1500);
2632
- });
2755
+ void navigator.clipboard.writeText(text).then(() => setCopied(true));
2633
2756
  },
2634
2757
  "aria-label": s.copyJson,
2635
2758
  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",
@@ -2717,5 +2840,5 @@ function StuffViewer({
2717
2840
  }
2718
2841
 
2719
2842
  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 };
2720
- //# sourceMappingURL=chunk-KG4IYFWI.js.map
2721
- //# sourceMappingURL=chunk-KG4IYFWI.js.map
2843
+ //# sourceMappingURL=chunk-W56WIIVC.js.map
2844
+ //# sourceMappingURL=chunk-W56WIIVC.js.map