@pipelex/mthds-form 0.29.0 → 0.30.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.
@@ -821,6 +821,27 @@ function MoreFooter({ pages }) {
821
821
  if (!pages.hasMore) return null;
822
822
  return /* @__PURE__ */ jsx("div", { className: "flex justify-center px-4 py-4", children: /* @__PURE__ */ jsx(ListAction, { onClick: pages.loadMore, children: s.showMore }) });
823
823
  }
824
+
825
+ // src/react/style-tokens.ts
826
+ var headingFaceClass = "[font-family:var(--font-heading,revert-layer)]";
827
+ var headingTrackingClass = "[letter-spacing:var(--heading-tracking,revert-layer)]";
828
+ var headingTrackingTightClass = "[letter-spacing:var(--heading-tracking,-0.025em)]";
829
+ var headingWeightClass = {
830
+ 500: "[font-weight:var(--heading-weight,500)]",
831
+ 600: "[font-weight:var(--heading-weight,600)]",
832
+ 700: "[font-weight:var(--heading-weight,700)]"
833
+ };
834
+ function headingClass(weight, tight = false) {
835
+ return [
836
+ headingFaceClass,
837
+ headingWeightClass[weight],
838
+ tight ? headingTrackingTightClass : headingTrackingClass
839
+ ].join(" ");
840
+ }
841
+ 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%)))]";
842
+ 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))]";
843
+ var panelFlatShadowClass = "[box-shadow:var(--panel-shadow,none)]";
844
+ 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
845
  var Dialog = DialogPrimitive.Root;
825
846
  var DialogClose = DialogPrimitive.Close;
826
847
  function closeAutoFocus(onCloseAutoFocus, returnFocus) {
@@ -871,7 +892,7 @@ var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE
871
892
  DialogPrimitive.Title,
872
893
  {
873
894
  ref,
874
- className: cn("text-[16px] font-semibold text-foreground", className),
895
+ className: cn("text-[16px] text-foreground", headingClass(600), className),
875
896
  ...props
876
897
  }
877
898
  ));
@@ -1041,7 +1062,7 @@ function FilePickerBody({
1041
1062
  return next;
1042
1063
  });
1043
1064
  const choices = [...chosen.values()];
1044
- const use8 = () => {
1065
+ const use9 = () => {
1045
1066
  if (choices.length === 0) return;
1046
1067
  onUse(choices);
1047
1068
  onClose();
@@ -1146,7 +1167,7 @@ function FilePickerBody({
1146
1167
  "button",
1147
1168
  {
1148
1169
  type: "button",
1149
- onClick: use8,
1170
+ onClick: use9,
1150
1171
  disabled: choices.length === 0,
1151
1172
  className: cn(
1152
1173
  "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",
@@ -1978,7 +1999,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
1978
1999
  value: option,
1979
2000
  variant: "outline",
1980
2001
  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",
2002
+ "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
2003
  selectedStateBorderClass
1983
2004
  ),
1984
2005
  children: [
@@ -2170,11 +2191,27 @@ function OptionalToggle({ count, expanded, onToggle, noun = "field" }) {
2170
2191
  }
2171
2192
  );
2172
2193
  }
2194
+ var StructureDepthContext = createContext(0);
2195
+ function structureGrouping(depth, labelled) {
2196
+ if (!labelled) return "none";
2197
+ return depth === 0 ? "box" : "rule";
2198
+ }
2199
+ function structureNameClass(isApp, depth) {
2200
+ if (!isApp) return "font-mono text-[13px] font-medium leading-none";
2201
+ return cn(
2202
+ depth === 0 ? "text-[16px] font-semibold leading-snug" : "text-[14.5px] font-semibold leading-snug",
2203
+ headingFaceClass
2204
+ );
2205
+ }
2173
2206
  function ObjectField({ field, value, onChange, id, error, env }) {
2174
2207
  const [showOptional, setShowOptional] = useState(false);
2175
2208
  const presentation = useFieldPresentation();
2176
2209
  const isApp = presentation === "app";
2210
+ const depth = use(StructureDepthContext);
2177
2211
  const data = value ?? {};
2212
+ const label = fieldLabel(field.title, field.name, presentation);
2213
+ const grouping = structureGrouping(depth, label.trim() !== "");
2214
+ const domId = useFieldDomId(id);
2178
2215
  const setChild = (name, childValue) => onChange({ ...data, [name]: childValue });
2179
2216
  const folded = field.fields.filter((f) => isFoldedOptional(f, ownProp(data, f.name)));
2180
2217
  const structureOpen = anyOptionalStructureOpen(field.fields, data);
@@ -2189,30 +2226,31 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2189
2226
  );
2190
2227
  }
2191
2228
  };
2192
- return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2229
+ const nameId = `${domId}--name`;
2230
+ const descriptionId = `${domId}--description`;
2231
+ const group = grouping === "none" ? {} : {
2232
+ role: "group",
2233
+ "aria-labelledby": nameId,
2234
+ "aria-describedby": field.description ? descriptionId : void 0
2235
+ };
2236
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-2", ...group, children: [
2193
2237
  /* @__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
- ),
2238
+ /* @__PURE__ */ jsx("span", { id: nameId, className: cn("text-foreground", structureNameClass(isApp, depth)), children: label }),
2204
2239
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" })
2205
2240
  ] }),
2206
- field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2241
+ field.description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2207
2242
  /* @__PURE__ */ jsxs(
2208
2243
  "div",
2209
2244
  {
2245
+ "data-structure-group": grouping,
2210
2246
  className: cn(
2211
- "space-y-4 rounded-lg border bg-card/40 p-4",
2212
- error ? "border-destructive/50" : "border-border"
2247
+ "space-y-4",
2248
+ grouping === "box" && "rounded-lg border bg-card/40 p-4 max-sm:p-3",
2249
+ grouping === "rule" && "border-l-2 pl-4 max-sm:pl-3",
2250
+ grouping !== "none" && (error ? "border-destructive/50" : "border-border")
2213
2251
  ),
2214
2252
  children: [
2215
- visible.map((child) => /* @__PURE__ */ jsx(
2253
+ /* @__PURE__ */ jsx(StructureDepthContext, { value: depth + 1, children: visible.map((child) => /* @__PURE__ */ jsx(
2216
2254
  FieldRenderer,
2217
2255
  {
2218
2256
  field: child,
@@ -2222,7 +2260,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2222
2260
  env
2223
2261
  },
2224
2262
  child.name
2225
- )),
2263
+ )) }),
2226
2264
  (folded.length > 0 || structureOpen) && /* @__PURE__ */ jsx(
2227
2265
  OptionalToggle,
2228
2266
  {
@@ -2921,7 +2959,7 @@ var AlertDialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @_
2921
2959
  AlertDialogPrimitive.Title,
2922
2960
  {
2923
2961
  ref,
2924
- className: cn("text-[16px] font-semibold text-foreground", className),
2962
+ className: cn("text-[16px] text-foreground", headingClass(600), className),
2925
2963
  ...props
2926
2964
  }
2927
2965
  ));
@@ -4525,7 +4563,7 @@ function FileNameDialog({ stem: stem2, extension, onConfirm, onCancel }) {
4525
4563
  onKeyDown,
4526
4564
  className: "w-full max-w-sm space-y-3 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg",
4527
4565
  children: [
4528
- /* @__PURE__ */ jsx("h2", { id: titleId, className: "text-[14px] font-medium", children: s.fileNameTitle }),
4566
+ /* @__PURE__ */ jsx("h2", { id: titleId, className: cn("text-[14px]", headingClass(500)), children: s.fileNameTitle }),
4529
4567
  /* @__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
4568
  /* @__PURE__ */ jsx(
4531
4569
  "input",
@@ -5204,7 +5242,8 @@ function PdfDownloadButton({
5204
5242
  title: failed ? s.downloadPdfFailed : name,
5205
5243
  ...failed ? { "aria-describedby": statusId } : {},
5206
5244
  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",
5245
+ // 44 pixels tall below `sm`, as every control a finger hits is.
5246
+ "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
5247
  focusOutlineClass,
5209
5248
  primary ? "bg-primary text-primary-foreground hover:bg-primary/90" : "border border-border text-muted-foreground hover:text-foreground",
5210
5249
  failed && (primary ? "bg-destructive text-white hover:bg-destructive/90" : "text-destructive"),
@@ -5477,7 +5516,7 @@ function ImageViewer({
5477
5516
  children: [
5478
5517
  /* @__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
5518
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
5480
- /* @__PURE__ */ jsx(DialogTitle, { className: "truncate text-[14px] font-medium", children: picture.name }),
5519
+ /* @__PURE__ */ jsx(DialogTitle, { className: cn("truncate text-[14px]", headingWeightClass[500]), children: picture.name }),
5481
5520
  several && // A live region from the moment the viewer opens, so each move
5482
5521
  // is announced: one inserted with its text already in it is not.
5483
5522
  /* @__PURE__ */ jsx(
@@ -5638,15 +5677,24 @@ function Markdown({ text, className, proseImages, citations }) {
5638
5677
  const mode = proseImages ?? fromEnv;
5639
5678
  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
5679
  }
5680
+ function opensWithHeading(text) {
5681
+ const body = text.slice(LEADING_BLANK_LINES.exec(text)?.[0].length ?? 0);
5682
+ const end = body.search(BLANK_LINE);
5683
+ const opening = end === -1 ? body : body.slice(0, end);
5684
+ const first = marked.lexer(opening, LEX_OPTIONS).find((token) => token.type !== "space");
5685
+ return first?.type === "heading";
5686
+ }
5687
+ var LEADING_BLANK_LINES = /^(?:[ \t]*\r?\n)*/;
5688
+ var BLANK_LINE = /\r?\n[ \t]*\r?\n/;
5641
5689
  var ProseImagesContext = React9.createContext("link");
5642
5690
  var CitationsContext = React9.createContext(void 0);
5643
5691
  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"
5692
+ 1: cn("text-[16px]", headingClass(600)),
5693
+ 2: cn("text-[14.5px]", headingClass(600)),
5694
+ 3: cn("text-[13.5px]", headingClass(600)),
5695
+ 4: cn("text-[13px]", headingClass(600)),
5696
+ 5: cn("text-[13px]", headingClass(600)),
5697
+ 6: cn("text-[13px]", headingClass(600))
5650
5698
  };
5651
5699
  function Blocks({ tokens }) {
5652
5700
  return /* @__PURE__ */ jsx(Fragment, { children: tokens.map((token, index) => /* @__PURE__ */ jsx(Block, { token }, index)) });
@@ -6350,11 +6398,11 @@ function fencedBlockEnd(text, start) {
6350
6398
  }
6351
6399
  return text.length;
6352
6400
  }
6353
- var BLANK_LINE = /\n[ \t]*\n/g;
6401
+ var BLANK_LINE2 = /\n[ \t]*\n/g;
6354
6402
  function codeSpanEnd(text, start) {
6355
6403
  const open = backtickRun(text, start);
6356
- BLANK_LINE.lastIndex = start;
6357
- const blank = BLANK_LINE.exec(text);
6404
+ BLANK_LINE2.lastIndex = start;
6405
+ const blank = BLANK_LINE2.exec(text);
6358
6406
  const limit = blank ? blank.index : text.length;
6359
6407
  let cursor = start + open;
6360
6408
  for (; ; ) {
@@ -6603,13 +6651,21 @@ function Described({ description, children }) {
6603
6651
  /* @__PURE__ */ jsx(TooltipContent, { children: description })
6604
6652
  ] }) });
6605
6653
  }
6606
- function ResultHeader({ field }) {
6607
- return /* @__PURE__ */ jsx(Label2, { field, describe: true });
6654
+ function ResultHeader({ field, unseen = false }) {
6655
+ return /* @__PURE__ */ jsx(Label2, { field, describe: true, unseen });
6608
6656
  }
6609
- function Label2({ field, describe = false }) {
6657
+ function Label2({
6658
+ field,
6659
+ describe = false,
6660
+ unseen = false
6661
+ }) {
6610
6662
  const presentation = useFieldPresentation();
6611
6663
  const isApp = presentation === "app";
6612
6664
  const labelText = fieldLabel(field.title, field.name, presentation);
6665
+ if (unseen) {
6666
+ if (labelText.trim() === "") return null;
6667
+ return /* @__PURE__ */ jsx("span", { className: "sr-only", children: describe && field.description ? `${labelText}: ${field.description}` : labelText });
6668
+ }
6613
6669
  if (labelText.trim() === "") {
6614
6670
  return /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: conceptCategory(field) });
6615
6671
  }
@@ -7336,11 +7392,25 @@ var TOGGLE = cn(
7336
7392
  );
7337
7393
  var STACK_NEVER_UP_TO = 3;
7338
7394
  var STACK_ALWAYS_BEYOND = 6;
7339
- function stackingFor(columns) {
7340
- if (columns <= STACK_NEVER_UP_TO) return "never";
7395
+ function stackingFor(columns, rowsOpen) {
7396
+ if (columns <= STACK_NEVER_UP_TO) {
7397
+ return columns === STACK_NEVER_UP_TO && rowsOpen ? "phone" : "never";
7398
+ }
7341
7399
  return columns > STACK_ALWAYS_BEYOND ? "always" : "narrow";
7342
7400
  }
7343
7401
  var STACKED = {
7402
+ phone: {
7403
+ table: "max-sm:block",
7404
+ head: "max-sm:hidden",
7405
+ body: "max-sm:block",
7406
+ row: "max-sm:relative max-sm:block max-sm:px-3 max-sm:py-2",
7407
+ rowWithToggle: "max-sm:pl-12",
7408
+ toggle: "max-sm:absolute max-sm:top-0 max-sm:left-0 max-sm:p-0",
7409
+ 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",
7410
+ label: "max-sm:block",
7411
+ detail: "max-sm:block",
7412
+ detailInner: "max-sm:px-0 max-sm:py-1"
7413
+ },
7344
7414
  narrow: {
7345
7415
  table: "@max-[640px]:block",
7346
7416
  head: "@max-[640px]:hidden",
@@ -7369,21 +7439,34 @@ var STACKED = {
7369
7439
  var FIT_CELL = "min-w-[min(8ch,14cqw)] wrap-anywhere [&>span]:whitespace-normal";
7370
7440
  var FIT_NAME = "min-w-[min(16ch,28cqw)] wrap-anywhere [&>span]:whitespace-normal";
7371
7441
  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)]";
7442
+ var FIT_CHEVRON = "[--fit-chevron:var(--expand-column,2rem)]";
7443
+ var FIT_NO_CHEVRON = "[--fit-chevron:0px]";
7372
7444
  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)]"
7445
+ 2: "[--fit-share:calc(72cqw_-_3rem_-_var(--fit-chevron,0px))]",
7446
+ 3: "[--fit-share:calc((72cqw_-_4.5rem_-_var(--fit-chevron,0px))/2)]",
7447
+ 4: "[--fit-share:calc((72cqw_-_6rem_-_var(--fit-chevron,0px))/3)]",
7448
+ 5: "[--fit-share:calc((72cqw_-_7.5rem_-_var(--fit-chevron,0px))/4)]",
7449
+ 6: "[--fit-share:calc((72cqw_-_9rem_-_var(--fit-chevron,0px))/5)]"
7378
7450
  };
7379
7451
  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)]"
7452
+ 1: "[--fit-share:calc(100cqw_-_1.5rem_-_var(--fit-chevron,0px))]",
7453
+ 2: "[--fit-share:calc((100cqw_-_3rem_-_var(--fit-chevron,0px))/2)]",
7454
+ 3: "[--fit-share:calc((100cqw_-_4.5rem_-_var(--fit-chevron,0px))/3)]",
7455
+ 4: "[--fit-share:calc((100cqw_-_6rem_-_var(--fit-chevron,0px))/4)]",
7456
+ 5: "[--fit-share:calc((100cqw_-_7.5rem_-_var(--fit-chevron,0px))/5)]",
7457
+ 6: "[--fit-share:calc((100cqw_-_9rem_-_var(--fit-chevron,0px))/6)]"
7386
7458
  };
7459
+ function identifierWords(label) {
7460
+ const words = label.split("_");
7461
+ if (words.length === 1) return label;
7462
+ return words.map(
7463
+ (word, index) => index === words.length - 1 ? word : /* @__PURE__ */ jsxs(Fragment$1, { children: [
7464
+ word,
7465
+ "_",
7466
+ /* @__PURE__ */ jsx("wbr", {})
7467
+ ] }, index)
7468
+ );
7469
+ }
7387
7470
  function cellWords(column, cell, presentation) {
7388
7471
  return typeof cell === "string" || typeof cell === "number" ? cellTitle(column, cell, presentation) : void 0;
7389
7472
  }
@@ -7424,7 +7507,7 @@ function ObjectTable({
7424
7507
  const { shown, hidden, name } = chooseColumns(columns, budget);
7425
7508
  const canExpand = hidden > 0 || shown.some((column) => column !== name && !fitsACellWhole(column));
7426
7509
  const fits = layout === "stack";
7427
- const stacking = fits ? stackingFor(shown.length) : "never";
7510
+ const stacking = fits ? stackingFor(shown.length, canExpand) : "never";
7428
7511
  const stacked = stacking === "never" ? void 0 : STACKED[stacking];
7429
7512
  const nameShown = name !== void 0 && shown.includes(name);
7430
7513
  const fitShare = fits ? (nameShown ? FIT_SHARE_BESIDE_NAME : FIT_SHARE_WITHOUT_NAME)[shown.length] : void 0;
@@ -7439,6 +7522,19 @@ function ObjectTable({
7439
7522
  observer.observe(scroller);
7440
7523
  return () => observer.disconnect();
7441
7524
  }, [fits]);
7525
+ const [overflows, setOverflows] = useState(false);
7526
+ useEffect(() => {
7527
+ const scroller = scrollerRef.current;
7528
+ if (!scroller || !fits) return;
7529
+ const measure = () => setOverflows(scroller.scrollWidth > scroller.clientWidth);
7530
+ measure();
7531
+ if (typeof ResizeObserver === "undefined") return;
7532
+ const observer = new ResizeObserver(measure);
7533
+ observer.observe(scroller);
7534
+ const content = scroller.firstElementChild;
7535
+ if (content) observer.observe(content);
7536
+ return () => observer.disconnect();
7537
+ }, [fits]);
7442
7538
  const toggle = (index) => setExpanded((current) => {
7443
7539
  const next = new Set(current);
7444
7540
  if (!next.delete(index)) next.add(index);
@@ -7465,16 +7561,16 @@ function ObjectTable({
7465
7561
  //
7466
7562
  // Under `tableLayout="stack"` the box is the SIZE CONTAINER its stacking
7467
7563
  // 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.
7564
+ // viewport's, and it takes no focus while nothing scrolls, since the stop
7565
+ // would have nothing to do. It still clips to its rounded border, and takes
7566
+ // focus again only while its headers' words make it overflow (`overflows`).
7471
7567
  /* @__PURE__ */ jsx(
7472
7568
  "div",
7473
7569
  {
7474
7570
  ref: scrollerRef,
7475
7571
  role: "group",
7476
7572
  "aria-label": label,
7477
- ...fits ? {} : { tabIndex: 0 },
7573
+ ...fits && !overflows ? {} : { tabIndex: 0 },
7478
7574
  className: cn(
7479
7575
  "overflow-x-auto rounded-lg border border-border",
7480
7576
  focusOutlineInsetClass,
@@ -7484,7 +7580,12 @@ function ObjectTable({
7484
7580
  children: /* @__PURE__ */ jsxs(
7485
7581
  "table",
7486
7582
  {
7487
- className: cn("w-full min-w-full border-collapse text-[13px]", stacked?.table, fitShare),
7583
+ className: cn(
7584
+ "w-full min-w-full border-collapse text-[13px]",
7585
+ stacked?.table,
7586
+ fitShare,
7587
+ fitShare !== void 0 && (canExpand ? FIT_CHEVRON : FIT_NO_CHEVRON)
7588
+ ),
7488
7589
  children: [
7489
7590
  /* @__PURE__ */ jsx("thead", { className: stacked?.head, children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-border bg-muted text-left", children: [
7490
7591
  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 +7593,20 @@ function ObjectTable({
7492
7593
  "th",
7493
7594
  {
7494
7595
  scope: "col",
7495
- ...floorsWords(column) ? { "data-fit": fieldLabel(column.title, column.name, presentation) } : {},
7496
7596
  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",
7597
+ // One line, or, in a table that fits its box, wrapped between
7598
+ // words and never inside one: "Quantity" over three digits is
7599
+ // a header, "Quantit" over "y" is damage. So a header's
7600
+ // longest word is its column's floor, with no share capping
7601
+ // it, as a value's is capped: a header's words are the label
7602
+ // the descriptor states, not a payload's token of any length.
7603
+ // Where the box cannot hold every header's longest word, the
7604
+ // table scrolls inside it (see `overflows`).
7605
+ fits ? "wrap-normal" : "whitespace-nowrap",
7500
7606
  "px-3 py-2 text-[12px] font-semibold text-foreground",
7501
- presentation === "app" ? void 0 : "font-mono",
7502
- floorsWords(column) && WORD_FLOOR
7607
+ presentation === "app" ? void 0 : "font-mono"
7503
7608
  ),
7504
- children: /* @__PURE__ */ jsx(Described, { description: column.description, children: fieldLabel(column.title, column.name, presentation) })
7609
+ children: /* @__PURE__ */ jsx(Described, { description: column.description, children: presentation === "studio" ? identifierWords(fieldLabel(column.title, column.name, presentation)) : fieldLabel(column.title, column.name, presentation) })
7505
7610
  },
7506
7611
  column.name
7507
7612
  ))
@@ -7604,13 +7709,16 @@ function CompositeValue({ members }) {
7604
7709
  const isApp = presentation === "app";
7605
7710
  return /* @__PURE__ */ jsx("div", { className: "space-y-3", children: members.map((member) => {
7606
7711
  const label = isApp ? humanizeFieldName(member.name) : member.name;
7712
+ const text = memberText(member.value);
7713
+ const headed = isApp && text !== void 0 && opensWithHeading(text);
7607
7714
  return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
7608
7715
  /* @__PURE__ */ jsx(
7609
7716
  "span",
7610
7717
  {
7611
7718
  className: cn(
7612
7719
  "font-semibold text-foreground",
7613
- isApp ? "text-[13.5px]" : "font-mono text-[12px]"
7720
+ isApp ? "text-[13.5px]" : "font-mono text-[12px]",
7721
+ headed && "sr-only"
7614
7722
  ),
7615
7723
  children: label
7616
7724
  }
@@ -7649,6 +7757,12 @@ function NativeValue({ value, label }) {
7649
7757
  return /* @__PURE__ */ jsx(RawValue, { value, compact: false });
7650
7758
  }
7651
7759
  }
7760
+ function memberText(value) {
7761
+ if (typeof value === "string") return value === "" ? void 0 : value;
7762
+ if (!isRecord(value)) return void 0;
7763
+ const content = readMemberContent(value);
7764
+ return content?.model === "text" && content.text !== "" ? content.text : void 0;
7765
+ }
7652
7766
  function NativeList({ items, label }) {
7653
7767
  if (items.length === 0) return /* @__PURE__ */ jsx(Absent, {});
7654
7768
  if (items.every(isPlainRecord)) return /* @__PURE__ */ jsx(RecordsTable, { items, label });
@@ -7981,6 +8095,18 @@ function SearchSource({ source }) {
7981
8095
  function isLongText(value) {
7982
8096
  return typeof value === "string" && (value.includes("\n") || value.length > CELL_LENGTH_LIMIT);
7983
8097
  }
8098
+ function typesetText(field, value, isApp) {
8099
+ if (typeof value !== "string" || value === "") return void 0;
8100
+ if (field.kind === "prose") return value;
8101
+ return isApp && field.kind === "text" && isLongText(value) ? value : void 0;
8102
+ }
8103
+ function namesItself(field, value, isApp) {
8104
+ const typeset = typesetText(field, value, isApp);
8105
+ return isApp && typeset !== void 0 && opensWithHeading(typeset);
8106
+ }
8107
+ function resultHeadsItself(field, value, presentation) {
8108
+ return namesItself(field, unwrap(field, value), presentation === "app");
8109
+ }
7984
8110
  var PanelResultContext = createContext(null);
7985
8111
  var TITLED_PANEL = { hostHeaded: false };
7986
8112
  var HOST_HEADED_PANEL = { hostHeaded: true };
@@ -8210,14 +8336,35 @@ function ResultNode({
8210
8336
  case "prose":
8211
8337
  case "number":
8212
8338
  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
- ] });
8339
+ case "unknown": {
8340
+ const typeset = typesetText(field, unwrapped, isApp);
8341
+ const headed = !hideLabel && namesItself(field, unwrapped, isApp);
8342
+ const labelDrawn = !hideLabel && !headed;
8343
+ return /* @__PURE__ */ jsxs(
8344
+ "div",
8345
+ {
8346
+ className: cn(
8347
+ field.kind === "image" ? "space-y-1.5" : "space-y-0.5",
8348
+ // Holds the copy control's float, when it floats.
8349
+ copyText !== void 0 && !labelDrawn && "flow-root"
8350
+ ),
8351
+ children: [
8352
+ copyText === void 0 ? (
8353
+ // A text that heads itself always has something to copy, so this
8354
+ // arm draws the label exactly when it is not hidden.
8355
+ header
8356
+ ) : labelDrawn ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
8357
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
8358
+ /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText })
8359
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
8360
+ headed && /* @__PURE__ */ jsx(Label2, { field, describe: true, unseen: true }),
8361
+ /* @__PURE__ */ jsx("div", { className: "float-right ml-2", children: /* @__PURE__ */ jsx(CopyButton, { value: copyText, label: s.copyText }) })
8362
+ ] }),
8363
+ typeset !== void 0 ? /* @__PURE__ */ jsx(Markdown, { text: typeset }) : field.kind === "document" && whole ? /* @__PURE__ */ jsx(DocumentValue, { value: unwrapped, whole: true }) : /* @__PURE__ */ jsx(LeafValue, { field, value })
8364
+ ]
8365
+ }
8366
+ );
8367
+ }
8221
8368
  }
8222
8369
  return null;
8223
8370
  }
@@ -8257,9 +8404,13 @@ function highlight(json) {
8257
8404
  }
8258
8405
  return out;
8259
8406
  }
8407
+ var noSubscription = () => () => {
8408
+ };
8409
+ var hasClipboard = () => typeof navigator !== "undefined" && Boolean(navigator.clipboard);
8260
8410
  function JsonView({ value }) {
8261
8411
  const s = useFieldStrings();
8262
8412
  const [copied, setCopied] = useState(false);
8413
+ const canCopy = useSyncExternalStore(noSubscription, hasClipboard, () => false);
8263
8414
  useEffect(() => {
8264
8415
  if (!copied) return;
8265
8416
  const timer = window.setTimeout(() => setCopied(false), 1500);
@@ -8270,7 +8421,7 @@ function JsonView({ value }) {
8270
8421
  return /* @__PURE__ */ jsx(Absent, {});
8271
8422
  }
8272
8423
  return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
8273
- typeof navigator !== "undefined" && navigator.clipboard && /* @__PURE__ */ jsx(
8424
+ canCopy && /* @__PURE__ */ jsx(
8274
8425
  "button",
8275
8426
  {
8276
8427
  type: "button",
@@ -8279,13 +8430,27 @@ function JsonView({ value }) {
8279
8430
  },
8280
8431
  "aria-label": s.copyJson,
8281
8432
  className: cn(
8282
- "absolute right-2 top-2 rounded border border-border bg-card p-1 text-muted-foreground hover:text-foreground",
8433
+ // 44 by 44 on a phone, like every control a finger taps.
8434
+ "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
8435
  focusOutlineClass
8284
8436
  ),
8285
8437
  children: copied ? /* @__PURE__ */ jsx(Check, { "aria-hidden": true, className: "size-3.5" }) : /* @__PURE__ */ jsx(Copy, { "aria-hidden": true, className: "size-3.5" })
8286
8438
  }
8287
8439
  ),
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) })
8440
+ /* @__PURE__ */ jsx(
8441
+ "pre",
8442
+ {
8443
+ role: "group",
8444
+ "aria-label": s.viewJson,
8445
+ tabIndex: 0,
8446
+ className: cn(
8447
+ "overflow-x-auto rounded-lg border border-border bg-card/40 px-3.5 py-3 pr-12 font-mono text-[12px] leading-relaxed",
8448
+ canCopy && "max-sm:min-h-[60px] max-sm:pr-14",
8449
+ focusOutlineClass
8450
+ ),
8451
+ children: highlight(text)
8452
+ }
8453
+ )
8289
8454
  ] });
8290
8455
  }
8291
8456
  function isShownResult(attempt, field, baseName, value) {
@@ -8378,9 +8543,38 @@ function StuffPanel({
8378
8543
  const labels = { rendered: s.viewRendered, json: s.viewJson };
8379
8544
  const pdfName = page && pdfShown ? pdfFileName(field, value, baseName) : void 0;
8380
8545
  const pageView = page !== void 0 && view !== "json";
8381
- const titled = presentation !== "app" || name !== void 0 && name !== "" && !page;
8546
+ const headsItself = !page && resultHeadsItself(field, value, presentation);
8547
+ const hostNamed = name !== void 0 && name !== "";
8548
+ const titled = presentation !== "app" || hostNamed && !page && !headsItself;
8382
8549
  const switchShown = offered.length > 1;
8383
- const headerShown = titled || downloadShown || switchShown || page && pdfName !== void 0;
8550
+ const downloadAtFoot = downloadShown && !titled && !switchShown && !page;
8551
+ const headerShown = titled || downloadShown && !downloadAtFoot || switchShown || page && pdfName !== void 0;
8552
+ const downloadButton = downloadShown && /* @__PURE__ */ jsxs(
8553
+ "button",
8554
+ {
8555
+ type: "button",
8556
+ onClick: () => void handleDownload(),
8557
+ disabled: savingShown,
8558
+ "aria-label": page ? s.downloadHtml : s.download,
8559
+ className: cn(
8560
+ // 44 pixels tall below `sm`, as every control a finger hits is.
8561
+ "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",
8562
+ focusOutlineClass
8563
+ ),
8564
+ children: [
8565
+ savingShown ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(Download, { className: "h-3.5 w-3.5" }),
8566
+ savingShown ? s.downloading : page ? s.downloadHtml : s.download
8567
+ ]
8568
+ }
8569
+ );
8570
+ const missedStatus = downloadShown && /* @__PURE__ */ jsx(
8571
+ "p",
8572
+ {
8573
+ role: "status",
8574
+ className: cn("shrink-0 text-[12px] text-destructive", missedShown.length === 0 && "sr-only"),
8575
+ children: missedShown.length > 0 ? s.downloadIncomplete(missedShown) : ""
8576
+ }
8577
+ );
8384
8578
  return /* @__PURE__ */ jsxs(
8385
8579
  "div",
8386
8580
  {
@@ -8396,6 +8590,7 @@ function StuffPanel({
8396
8590
  ),
8397
8591
  children: [
8398
8592
  dialog,
8593
+ hostNamed && headsItself && /* @__PURE__ */ jsx(ResultHeader, { field: named, unseen: true }),
8399
8594
  headerShown && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 flex-wrap items-center justify-between gap-x-4 gap-y-1", children: [
8400
8595
  titled ? /* @__PURE__ */ jsx("div", { className: "min-w-0 space-y-1", children: /* @__PURE__ */ jsx(ResultHeader, { field: named }) }) : /* @__PURE__ */ jsx("div", {}),
8401
8596
  /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
@@ -8408,23 +8603,7 @@ function StuffPanel({
8408
8603
  ...pdfName ? { fileName: pdfName } : {}
8409
8604
  }
8410
8605
  ),
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
- ),
8606
+ !downloadAtFoot && downloadButton,
8428
8607
  switchShown && /* @__PURE__ */ jsx(
8429
8608
  "div",
8430
8609
  {
@@ -8438,7 +8617,8 @@ function StuffPanel({
8438
8617
  onClick: () => setView(id),
8439
8618
  "aria-pressed": view === id,
8440
8619
  className: cn(
8441
- "rounded px-2 py-0.5 text-[12px]",
8620
+ // 44 by 44 below `sm`, as every control a finger hits is.
8621
+ "rounded px-2 py-0.5 text-[12px] max-sm:min-h-11 max-sm:min-w-11 max-sm:px-3",
8442
8622
  focusOutlineClass,
8443
8623
  view === id ? "bg-card font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
8444
8624
  ),
@@ -8450,23 +8630,17 @@ function StuffPanel({
8450
8630
  )
8451
8631
  ] })
8452
8632
  ] }),
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
- ),
8633
+ !downloadAtFoot && missedStatus,
8464
8634
  view === "json" ? /* @__PURE__ */ jsx(JsonView, { value }) : page ? (
8465
8635
  // The page is the panel's main view: drawn here rather than through
8466
8636
  // `ResultField`, whose page arm would add a second control row saving
8467
8637
  // the same file, and with no box around it.
8468
8638
  /* @__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 }) }) })
8639
+ ) : /* @__PURE__ */ jsx(ResultRoot, { baseName, path: [field.name], children: /* @__PURE__ */ jsx(PanelResult, { hostHeaded: !titled, children: /* @__PURE__ */ jsx(ResultField, { field, value, hideLabel: true }) }) }),
8640
+ downloadAtFoot && /* @__PURE__ */ jsxs("div", { className: "space-y-1 pt-2", children: [
8641
+ downloadButton,
8642
+ missedStatus
8643
+ ] })
8470
8644
  ]
8471
8645
  }
8472
8646
  );
@@ -8477,6 +8651,6 @@ function pdfFileName(field, value, baseName) {
8477
8651
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
8478
8652
  }
8479
8653
 
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
8654
+ 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, linkTextClass, panelFlatShadowClass, panelRaisedShadowClass, panelSurfaceClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
8655
+ //# sourceMappingURL=chunk-JCFALTTY.js.map
8656
+ //# sourceMappingURL=chunk-JCFALTTY.js.map