@pipelex/mthds-form 0.29.0 → 0.31.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.
@@ -1,15 +1,15 @@
1
+ import { apiInputsFromRunValues, sameRunValues, runValuesFromInputs, setValueAtPath } from './chunk-2UCH4AAO.js';
1
2
  import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chunk-4VAO6BDE.js';
2
3
  import { seedInputs, closeOptionalStructures, openOptionalStructures } from './chunk-VH5CDQDR.js';
3
4
  import { sourceFileValue, roomForChosenFiles, roomForDroppedFiles, addChosenFiles, placeDroppedFiles, chosenFilesAdded } from './chunk-L23R3JNQ.js';
4
5
  import { readStuffFile } from './chunk-56WECFBD.js';
5
- import { apiInputsFromRunValues, sameRunValues, runValuesFromInputs, setValueAtPath } from './chunk-ZX52CQJ4.js';
6
+ import { shownAsOptional, isFoldedOptional, anyOptionalStructureOpen, isOptionalStructure, computeReadiness, optionalStructureOpen, mustBeFilled, fieldFilled, isFilled } from './chunk-XO5OENNF.js';
6
7
  import { enumValueLabeler, humanizeFieldName, humanizeEnumValue } from './chunk-GHVVULHY.js';
7
8
  import { TYPED_SCALAR_MARKERS, viewableUrl, splitCitations, webHref, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativePageNode, nativeVerdictOf, isNativeSearchResultNode, readSearchResultContent, isNativeValueNode, readDateContent, formatDateContent, readPageContent, readYesNoContent, readRatingContent, readChoiceContent, readDocumentContent, readImageContent, readDataUrl, citedSource } from './chunk-QQK6Y55E.js';
8
- import { buildRunFields } from './chunk-ZB4RYUKV.js';
9
+ import { buildRunFields } from './chunk-HVWE3TTC.js';
10
+ import { ownProp } from './chunk-V3OV2XQT.js';
9
11
  import { conceptCategory } from './chunk-FABY3DEY.js';
10
12
  import { acceptLabel, acceptMap, isAcceptedFile, normalizeMimeType, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
11
- import { shownAsOptional, isFoldedOptional, anyOptionalStructureOpen, isOptionalStructure, computeReadiness, optionalStructureOpen, mustBeFilled, fieldFilled, isFilled } from './chunk-XO5OENNF.js';
12
- import { ownProp } from './chunk-V3OV2XQT.js';
13
13
  import { toStoredDateValue, toDateInputValue } from './chunk-GKV77RAS.js';
14
14
  import { clsx } from 'clsx';
15
15
  import { twMerge } from 'tailwind-merge';
@@ -88,7 +88,7 @@ var Switch = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
88
88
  SwitchPrimitives.Root,
89
89
  {
90
90
  className: cn(
91
- "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
91
+ "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted-foreground",
92
92
  "max-sm:relative max-sm:after:absolute max-sm:after:-inset-x-0.5 max-sm:after:-inset-y-3 max-sm:after:content-['']",
93
93
  className
94
94
  ),
@@ -582,7 +582,18 @@ function FieldShell({
582
582
  }
583
583
  ),
584
584
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef, category }),
585
- !required && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
585
+ !required && // In `app` the mark is in the body face, as the label beside it is:
586
+ // a monospace tag was a third face on a host's page.
587
+ /* @__PURE__ */ jsx(
588
+ "span",
589
+ {
590
+ className: cn(
591
+ "ml-auto text-[10px] uppercase tracking-wider text-muted-foreground",
592
+ !isApp && "font-mono"
593
+ ),
594
+ children: s.optionalBadge
595
+ }
596
+ )
586
597
  ] }),
587
598
  description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: description }),
588
599
  /* @__PURE__ */ jsx(
@@ -821,6 +832,27 @@ function MoreFooter({ pages }) {
821
832
  if (!pages.hasMore) return null;
822
833
  return /* @__PURE__ */ jsx("div", { className: "flex justify-center px-4 py-4", children: /* @__PURE__ */ jsx(ListAction, { onClick: pages.loadMore, children: s.showMore }) });
823
834
  }
835
+
836
+ // src/react/style-tokens.ts
837
+ var headingFaceClass = "[font-family:var(--font-heading,revert-layer)]";
838
+ var headingTrackingClass = "[letter-spacing:var(--heading-tracking,revert-layer)]";
839
+ var headingTrackingTightClass = "[letter-spacing:var(--heading-tracking,-0.025em)]";
840
+ var headingWeightClass = {
841
+ 500: "[font-weight:var(--heading-weight,500)]",
842
+ 600: "[font-weight:var(--heading-weight,600)]",
843
+ 700: "[font-weight:var(--heading-weight,700)]"
844
+ };
845
+ function headingClass(weight, tight = false) {
846
+ return [
847
+ headingFaceClass,
848
+ headingWeightClass[weight],
849
+ tight ? headingTrackingTightClass : headingTrackingClass
850
+ ].join(" ");
851
+ }
852
+ var panelSurfaceClass = "[border-style:solid] [border-width:var(--panel-edge-width,1px)] [border-color:var(--panel-edge,var(--border,hsl(240_5.9%_90%)))] [background-color:var(--panel-fill,var(--card,hsl(0_0%_100%)))]";
853
+ var panelRaisedShadowClass = "[box-shadow:var(--panel-shadow,0_20px_25px_-5px_rgb(0_0_0/0.1),0_8px_10px_-6px_rgb(0_0_0/0.1))]";
854
+ var panelFlatShadowClass = "[box-shadow:var(--panel-shadow,none)]";
855
+ var actionShadowClass = "[box-shadow:var(--action-shadow,0_0_28px_color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_35%,transparent))] hover:[box-shadow:var(--action-shadow,0_0_44px_color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_50%,transparent))]";
824
856
  var Dialog = DialogPrimitive.Root;
825
857
  var DialogClose = DialogPrimitive.Close;
826
858
  function closeAutoFocus(onCloseAutoFocus, returnFocus) {
@@ -871,7 +903,7 @@ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE
871
903
  DialogPrimitive.Title,
872
904
  {
873
905
  ref,
874
- className: cn("text-[16px] font-semibold text-foreground", className),
906
+ className: cn("text-[16px] text-foreground", headingClass(600), className),
875
907
  ...props
876
908
  }
877
909
  ));
@@ -1041,7 +1073,7 @@ function FilePickerBody({
1041
1073
  return next;
1042
1074
  });
1043
1075
  const choices = [...chosen.values()];
1044
- const use8 = () => {
1076
+ const use9 = () => {
1045
1077
  if (choices.length === 0) return;
1046
1078
  onUse(choices);
1047
1079
  onClose();
@@ -1146,7 +1178,7 @@ function FilePickerBody({
1146
1178
  "button",
1147
1179
  {
1148
1180
  type: "button",
1149
- onClick: use8,
1181
+ onClick: use9,
1150
1182
  disabled: choices.length === 0,
1151
1183
  className: cn(
1152
1184
  "flex h-12 w-full items-center justify-center rounded-md bg-primary px-4 text-[16px] font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:cursor-not-allowed disabled:opacity-50",
@@ -1694,7 +1726,8 @@ function FileField({
1694
1726
  disabled: busy,
1695
1727
  onClick: () => setShowUrl(true),
1696
1728
  className: cn(
1697
- "flex w-fit items-center gap-1 rounded-sm font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 max-sm:min-h-11",
1729
+ "flex w-fit items-center gap-1 rounded-sm text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 max-sm:min-h-11",
1730
+ presentation !== "app" && "font-mono",
1698
1731
  focusOutlineClass,
1699
1732
  "focus-visible:outline-offset-2"
1700
1733
  ),
@@ -1978,7 +2011,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
1978
2011
  value: option,
1979
2012
  variant: "outline",
1980
2013
  className: cn(
1981
- "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]:font-medium",
2014
+ "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]:font-medium max-sm:h-11 max-sm:min-w-11",
1982
2015
  selectedStateBorderClass
1983
2016
  ),
1984
2017
  children: [
@@ -2170,11 +2203,30 @@ function OptionalToggle({ count, expanded, onToggle, noun = "field" }) {
2170
2203
  }
2171
2204
  );
2172
2205
  }
2206
+ var StructureDepthContext = createContext(0);
2207
+ var FramedFormContext = createContext(false);
2208
+ function structureGrouping(depth, labelled, framed) {
2209
+ if (!labelled) return "none";
2210
+ if (depth > 0) return "rule";
2211
+ return framed ? "section" : "box";
2212
+ }
2213
+ var SECTION_ERROR_EDGE = "pointer-events-none absolute inset-y-0 -start-[7px] m-0 w-[3px] rounded-full bg-destructive";
2214
+ function structureNameClass(isApp, depth) {
2215
+ if (!isApp) return "font-mono text-[13px] font-medium leading-none";
2216
+ return cn(
2217
+ depth === 0 ? "text-[16px] font-semibold leading-snug" : "text-[14.5px] font-semibold leading-snug",
2218
+ headingFaceClass
2219
+ );
2220
+ }
2173
2221
  function ObjectField({ field, value, onChange, id, error, env }) {
2174
2222
  const [showOptional, setShowOptional] = useState(false);
2175
2223
  const presentation = useFieldPresentation();
2176
2224
  const isApp = presentation === "app";
2225
+ const depth = use(StructureDepthContext);
2177
2226
  const data = value ?? {};
2227
+ const label = fieldLabel(field.title, field.name, presentation);
2228
+ const grouping = structureGrouping(depth, label.trim() !== "", use(FramedFormContext));
2229
+ const domId = useFieldDomId(id);
2178
2230
  const setChild = (name, childValue) => onChange({ ...data, [name]: childValue });
2179
2231
  const folded = field.fields.filter((f) => isFoldedOptional(f, ownProp(data, f.name)));
2180
2232
  const structureOpen = anyOptionalStructureOpen(field.fields, data);
@@ -2189,30 +2241,35 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2189
2241
  );
2190
2242
  }
2191
2243
  };
2192
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2244
+ const nameId = `${domId}--name`;
2245
+ const descriptionId = `${domId}--description`;
2246
+ const group = grouping === "none" ? {} : {
2247
+ role: "group",
2248
+ "aria-labelledby": nameId,
2249
+ "aria-describedby": field.description ? descriptionId : void 0
2250
+ };
2251
+ const section = grouping === "section";
2252
+ const caption = error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error });
2253
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
2193
2254
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
2194
- /* @__PURE__ */ jsx(
2195
- "span",
2196
- {
2197
- className: cn(
2198
- "text-[13px] font-medium leading-none text-foreground",
2199
- isApp ? "text-[13.5px]" : "font-mono"
2200
- ),
2201
- children: fieldLabel(field.title, field.name, presentation)
2202
- }
2203
- ),
2204
- !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" })
2255
+ /* @__PURE__ */ jsx("span", { id: nameId, className: cn("text-foreground", structureNameClass(isApp, depth)), children: label }),
2256
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" }),
2257
+ section && caption
2205
2258
  ] }),
2206
- field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2259
+ field.description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2207
2260
  /* @__PURE__ */ jsxs(
2208
2261
  "div",
2209
2262
  {
2263
+ "data-structure-group": grouping,
2210
2264
  className: cn(
2211
- "space-y-4 rounded-lg border bg-card/40 p-4",
2212
- error ? "border-destructive/50" : "border-border"
2265
+ "space-y-4",
2266
+ grouping === "box" && "rounded-lg border bg-card/40 p-4 max-sm:p-3",
2267
+ grouping === "section" && "pt-2",
2268
+ grouping === "rule" && "border-l-2 pl-4 max-sm:pl-3",
2269
+ (grouping === "box" || grouping === "rule") && (error ? "border-destructive/50" : "border-border")
2213
2270
  ),
2214
2271
  children: [
2215
- visible.map((child) => /* @__PURE__ */ jsx(
2272
+ /* @__PURE__ */ jsx(StructureDepthContext, { value: depth + 1, children: visible.map((child) => /* @__PURE__ */ jsx(
2216
2273
  FieldRenderer,
2217
2274
  {
2218
2275
  field: child,
@@ -2222,7 +2279,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2222
2279
  env
2223
2280
  },
2224
2281
  child.name
2225
- )),
2282
+ )) }),
2226
2283
  (folded.length > 0 || structureOpen) && /* @__PURE__ */ jsx(
2227
2284
  OptionalToggle,
2228
2285
  {
@@ -2235,8 +2292,26 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2235
2292
  ]
2236
2293
  }
2237
2294
  ),
2238
- error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
2295
+ !section && caption
2239
2296
  ] });
2297
+ return /* @__PURE__ */ jsx(
2298
+ "div",
2299
+ {
2300
+ className: section ? void 0 : "space-y-2",
2301
+ "data-form-section": section ? "" : void 0,
2302
+ ...group,
2303
+ children: section ? (
2304
+ // The section's content, without the padding `MethodForm`'s
2305
+ // hairlines give the section's own box: what the error edge runs
2306
+ // beside. Positioned whether or not it is in error, so an error
2307
+ // changes nothing a descendant is placed against.
2308
+ /* @__PURE__ */ jsxs("div", { className: "relative space-y-2", children: [
2309
+ error && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-error-edge": "", className: SECTION_ERROR_EDGE }),
2310
+ content
2311
+ ] })
2312
+ ) : content
2313
+ }
2314
+ );
2240
2315
  }
2241
2316
 
2242
2317
  // src/react/focus.ts
@@ -2378,6 +2453,7 @@ function ListField({ field, value, onChange, id, error, env }) {
2378
2453
  onChange(items.filter((_, i) => i !== index));
2379
2454
  };
2380
2455
  const addItem = () => onChange([...items, emptyValue(field.item)]);
2456
+ const structuredRows = field.item.kind === "object";
2381
2457
  const busy = listIsBusy(id, env?.uploadingIds) || arriving.size > 0;
2382
2458
  const hostErrors = env?.uploadErrors;
2383
2459
  useEffect(() => {
@@ -2484,8 +2560,17 @@ function ListField({ field, value, onChange, id, error, env }) {
2484
2560
  }
2485
2561
  ),
2486
2562
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "list" }),
2487
- /* @__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) }),
2488
- shownAsOptional(field) && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
2563
+ /* @__PURE__ */ jsx("span", { className: cn("text-[10.5px] text-muted-foreground", !isApp && "font-mono"), children: field.itemCount !== void 0 && field.maxItemCount === field.itemCount ? s.itemsCountOf(items.length, field.itemCount) : s.itemsCount(items.length) }),
2564
+ shownAsOptional(field) && /* @__PURE__ */ jsx(
2565
+ "span",
2566
+ {
2567
+ className: cn(
2568
+ "ml-auto text-[10px] uppercase tracking-wider text-muted-foreground",
2569
+ !isApp && "font-mono"
2570
+ ),
2571
+ children: s.optionalBadge
2572
+ }
2573
+ )
2489
2574
  ] }),
2490
2575
  field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2491
2576
  /* @__PURE__ */ jsxs("div", { ref: rowsRef, className: "space-y-2", children: [
@@ -2494,10 +2579,26 @@ function ListField({ field, value, onChange, id, error, env }) {
2494
2579
  "div",
2495
2580
  {
2496
2581
  "data-list-row": true,
2497
- className: "flex items-start gap-2 rounded-md border border-border bg-card/40 p-3",
2582
+ className: cn(
2583
+ "rounded-md border border-border bg-card/40 p-3",
2584
+ // A row holding a structure puts its number and its remove
2585
+ // button on a line of their own, so its fields take the row's
2586
+ // whole width: beside the button every input in the row ended
2587
+ // a button short of the edge, a third of a phone's row.
2588
+ structuredRows ? "grid grid-cols-[auto_minmax(0,1fr)_auto] items-center gap-x-2 gap-y-1" : "flex items-start gap-2"
2589
+ ),
2498
2590
  children: [
2499
- /* @__PURE__ */ jsx("span", { className: "mt-1 w-5 shrink-0 text-center font-mono text-[11px] text-muted-foreground", children: index + 1 }),
2500
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
2591
+ /* @__PURE__ */ jsx(
2592
+ "span",
2593
+ {
2594
+ className: cn(
2595
+ "shrink-0 font-mono text-[11px] text-muted-foreground",
2596
+ structuredRows ? "col-start-1 row-start-1" : "mt-1 w-5 text-center"
2597
+ ),
2598
+ children: index + 1
2599
+ }
2600
+ ),
2601
+ /* @__PURE__ */ jsx("div", { className: structuredRows ? "col-span-3 row-start-2 min-w-0" : "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
2501
2602
  FieldRenderer,
2502
2603
  {
2503
2604
  field: {
@@ -2520,7 +2621,8 @@ function ListField({ field, value, onChange, id, error, env }) {
2520
2621
  disabled: env?.disabled || busy,
2521
2622
  "aria-label": s.removeItemAria(index + 1),
2522
2623
  className: cn(
2523
- "mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-destructive disabled:opacity-50 max-sm:mt-0 max-sm:size-11",
2624
+ "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-destructive disabled:opacity-50 max-sm:size-11",
2625
+ structuredRows ? "col-start-3 row-start-1" : "mt-0.5 max-sm:mt-0",
2524
2626
  focusOutlineClass
2525
2627
  ),
2526
2628
  children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
@@ -2921,7 +3023,7 @@ var AlertDialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @_
2921
3023
  AlertDialogPrimitive.Title,
2922
3024
  {
2923
3025
  ref,
2924
- className: cn("text-[16px] font-semibold text-foreground", className),
3026
+ className: cn("text-[16px] text-foreground", headingClass(600), className),
2925
3027
  ...props
2926
3028
  }
2927
3029
  ));
@@ -3681,8 +3783,8 @@ function useMethodForm({
3681
3783
  exampleSource
3682
3784
  };
3683
3785
  }
3684
- function MethodForm({ locale, strings, presentation, ...props }) {
3685
- let body = /* @__PURE__ */ jsx(MethodFormFields, { ...props });
3786
+ function MethodForm({ locale, strings, presentation, framed, ...props }) {
3787
+ let body = /* @__PURE__ */ jsx(FramedFormContext, { value: framed ?? false, children: /* @__PURE__ */ jsx(MethodFormFields, { ...props, framed }) });
3686
3788
  if (presentation) {
3687
3789
  body = /* @__PURE__ */ jsx(FieldPresentationProvider, { presentation, children: body });
3688
3790
  }
@@ -3691,6 +3793,7 @@ function MethodForm({ locale, strings, presentation, ...props }) {
3691
3793
  }
3692
3794
  return body;
3693
3795
  }
3796
+ var SECTION_RULES = "[&>[data-form-section]:not(:last-child)]:border-b [&>[data-form-section]:not(:last-child)]:border-border [&>[data-form-section]:not(:last-child)]:pb-6 [&>:not([data-form-section])+[data-form-section]]:border-t [&>:not([data-form-section])+[data-form-section]]:border-border [&>:not([data-form-section])+[data-form-section]]:pt-6";
3694
3797
  function MethodFormFields({
3695
3798
  form,
3696
3799
  disabled,
@@ -3698,6 +3801,7 @@ function MethodFormFields({
3698
3801
  resolveUrl,
3699
3802
  foldOptional = true,
3700
3803
  errors,
3804
+ framed,
3701
3805
  className
3702
3806
  }) {
3703
3807
  const s = useFieldStrings();
@@ -3775,7 +3879,7 @@ function MethodFormFields({
3775
3879
  if (mustBeFilled(field)) return fieldFilled(field, value) ? void 0 : s.requiredField;
3776
3880
  return isFilled(value) && !fieldFilled(field, value) ? s.incompleteField : void 0;
3777
3881
  };
3778
- return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
3882
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", framed && SECTION_RULES, className), children: [
3779
3883
  exampleSource && /* @__PURE__ */ jsx(
3780
3884
  FillWithExample,
3781
3885
  {
@@ -4525,7 +4629,7 @@ function FileNameDialog({ stem: stem2, extension, onConfirm, onCancel }) {
4525
4629
  onKeyDown,
4526
4630
  className: "w-full max-w-sm space-y-3 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg",
4527
4631
  children: [
4528
- /* @__PURE__ */ jsx("h2", { id: titleId, className: "text-[14px] font-medium", children: s.fileNameTitle }),
4632
+ /* @__PURE__ */ jsx("h2", { id: titleId, className: cn("text-[14px]", headingClass(500)), children: s.fileNameTitle }),
4529
4633
  /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-md border border-border bg-input focus-within:outline-solid focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-ring", children: [
4530
4634
  /* @__PURE__ */ jsx(
4531
4635
  "input",
@@ -5204,7 +5308,8 @@ function PdfDownloadButton({
5204
5308
  title: failed ? s.downloadPdfFailed : name,
5205
5309
  ...failed ? { "aria-describedby": statusId } : {},
5206
5310
  className: cn(
5207
- "inline-flex shrink-0 items-center gap-1.5 rounded-md px-2 py-1 text-[12px] font-medium disabled:opacity-70",
5311
+ // 44 pixels tall below `sm`, as every control a finger hits is.
5312
+ "inline-flex shrink-0 items-center gap-1.5 rounded-md px-2 py-1 text-[12px] font-medium disabled:opacity-70 max-sm:min-h-11 max-sm:px-3",
5208
5313
  focusOutlineClass,
5209
5314
  primary ? "bg-primary text-primary-foreground hover:bg-primary/90" : "border border-border text-muted-foreground hover:text-foreground",
5210
5315
  failed && (primary ? "bg-destructive text-white hover:bg-destructive/90" : "text-destructive"),
@@ -5477,7 +5582,7 @@ function ImageViewer({
5477
5582
  children: [
5478
5583
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2 border-b border-border py-1.5 pr-1.5 pl-4", children: [
5479
5584
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
5480
- /* @__PURE__ */ jsx(DialogTitle, { className: "truncate text-[14px] font-medium", children: picture.name }),
5585
+ /* @__PURE__ */ jsx(DialogTitle, { className: cn("truncate text-[14px]", headingWeightClass[500]), children: picture.name }),
5481
5586
  several && // A live region from the moment the viewer opens, so each move
5482
5587
  // is announced: one inserted with its text already in it is not.
5483
5588
  /* @__PURE__ */ jsx(
@@ -5638,15 +5743,24 @@ function Markdown({ text, className, proseImages, citations }) {
5638
5743
  const mode = proseImages ?? fromEnv;
5639
5744
  return /* @__PURE__ */ jsx(ProseImagesContext, { value: mode, children: /* @__PURE__ */ jsx(CitationsContext, { value: citations, children: /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) }) }) });
5640
5745
  }
5746
+ function opensWithHeading(text) {
5747
+ const body = text.slice(LEADING_BLANK_LINES.exec(text)?.[0].length ?? 0);
5748
+ const end = body.search(BLANK_LINE);
5749
+ const opening = end === -1 ? body : body.slice(0, end);
5750
+ const first = marked.lexer(opening, LEX_OPTIONS).find((token) => token.type !== "space");
5751
+ return first?.type === "heading";
5752
+ }
5753
+ var LEADING_BLANK_LINES = /^(?:[ \t]*\r?\n)*/;
5754
+ var BLANK_LINE = /\r?\n[ \t]*\r?\n/;
5641
5755
  var ProseImagesContext = React9.createContext("link");
5642
5756
  var CitationsContext = React9.createContext(void 0);
5643
5757
  var HEADING_CLASS = {
5644
- 1: "text-[16px] font-semibold",
5645
- 2: "text-[14.5px] font-semibold",
5646
- 3: "text-[13.5px] font-semibold",
5647
- 4: "text-[13px] font-semibold",
5648
- 5: "text-[13px] font-semibold",
5649
- 6: "text-[13px] font-semibold"
5758
+ 1: cn("text-[16px]", headingClass(600)),
5759
+ 2: cn("text-[14.5px]", headingClass(600)),
5760
+ 3: cn("text-[13.5px]", headingClass(600)),
5761
+ 4: cn("text-[13px]", headingClass(600)),
5762
+ 5: cn("text-[13px]", headingClass(600)),
5763
+ 6: cn("text-[13px]", headingClass(600))
5650
5764
  };
5651
5765
  function Blocks({ tokens }) {
5652
5766
  return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(Block, { token }, index)) });
@@ -6350,11 +6464,11 @@ function fencedBlockEnd(text, start) {
6350
6464
  }
6351
6465
  return text.length;
6352
6466
  }
6353
- var BLANK_LINE = /\n[ \t]*\n/g;
6467
+ var BLANK_LINE2 = /\n[ \t]*\n/g;
6354
6468
  function codeSpanEnd(text, start) {
6355
6469
  const open = backtickRun(text, start);
6356
- BLANK_LINE.lastIndex = start;
6357
- const blank = BLANK_LINE.exec(text);
6470
+ BLANK_LINE2.lastIndex = start;
6471
+ const blank = BLANK_LINE2.exec(text);
6358
6472
  const limit = blank ? blank.index : text.length;
6359
6473
  let cursor = start + open;
6360
6474
  for (; ; ) {
@@ -6603,13 +6717,21 @@ function Described({ description, children }) {
6603
6717
  /* @__PURE__ */ jsx(TooltipContent, { children: description })
6604
6718
  ] }) });
6605
6719
  }
6606
- function ResultHeader({ field }) {
6607
- return /* @__PURE__ */ jsx(Label2, { field, describe: true });
6720
+ function ResultHeader({ field, unseen = false }) {
6721
+ return /* @__PURE__ */ jsx(Label2, { field, describe: true, unseen });
6608
6722
  }
6609
- function Label2({ field, describe = false }) {
6723
+ function Label2({
6724
+ field,
6725
+ describe = false,
6726
+ unseen = false
6727
+ }) {
6610
6728
  const presentation = useFieldPresentation();
6611
6729
  const isApp = presentation === "app";
6612
6730
  const labelText = fieldLabel(field.title, field.name, presentation);
6731
+ if (unseen) {
6732
+ if (labelText.trim() === "") return null;
6733
+ return /* @__PURE__ */ jsx("span", { className: "sr-only", children: describe && field.description ? `${labelText}: ${field.description}` : labelText });
6734
+ }
6613
6735
  if (labelText.trim() === "") {
6614
6736
  return /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) });
6615
6737
  }
@@ -7336,11 +7458,25 @@ var TOGGLE = cn(
7336
7458
  );
7337
7459
  var STACK_NEVER_UP_TO = 3;
7338
7460
  var STACK_ALWAYS_BEYOND = 6;
7339
- function stackingFor(columns) {
7340
- if (columns <= STACK_NEVER_UP_TO) return "never";
7461
+ function stackingFor(columns, rowsOpen) {
7462
+ if (columns <= STACK_NEVER_UP_TO) {
7463
+ return columns === STACK_NEVER_UP_TO && rowsOpen ? "phone" : "never";
7464
+ }
7341
7465
  return columns > STACK_ALWAYS_BEYOND ? "always" : "narrow";
7342
7466
  }
7343
7467
  var STACKED = {
7468
+ phone: {
7469
+ table: "max-sm:block",
7470
+ head: "max-sm:hidden",
7471
+ body: "max-sm:block",
7472
+ row: "max-sm:relative max-sm:block max-sm:px-3 max-sm:py-2",
7473
+ rowWithToggle: "max-sm:pl-12",
7474
+ toggle: "max-sm:absolute max-sm:top-0 max-sm:left-0 max-sm:p-0",
7475
+ cell: "max-sm:grid max-sm:grid-cols-[minmax(0,min(40%,10rem))_minmax(0,1fr)] max-sm:items-baseline max-sm:gap-x-3 max-sm:px-0 max-sm:py-0.5 max-sm:after:hidden",
7476
+ label: "max-sm:block",
7477
+ detail: "max-sm:block",
7478
+ detailInner: "max-sm:px-0 max-sm:py-1"
7479
+ },
7344
7480
  narrow: {
7345
7481
  table: "@max-[640px]:block",
7346
7482
  head: "@max-[640px]:hidden",
@@ -7369,21 +7505,34 @@ var STACKED = {
7369
7505
  var FIT_CELL = "min-w-[min(8ch,14cqw)] wrap-anywhere [&>span]:whitespace-normal";
7370
7506
  var FIT_NAME = "min-w-[min(16ch,28cqw)] wrap-anywhere [&>span]:whitespace-normal";
7371
7507
  var WORD_FLOOR = "after:block after:h-0 after:max-w-[var(--fit-share,0px)] after:overflow-hidden after:invisible after:wrap-normal after:content-[attr(data-fit)]";
7508
+ var FIT_CHEVRON = "[--fit-chevron:var(--expand-column,2rem)]";
7509
+ var FIT_NO_CHEVRON = "[--fit-chevron:0px]";
7372
7510
  var FIT_SHARE_BESIDE_NAME = {
7373
- 2: "[--fit-share:calc(72cqw_-_3rem_-_var(--expand-column,2rem))]",
7374
- 3: "[--fit-share:calc((72cqw_-_4.5rem_-_var(--expand-column,2rem))/2)]",
7375
- 4: "[--fit-share:calc((72cqw_-_6rem_-_var(--expand-column,2rem))/3)]",
7376
- 5: "[--fit-share:calc((72cqw_-_7.5rem_-_var(--expand-column,2rem))/4)]",
7377
- 6: "[--fit-share:calc((72cqw_-_9rem_-_var(--expand-column,2rem))/5)]"
7511
+ 2: "[--fit-share:calc(72cqw_-_3rem_-_var(--fit-chevron,0px))]",
7512
+ 3: "[--fit-share:calc((72cqw_-_4.5rem_-_var(--fit-chevron,0px))/2)]",
7513
+ 4: "[--fit-share:calc((72cqw_-_6rem_-_var(--fit-chevron,0px))/3)]",
7514
+ 5: "[--fit-share:calc((72cqw_-_7.5rem_-_var(--fit-chevron,0px))/4)]",
7515
+ 6: "[--fit-share:calc((72cqw_-_9rem_-_var(--fit-chevron,0px))/5)]"
7378
7516
  };
7379
7517
  var FIT_SHARE_WITHOUT_NAME = {
7380
- 1: "[--fit-share:calc(100cqw_-_1.5rem_-_var(--expand-column,2rem))]",
7381
- 2: "[--fit-share:calc((100cqw_-_3rem_-_var(--expand-column,2rem))/2)]",
7382
- 3: "[--fit-share:calc((100cqw_-_4.5rem_-_var(--expand-column,2rem))/3)]",
7383
- 4: "[--fit-share:calc((100cqw_-_6rem_-_var(--expand-column,2rem))/4)]",
7384
- 5: "[--fit-share:calc((100cqw_-_7.5rem_-_var(--expand-column,2rem))/5)]",
7385
- 6: "[--fit-share:calc((100cqw_-_9rem_-_var(--expand-column,2rem))/6)]"
7518
+ 1: "[--fit-share:calc(100cqw_-_1.5rem_-_var(--fit-chevron,0px))]",
7519
+ 2: "[--fit-share:calc((100cqw_-_3rem_-_var(--fit-chevron,0px))/2)]",
7520
+ 3: "[--fit-share:calc((100cqw_-_4.5rem_-_var(--fit-chevron,0px))/3)]",
7521
+ 4: "[--fit-share:calc((100cqw_-_6rem_-_var(--fit-chevron,0px))/4)]",
7522
+ 5: "[--fit-share:calc((100cqw_-_7.5rem_-_var(--fit-chevron,0px))/5)]",
7523
+ 6: "[--fit-share:calc((100cqw_-_9rem_-_var(--fit-chevron,0px))/6)]"
7386
7524
  };
7525
+ function identifierWords(label) {
7526
+ const words = label.split("_");
7527
+ if (words.length === 1) return label;
7528
+ return words.map(
7529
+ (word, index) => index === words.length - 1 ? word : /* @__PURE__ */ jsxs(Fragment$1, { children: [
7530
+ word,
7531
+ "_",
7532
+ /* @__PURE__ */ jsx("wbr", {})
7533
+ ] }, index)
7534
+ );
7535
+ }
7387
7536
  function cellWords(column, cell, presentation) {
7388
7537
  return typeof cell === "string" || typeof cell === "number" ? cellTitle(column, cell, presentation) : void 0;
7389
7538
  }
@@ -7424,7 +7573,7 @@ function ObjectTable({
7424
7573
  const { shown, hidden, name } = chooseColumns(columns, budget);
7425
7574
  const canExpand = hidden > 0 || shown.some((column) => column !== name && !fitsACellWhole(column));
7426
7575
  const fits = layout === "stack";
7427
- const stacking = fits ? stackingFor(shown.length) : "never";
7576
+ const stacking = fits ? stackingFor(shown.length, canExpand) : "never";
7428
7577
  const stacked = stacking === "never" ? void 0 : STACKED[stacking];
7429
7578
  const nameShown = name !== void 0 && shown.includes(name);
7430
7579
  const fitShare = fits ? (nameShown ? FIT_SHARE_BESIDE_NAME : FIT_SHARE_WITHOUT_NAME)[shown.length] : void 0;
@@ -7439,6 +7588,19 @@ function ObjectTable({
7439
7588
  observer.observe(scroller);
7440
7589
  return () => observer.disconnect();
7441
7590
  }, [fits]);
7591
+ const [overflows, setOverflows] = useState(false);
7592
+ useEffect(() => {
7593
+ const scroller = scrollerRef.current;
7594
+ if (!scroller || !fits) return;
7595
+ const measure = () => setOverflows(scroller.scrollWidth > scroller.clientWidth);
7596
+ measure();
7597
+ if (typeof ResizeObserver === "undefined") return;
7598
+ const observer = new ResizeObserver(measure);
7599
+ observer.observe(scroller);
7600
+ const content = scroller.firstElementChild;
7601
+ if (content) observer.observe(content);
7602
+ return () => observer.disconnect();
7603
+ }, [fits]);
7442
7604
  const toggle = (index) => setExpanded((current) => {
7443
7605
  const next = new Set(current);
7444
7606
  if (!next.delete(index)) next.add(index);
@@ -7465,16 +7627,16 @@ function ObjectTable({
7465
7627
  //
7466
7628
  // Under `tableLayout="stack"` the box is the SIZE CONTAINER its stacking
7467
7629
  // rules query, so the table answers to its own width rather than the
7468
- // viewport's, and it takes no focus: nothing scrolls, so the stop would have
7469
- // nothing to do. It still clips to its rounded border, and should anything
7470
- // ever overflow it, the a11y gate names the scroll a keyboard cannot reach.
7630
+ // viewport's, and it takes no focus while nothing scrolls, since the stop
7631
+ // would have nothing to do. It still clips to its rounded border, and takes
7632
+ // focus again only while its headers' words make it overflow (`overflows`).
7471
7633
  /* @__PURE__ */ jsx(
7472
7634
  "div",
7473
7635
  {
7474
7636
  ref: scrollerRef,
7475
7637
  role: "group",
7476
7638
  "aria-label": label,
7477
- ...fits ? {} : { tabIndex: 0 },
7639
+ ...fits && !overflows ? {} : { tabIndex: 0 },
7478
7640
  className: cn(
7479
7641
  "overflow-x-auto rounded-lg border border-border",
7480
7642
  focusOutlineInsetClass,
@@ -7484,7 +7646,12 @@ function ObjectTable({
7484
7646
  children: /* @__PURE__ */ jsxs(
7485
7647
  "table",
7486
7648
  {
7487
- className: cn("w-full min-w-full border-collapse text-[13px]", stacked?.table, fitShare),
7649
+ className: cn(
7650
+ "w-full min-w-full border-collapse text-[13px]",
7651
+ stacked?.table,
7652
+ fitShare,
7653
+ fitShare !== void 0 && (canExpand ? FIT_CHEVRON : FIT_NO_CHEVRON)
7654
+ ),
7488
7655
  children: [
7489
7656
  /* @__PURE__ */ jsx("thead", { className: stacked?.head, children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
7490
7657
  canExpand && /* @__PURE__ */ jsx("th", { scope: "col", style: { width: EXPAND_COLUMN }, className: "px-1", children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: s.rowDetailsColumn }) }),
@@ -7492,16 +7659,20 @@ function ObjectTable({
7492
7659
  "th",
7493
7660
  {
7494
7661
  scope: "col",
7495
- ...floorsWords(column) ? { "data-fit": fieldLabel(column.title, column.name, presentation) } : {},
7496
7662
  className: cn(
7497
- // One line, or wrapped anywhere so a header is never what
7498
- // widens a table that has to fit its box. See `FIT_CELL`.
7499
- fits ? "wrap-anywhere" : "whitespace-nowrap",
7663
+ // One line, or, in a table that fits its box, wrapped between
7664
+ // words and never inside one: "Quantity" over three digits is
7665
+ // a header, "Quantit" over "y" is damage. So a header's
7666
+ // longest word is its column's floor, with no share capping
7667
+ // it, as a value's is capped: a header's words are the label
7668
+ // the descriptor states, not a payload's token of any length.
7669
+ // Where the box cannot hold every header's longest word, the
7670
+ // table scrolls inside it (see `overflows`).
7671
+ fits ? "wrap-normal" : "whitespace-nowrap",
7500
7672
  "px-3 py-2 text-[12px] font-semibold text-foreground",
7501
- presentation === "app" ? void 0 : "font-mono",
7502
- floorsWords(column) && WORD_FLOOR
7673
+ presentation === "app" ? void 0 : "font-mono"
7503
7674
  ),
7504
- children: /* @__PURE__ */ jsx(Described, { description: column.description, children: fieldLabel(column.title, column.name, presentation) })
7675
+ children: /* @__PURE__ */ jsx(Described, { description: column.description, children: presentation === "studio" ? identifierWords(fieldLabel(column.title, column.name, presentation)) : fieldLabel(column.title, column.name, presentation) })
7505
7676
  },
7506
7677
  column.name
7507
7678
  ))
@@ -7604,13 +7775,16 @@ function CompositeValue({ members }) {
7604
7775
  const isApp = presentation === "app";
7605
7776
  return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: members.map((member) => {
7606
7777
  const label = isApp ? humanizeFieldName(member.name) : member.name;
7778
+ const text = memberText(member.value);
7779
+ const headed = isApp && text !== void 0 && opensWithHeading(text);
7607
7780
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
7608
7781
  /* @__PURE__ */ jsx(
7609
7782
  "span",
7610
7783
  {
7611
7784
  className: cn(
7612
7785
  "font-semibold text-foreground",
7613
- isApp ? "text-[13.5px]" : "font-mono text-[12px]"
7786
+ isApp ? "text-[13.5px]" : "font-mono text-[12px]",
7787
+ headed && "sr-only"
7614
7788
  ),
7615
7789
  children: label
7616
7790
  }
@@ -7649,6 +7823,12 @@ function NativeValue({ value, label }) {
7649
7823
  return /* @__PURE__ */ jsx(RawValue, { value, compact: false });
7650
7824
  }
7651
7825
  }
7826
+ function memberText(value) {
7827
+ if (typeof value === "string") return value === "" ? void 0 : value;
7828
+ if (!isRecord(value)) return void 0;
7829
+ const content = readMemberContent(value);
7830
+ return content?.model === "text" && content.text !== "" ? content.text : void 0;
7831
+ }
7652
7832
  function NativeList({ items, label }) {
7653
7833
  if (items.length === 0) return /* @__PURE__ */ jsx(Absent, {});
7654
7834
  if (items.every(isPlainRecord)) return /* @__PURE__ */ jsx(RecordsTable, { items, label });
@@ -7981,6 +8161,18 @@ function SearchSource({ source }) {
7981
8161
  function isLongText(value) {
7982
8162
  return typeof value === "string" && (value.includes("\n") || value.length > CELL_LENGTH_LIMIT);
7983
8163
  }
8164
+ function typesetText(field, value, isApp) {
8165
+ if (typeof value !== "string" || value === "") return void 0;
8166
+ if (field.kind === "prose") return value;
8167
+ return isApp && field.kind === "text" && isLongText(value) ? value : void 0;
8168
+ }
8169
+ function namesItself(field, value, isApp) {
8170
+ const typeset = typesetText(field, value, isApp);
8171
+ return isApp && typeset !== void 0 && opensWithHeading(typeset);
8172
+ }
8173
+ function resultHeadsItself(field, value, presentation) {
8174
+ return namesItself(field, unwrap(field, value), presentation === "app");
8175
+ }
7984
8176
  var PanelResultContext = createContext(null);
7985
8177
  var TITLED_PANEL = { hostHeaded: false };
7986
8178
  var HOST_HEADED_PANEL = { hostHeaded: true };
@@ -8210,14 +8402,35 @@ function ResultNode({
8210
8402
  case "prose":
8211
8403
  case "number":
8212
8404
  case "enum":
8213
- case "unknown":
8214
- return /* @__PURE__ */ jsxs("div", { className: field.kind === "image" ? "space-y-1.5" : "space-y-0.5", children: [
8215
- copyText === void 0 ? header : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
8216
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
8217
- /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText })
8218
- ] }),
8219
- isApp && field.kind === "text" && isLongText(unwrapped) ? /* @__PURE__ */ jsx(Markdown, { text: unwrapped }) : field.kind === "document" && whole ? /* @__PURE__ */ jsx(DocumentValue, { value: unwrapped, whole: true }) : /* @__PURE__ */ jsx(LeafValue, { field, value })
8220
- ] });
8405
+ case "unknown": {
8406
+ const typeset = typesetText(field, unwrapped, isApp);
8407
+ const headed = !hideLabel && namesItself(field, unwrapped, isApp);
8408
+ const labelDrawn = !hideLabel && !headed;
8409
+ return /* @__PURE__ */ jsxs(
8410
+ "div",
8411
+ {
8412
+ className: cn(
8413
+ field.kind === "image" ? "space-y-1.5" : "space-y-0.5",
8414
+ // Holds the copy control's float, when it floats.
8415
+ copyText !== void 0 && !labelDrawn && "flow-root"
8416
+ ),
8417
+ children: [
8418
+ copyText === void 0 ? (
8419
+ // A text that heads itself always has something to copy, so this
8420
+ // arm draws the label exactly when it is not hidden.
8421
+ header
8422
+ ) : labelDrawn ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
8423
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
8424
+ /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText })
8425
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
8426
+ headed && /* @__PURE__ */ jsx(Label2, { field, describe: true, unseen: true }),
8427
+ /* @__PURE__ */ jsx("div", { className: "float-right ml-2", children: /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText }) })
8428
+ ] }),
8429
+ typeset !== void 0 ? /* @__PURE__ */ jsx(Markdown, { text: typeset }) : field.kind === "document" && whole ? /* @__PURE__ */ jsx(DocumentValue, { value: unwrapped, whole: true }) : /* @__PURE__ */ jsx(LeafValue, { field, value })
8430
+ ]
8431
+ }
8432
+ );
8433
+ }
8221
8434
  }
8222
8435
  return null;
8223
8436
  }
@@ -8257,20 +8470,40 @@ function highlight(json) {
8257
8470
  }
8258
8471
  return out;
8259
8472
  }
8473
+ var noSubscription = () => () => {
8474
+ };
8475
+ var hasClipboard = () => typeof navigator !== "undefined" && Boolean(navigator.clipboard);
8260
8476
  function JsonView({ value }) {
8261
8477
  const s = useFieldStrings();
8262
8478
  const [copied, setCopied] = useState(false);
8479
+ const canCopy = useSyncExternalStore(noSubscription, hasClipboard, () => false);
8263
8480
  useEffect(() => {
8264
8481
  if (!copied) return;
8265
8482
  const timer = window.setTimeout(() => setCopied(false), 1500);
8266
8483
  return () => window.clearTimeout(timer);
8267
8484
  }, [copied]);
8268
8485
  const text = value === null || value === void 0 ? void 0 : typeof value === "object" ? stringifyValue(value) : JSON.stringify(value);
8486
+ const block = useRef(null);
8487
+ const [overflows, setOverflows] = useState(false);
8488
+ useEffect(() => {
8489
+ const pre = block.current;
8490
+ if (!pre) return;
8491
+ const measure = () => setOverflows(pre.scrollWidth > pre.clientWidth);
8492
+ measure();
8493
+ const fonts = typeof document === "undefined" ? void 0 : document.fonts;
8494
+ fonts?.addEventListener("loadingdone", measure);
8495
+ const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(measure);
8496
+ observer?.observe(pre);
8497
+ return () => {
8498
+ fonts?.removeEventListener("loadingdone", measure);
8499
+ observer?.disconnect();
8500
+ };
8501
+ }, [text]);
8269
8502
  if (text === void 0) {
8270
8503
  return /* @__PURE__ */ jsx(Absent, {});
8271
8504
  }
8272
8505
  return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
8273
- typeof navigator !== "undefined" && navigator.clipboard && /* @__PURE__ */ jsx(
8506
+ canCopy && /* @__PURE__ */ jsx(
8274
8507
  "button",
8275
8508
  {
8276
8509
  type: "button",
@@ -8279,15 +8512,35 @@ function JsonView({ value }) {
8279
8512
  },
8280
8513
  "aria-label": s.copyJson,
8281
8514
  className: cn(
8282
- "absolute right-2 top-2 rounded border border-border bg-card p-1 text-muted-foreground hover:text-foreground",
8515
+ // 44 by 44 on a phone, like every control a finger taps.
8516
+ "absolute right-2 top-2 inline-flex items-center justify-center rounded border border-border bg-card p-1 text-muted-foreground hover:text-foreground max-sm:size-11",
8283
8517
  focusOutlineClass
8284
8518
  ),
8285
8519
  children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
8286
8520
  }
8287
8521
  ),
8288
- /* @__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) })
8522
+ /* @__PURE__ */ jsx(
8523
+ "pre",
8524
+ {
8525
+ ref: block,
8526
+ role: "group",
8527
+ "aria-label": s.viewJson,
8528
+ ...overflows ? { tabIndex: 0 } : {},
8529
+ className: cn(
8530
+ "overflow-x-auto rounded-lg border border-border bg-card/40 px-3.5 py-3 pr-12 font-mono text-[12px] leading-relaxed",
8531
+ canCopy && "max-sm:min-h-[60px] max-sm:pr-14",
8532
+ focusOutlineClass
8533
+ ),
8534
+ children: highlight(text)
8535
+ }
8536
+ )
8289
8537
  ] });
8290
8538
  }
8539
+ function underHostName(field, name) {
8540
+ const renamed = { ...field, name };
8541
+ delete renamed.title;
8542
+ return renamed;
8543
+ }
8291
8544
  function isShownResult(attempt, field, baseName, value) {
8292
8545
  if (attempt.field === field && attempt.baseName === baseName && attempt.value === value) {
8293
8546
  return true;
@@ -8319,7 +8572,7 @@ function StuffPanel({
8319
8572
  const s = useFieldStrings();
8320
8573
  const presentation = useFieldPresentation();
8321
8574
  const offered = viewsProp && viewsProp.length > 0 ? viewsProp : viewsProp || presentation === "app" ? RESULT_ONLY : ALL_VIEWS;
8322
- const named = name ? { ...field, name } : field;
8575
+ const named = name ? underHostName(field, name) : field;
8323
8576
  const baseName = downloadBaseName ?? named.name ?? "result";
8324
8577
  const [chosen, setView] = useState(defaultView);
8325
8578
  const view = offered.includes(chosen) ? chosen : offered[0] ?? "rendered";
@@ -8378,9 +8631,38 @@ function StuffPanel({
8378
8631
  const labels = { rendered: s.viewRendered, json: s.viewJson };
8379
8632
  const pdfName = page && pdfShown ? pdfFileName(field, value, baseName) : void 0;
8380
8633
  const pageView = page !== void 0 && view !== "json";
8381
- const titled = presentation !== "app" || name !== void 0 && name !== "" && !page;
8634
+ const headsItself = !page && resultHeadsItself(field, value, presentation);
8635
+ const hostNamed = name !== void 0 && name !== "";
8636
+ const titled = presentation !== "app" || hostNamed && !page && !headsItself;
8382
8637
  const switchShown = offered.length > 1;
8383
- const headerShown = titled || downloadShown || switchShown || page && pdfName !== void 0;
8638
+ const downloadAtFoot = downloadShown && !titled && !switchShown && !page;
8639
+ const headerShown = titled || downloadShown && !downloadAtFoot || switchShown || page && pdfName !== void 0;
8640
+ const downloadButton = downloadShown && /* @__PURE__ */ jsxs(
8641
+ "button",
8642
+ {
8643
+ type: "button",
8644
+ onClick: () => void handleDownload(),
8645
+ disabled: savingShown,
8646
+ "aria-label": page ? s.downloadHtml : s.download,
8647
+ className: cn(
8648
+ // 44 pixels tall below `sm`, as every control a finger hits is.
8649
+ "inline-flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-[12px] text-muted-foreground hover:text-foreground disabled:opacity-60 max-sm:min-h-11 max-sm:px-3",
8650
+ focusOutlineClass
8651
+ ),
8652
+ children: [
8653
+ savingShown ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }),
8654
+ savingShown ? s.downloading : page ? s.downloadHtml : s.download
8655
+ ]
8656
+ }
8657
+ );
8658
+ const missedStatus = downloadShown && /* @__PURE__ */ jsx(
8659
+ "p",
8660
+ {
8661
+ role: "status",
8662
+ className: cn("shrink-0 text-[12px] text-destructive", missedShown.length === 0 && "sr-only"),
8663
+ children: missedShown.length > 0 ? s.downloadIncomplete(missedShown) : ""
8664
+ }
8665
+ );
8384
8666
  return /* @__PURE__ */ jsxs(
8385
8667
  "div",
8386
8668
  {
@@ -8396,6 +8678,7 @@ function StuffPanel({
8396
8678
  ),
8397
8679
  children: [
8398
8680
  dialog,
8681
+ hostNamed && headsItself && /* @__PURE__ */ jsx(ResultHeader, { field: named, unseen: true }),
8399
8682
  headerShown && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center justify-between gap-x-4 gap-y-1", children: [
8400
8683
  titled ? /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-1", children: /* @__PURE__ */ jsx(ResultHeader, { field: named }) }) : /* @__PURE__ */ jsx("div", {}),
8401
8684
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
@@ -8408,23 +8691,7 @@ function StuffPanel({
8408
8691
  ...pdfName ? { fileName: pdfName } : {}
8409
8692
  }
8410
8693
  ),
8411
- downloadShown && /* @__PURE__ */ jsxs(
8412
- "button",
8413
- {
8414
- type: "button",
8415
- onClick: () => void handleDownload(),
8416
- disabled: savingShown,
8417
- "aria-label": page ? s.downloadHtml : s.download,
8418
- className: cn(
8419
- "inline-flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-[12px] text-muted-foreground hover:text-foreground disabled:opacity-60",
8420
- focusOutlineClass
8421
- ),
8422
- children: [
8423
- savingShown ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }),
8424
- savingShown ? s.downloading : page ? s.downloadHtml : s.download
8425
- ]
8426
- }
8427
- ),
8694
+ !downloadAtFoot && downloadButton,
8428
8695
  switchShown && /* @__PURE__ */ jsx(
8429
8696
  "div",
8430
8697
  {
@@ -8438,7 +8705,8 @@ function StuffPanel({
8438
8705
  onClick: () => setView(id),
8439
8706
  "aria-pressed": view === id,
8440
8707
  className: cn(
8441
- "rounded px-2 py-0.5 text-[12px]",
8708
+ // 44 by 44 below `sm`, as every control a finger hits is.
8709
+ "rounded px-2 py-0.5 text-[12px] max-sm:min-h-11 max-sm:min-w-11 max-sm:px-3",
8442
8710
  focusOutlineClass,
8443
8711
  view === id ? "bg-card font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
8444
8712
  ),
@@ -8450,23 +8718,17 @@ function StuffPanel({
8450
8718
  )
8451
8719
  ] })
8452
8720
  ] }),
8453
- downloadShown && /* @__PURE__ */ jsx(
8454
- "p",
8455
- {
8456
- role: "status",
8457
- className: cn(
8458
- "shrink-0 text-[12px] text-destructive",
8459
- missedShown.length === 0 && "sr-only"
8460
- ),
8461
- children: missedShown.length > 0 ? s.downloadIncomplete(missedShown) : ""
8462
- }
8463
- ),
8721
+ !downloadAtFoot && missedStatus,
8464
8722
  view === "json" ? /* @__PURE__ */ jsx(JsonView, { value }) : page ? (
8465
8723
  // The page is the panel's main view: drawn here rather than through
8466
8724
  // `ResultField`, whose page arm would add a second control row saving
8467
8725
  // the same file, and with no box around it.
8468
8726
  /* @__PURE__ */ jsx(HtmlPreview, { content: page, downloadPdf: false, bare: true, fill: true })
8469
- ) : /* @__PURE__ */ jsx(ResultRoot, { baseName, path: [field.name], children: /* @__PURE__ */ jsx(PanelResult, { hostHeaded: !titled, children: /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: true }) }) })
8727
+ ) : /* @__PURE__ */ jsx(ResultRoot, { baseName, path: [field.name], children: /* @__PURE__ */ jsx(PanelResult, { hostHeaded: !titled, children: /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: true }) }) }),
8728
+ downloadAtFoot && /* @__PURE__ */ jsxs("div", { className: "space-y-1 pt-2", children: [
8729
+ downloadButton,
8730
+ missedStatus
8731
+ ] })
8470
8732
  ]
8471
8733
  }
8472
8734
  );
@@ -8477,6 +8739,6 @@ function pdfFileName(field, value, baseName) {
8477
8739
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
8478
8740
  }
8479
8741
 
8480
- export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, checkedAccentClass, cn, enumLabeler, fieldControlClass, fieldStringsFor, focusOutlineClass, linkTextClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
8481
- //# sourceMappingURL=chunk-RHX7LQRL.js.map
8482
- //# sourceMappingURL=chunk-RHX7LQRL.js.map
8742
+ export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, FilePicker, FilePickerEntry, FillWithExample, HtmlPreview, ImageField, JsonView, ListField, Markdown, MethodForm, NumberField, ObjectField, OptionalToggle, PdfDownloadButton, ProseField, ResultEnvProvider, ResultField, ResultRoot, SUPPORTED_LOCALES, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, StuffViewer, Switch, TextField, ToggleGroup, ToggleGroupItem, UnknownField, actionShadowClass, checkedAccentClass, cn, enumLabeler, fieldControlClass, fieldStringsFor, focusOutlineClass, headingClass, isThenable, linkTextClass, panelFlatShadowClass, panelRaisedShadowClass, panelSurfaceClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
8743
+ //# sourceMappingURL=chunk-ENK5QTUK.js.map
8744
+ //# sourceMappingURL=chunk-ENK5QTUK.js.map