@pipelex/mthds-form 0.34.0 → 0.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10,13 +10,13 @@ import { conceptCategory } from './chunk-FABY3DEY.js';
10
10
  import { acceptLabel, acceptMap, isAcceptedFile, normalizeMimeType, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
11
11
  import { shownAsOptional, isFoldedOptional, anyOptionalStructureOpen, isOptionalStructure, computeReadiness, gateRefusal, optionalStructureOpen, mustBeFilled } from './chunk-TF4RTKLH.js';
12
12
  import { ownProp } from './chunk-V3OV2XQT.js';
13
- import { toStoredDateValue, toDateInputValue } from './chunk-GKV77RAS.js';
13
+ import { toDateInputValue, toStoredDateValue, asCalendarDate } from './chunk-GKV77RAS.js';
14
14
  import { clsx } from 'clsx';
15
15
  import { twMerge } from 'tailwind-merge';
16
- import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image as Image$1, FileText, CalendarDays, Type, Folder, Search, X, ImageIcon, Loader2, ImageOff, Upload, Link2, EyeOff, Eye, Minus, Plus, Trash2, Sparkles, MoreHorizontal, TriangleAlert, FileDown, Copy, Download, ChevronRight, ExternalLink, ChevronLeft } from 'lucide-react';
16
+ import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image as Image$1, FileText, CalendarDays, Type, Folder, Search, X, ImageIcon, Loader2, ImageOff, Upload, Link2, EyeOff, Eye, Minus, Plus, Trash2, ClipboardPaste, MoreHorizontal, TriangleAlert, FileDown, Copy, Download, ChevronRight, ExternalLink, ChevronLeft } from 'lucide-react';
17
17
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
18
18
  import * as React9 from 'react';
19
- import { createContext, use, useId, useMemo, useContext, useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1, useSyncExternalStore } from 'react';
19
+ import { createContext, use, useMemo, useContext, useId, useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1, useSyncExternalStore } from 'react';
20
20
  import * as SwitchPrimitives from '@radix-ui/react-switch';
21
21
  import { useDropzone } from 'react-dropzone';
22
22
  import * as DialogPrimitive from '@radix-ui/react-dialog';
@@ -84,100 +84,94 @@ function fieldLabel(title, name, presentation) {
84
84
  if (title !== void 0) return title;
85
85
  return presentation === "app" ? humanizeFieldName(name) : name;
86
86
  }
87
-
88
- // src/react/role-tokens.ts
89
- var linkTextClass = "text-[color:var(--link,var(--primary,hsl(240_5.9%_10%)))]";
90
- var selectedFillClass = "bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
91
- var selectedStateFillClass = "data-[state=on]:bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
92
- var selectedCheckedFillClass = "aria-checked:bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
93
- var selectedFillStrongClass = "bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_15%,transparent))]";
94
- var selectedBorderClass = "border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
95
- var selectedStateBorderClass = "data-[state=on]:border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
96
- var selectedHoverBorderClass = "hover:border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
97
- var checkedAccentClass = "accent-[color:var(--link,var(--primary,hsl(240_5.9%_10%)))]";
98
- var focusOutlineClass = "focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring";
99
- var focusWithinOutlineClass = "focus-within:outline-solid focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-ring";
100
- var focusOutlineInsetClass = "focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring";
101
- var frameFocusOutlineClass = "data-focus-visible:outline-solid data-focus-visible:outline-2 data-focus-visible:-outline-offset-2 data-focus-visible:outline-ring";
102
- var Switch = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
103
- SwitchPrimitives.Root,
104
- {
105
- className: cn(
106
- "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted-foreground",
107
- focusOutlineClass,
108
- "max-sm:relative max-sm:after:absolute max-sm:after:-inset-x-0.5 max-sm:after:-inset-y-3 max-sm:after:content-['']",
109
- className
110
- ),
111
- ...props,
112
- ref,
113
- children: /* @__PURE__ */ jsx(
114
- SwitchPrimitives.Thumb,
115
- {
116
- className: cn(
117
- "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
118
- )
119
- }
120
- )
121
- }
122
- ));
123
- Switch.displayName = SwitchPrimitives.Root.displayName;
124
- var FieldDomIdContext = createContext(null);
125
- function FieldDomIdProvider({
126
- prefix,
127
- children
128
- }) {
129
- const generated = useId();
130
- return /* @__PURE__ */ jsx(FieldDomIdContext, { value: prefix ?? generated, children });
131
- }
132
- function useFieldDomId(path) {
133
- const scoped = use(FieldDomIdContext);
134
- const local = useId();
135
- const prefix = scoped ?? local;
136
- return prefix === "" ? path : `${prefix}-${path}`;
137
- }
138
- function BooleanField({ field, value, onChange, id, disabled }) {
139
- const presentation = useFieldPresentation();
140
- const isApp = presentation === "app";
141
- const domId = useFieldDomId(id);
142
- return /* @__PURE__ */ jsxs(
143
- "div",
144
- {
145
- className: cn(
146
- "flex items-center justify-between gap-4 rounded-md border border-border bg-input px-3.5 py-3",
147
- disabled && "opacity-50"
148
- ),
149
- children: [
150
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
151
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
152
- /* @__PURE__ */ jsx(
153
- "label",
154
- {
155
- htmlFor: domId,
156
- className: cn(
157
- "text-[13px] font-medium leading-none text-foreground",
158
- isApp ? "text-[13.5px]" : "font-mono"
159
- ),
160
- children: fieldLabel(field.title, field.name, presentation)
161
- }
162
- ),
163
- !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "boolean" })
164
- ] }),
165
- field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description })
166
- ] }),
167
- /* @__PURE__ */ jsx(
168
- Switch,
169
- {
170
- id: domId,
171
- checked: value ?? false,
172
- onCheckedChange: onChange,
173
- disabled,
174
- className: "shrink-0"
175
- }
176
- )
177
- ]
87
+ function shownDescription(description, label, kinds, presentation) {
88
+ if (!description || presentation !== "app") return description || void 0;
89
+ if (label.trim() === "") return void 0;
90
+ return descriptionAddsToLabel(description, label, kinds) ? description : void 0;
91
+ }
92
+ function shownItemDescription(itemDescription, listLabel, listDescription, kinds, presentation) {
93
+ if (!itemDescription || presentation !== "app") return void 0;
94
+ if (itemDescription === listDescription) return void 0;
95
+ const said = listDescription ? `${listLabel} ${listDescription}` : listLabel;
96
+ return descriptionAddsToLabel(itemDescription, said, kinds) ? itemDescription : void 0;
97
+ }
98
+ function descriptionAddsToLabel(description, label, kinds) {
99
+ if (label.trim() === "") return true;
100
+ const words = contentStems(description);
101
+ if (words.length === 0) return true;
102
+ const said = new Set(contentStems(label));
103
+ for (const kind of kinds) for (const word of KIND_WORDS[kind]) said.add(stem(word));
104
+ return words.some((word) => !said.has(word));
105
+ }
106
+ var KIND_WORDS = {
107
+ text: ["text"],
108
+ date: ["date", "day"],
109
+ document: ["document", "file"],
110
+ image: ["image", "picture", "photo"],
111
+ number: ["number"],
112
+ boolean: [],
113
+ choice: [],
114
+ structured: [],
115
+ list: ["list"]
116
+ };
117
+ var FILLER = /* @__PURE__ */ new Set([
118
+ "a",
119
+ "an",
120
+ "the",
121
+ "of",
122
+ "for",
123
+ "to",
124
+ "in",
125
+ "on",
126
+ "at",
127
+ "by",
128
+ "with",
129
+ "and",
130
+ "or",
131
+ "is",
132
+ "are",
133
+ "be",
134
+ "it",
135
+ "its",
136
+ "this",
137
+ "that",
138
+ "these",
139
+ "those",
140
+ "their",
141
+ "they",
142
+ "them",
143
+ "his",
144
+ "her",
145
+ "your",
146
+ "you",
147
+ "our",
148
+ "we",
149
+ "if",
150
+ "any",
151
+ "as",
152
+ "from",
153
+ "some",
154
+ "one"
155
+ ]);
156
+ function contentStems(text) {
157
+ return (text.normalize("NFC").toLowerCase().replace(/['’]s\b/gu, "").match(WORD) ?? []).filter((word) => !FILLER.has(word)).map(stem);
158
+ }
159
+ var WORD = /[\p{L}\p{M}\p{N}]+|[\p{S}%‰#@]/gu;
160
+ function stem(word) {
161
+ for (const [suffix, floor] of SUFFIXES) {
162
+ if (word.endsWith(suffix) && word.length - suffix.length >= floor) {
163
+ return word.slice(0, -suffix.length);
178
164
  }
179
- );
180
- }
165
+ }
166
+ return word;
167
+ }
168
+ var SUFFIXES = [
169
+ ["ing", 4],
170
+ ["ed", 4],
171
+ ["es", 4],
172
+ ["s", 3],
173
+ ["e", 4]
174
+ ];
181
175
 
182
176
  // src/react/locales/calendar.ts
183
177
  function dayNumber(date) {
@@ -216,7 +210,7 @@ function someNames(names, more) {
216
210
  return AND.format(more > 0 ? [...names, `${more} more`] : names);
217
211
  }
218
212
  var EN_FIELD_STRINGS = {
219
- optionalBadge: "optional",
213
+ optionalBadge: "Optional",
220
214
  selectPlaceholder: "Select\u2026",
221
215
  typeValuePlaceholder: "Type a value\u2026",
222
216
  writeHerePlaceholder: "Write here\u2026",
@@ -231,8 +225,8 @@ var EN_FIELD_STRINGS = {
231
225
  fieldsCount: (count) => count === 1 ? "1 field" : `${count} fields`,
232
226
  uploading: "Uploading\u2026",
233
227
  dropToUpload: "Drop to upload",
234
- dropOrBrowse: "Drop a file or click to browse",
235
- pasteUrlInstead: "paste a URL instead",
228
+ dropOrBrowse: "Choose a file or drop it here",
229
+ pasteUrlInstead: "Paste a link instead",
236
230
  uploadUnavailable: "Files cannot be uploaded here. Paste a link to the file instead.",
237
231
  urlPlaceholder: "https://\u2026",
238
232
  fileUrlAria: (label) => label ? `Link to the file for ${label}` : "Link to the file",
@@ -282,7 +276,7 @@ var EN_FIELD_STRINGS = {
282
276
  nextImage: "Next picture",
283
277
  openOriginal: "Open the original",
284
278
  unsupportedFileType: (formats) => `That file type is not supported. Accepted formats: ${formats}.`,
285
- dropFilesOrBrowse: "Drop files or click to browse",
279
+ dropFilesOrBrowse: "Choose files or drop them here",
286
280
  dropFilesToAdd: "Drop to add the files",
287
281
  addFilesAria: (label) => label ? `Add files to ${label}` : "Add files",
288
282
  filesAdded: (count) => count === 1 ? "1 file added" : `${count} files added`,
@@ -402,7 +396,7 @@ function someNames2(names, more) {
402
396
  return AND2.format(more > 0 ? [...names, `${more} ${plural(more, "autre", "autres")}`] : names);
403
397
  }
404
398
  var FR_FIELD_STRINGS = {
405
- optionalBadge: "facultatif",
399
+ optionalBadge: "Facultatif",
406
400
  selectPlaceholder: "Choisir\u2026",
407
401
  typeValuePlaceholder: "Saisissez une valeur\u2026",
408
402
  writeHerePlaceholder: "\xC9crivez ici\u2026",
@@ -417,8 +411,8 @@ var FR_FIELD_STRINGS = {
417
411
  fieldsCount: (count) => `${count} ${plural(count, "champ", "champs")}`,
418
412
  uploading: "Envoi en cours\u2026",
419
413
  dropToUpload: "D\xE9posez pour envoyer",
420
- dropOrBrowse: "D\xE9posez un fichier ou cliquez pour parcourir",
421
- pasteUrlInstead: "coller un lien \xE0 la place",
414
+ dropOrBrowse: "Choisissez un fichier ou d\xE9posez-le ici",
415
+ pasteUrlInstead: "Collez plut\xF4t un lien",
422
416
  uploadUnavailable: "Impossible d\u2019envoyer un fichier ici. Collez plut\xF4t un lien vers le fichier.",
423
417
  urlPlaceholder: "https://\u2026",
424
418
  fileUrlAria: (label) => label ? `Lien vers le fichier pour ${label}` : "Lien vers le fichier",
@@ -468,7 +462,7 @@ var FR_FIELD_STRINGS = {
468
462
  nextImage: "Image suivante",
469
463
  openOriginal: "Ouvrir l\u2019original",
470
464
  unsupportedFileType: (formats) => `Ce type de fichier n\u2019est pas pris en charge. Formats accept\xE9s\xA0: ${formats}.`,
471
- dropFilesOrBrowse: "D\xE9posez des fichiers ou cliquez pour parcourir",
465
+ dropFilesOrBrowse: "Choisissez des fichiers ou d\xE9posez-les ici",
472
466
  dropFilesToAdd: "D\xE9posez pour ajouter les fichiers",
473
467
  addFilesAria: (label) => label ? `Ajouter des fichiers \xE0 ${label}` : "Ajouter des fichiers",
474
468
  filesAdded: (count) => `${count} ${plural(count, "fichier ajout\xE9", "fichiers ajout\xE9s")}`,
@@ -568,6 +562,23 @@ function FieldStringsProvider({
568
562
  function useFieldStrings() {
569
563
  return useContext(FieldStringsContext);
570
564
  }
565
+
566
+ // src/react/field-styles.ts
567
+ var fieldControlClass = cn(
568
+ "w-full rounded-md border border-border bg-input text-sm text-foreground",
569
+ "placeholder:text-muted-foreground/70 transition-colors",
570
+ "focus-visible:outline-hidden focus-visible:ring-1",
571
+ "focus-visible:border-ring focus-visible:ring-ring/40",
572
+ "disabled:cursor-not-allowed disabled:opacity-50",
573
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/30"
574
+ );
575
+ var rowActionClass = cn(
576
+ "flex min-h-11 w-full items-center justify-center gap-2 rounded-md border border-border px-3 text-[14px] text-foreground transition-colors",
577
+ "enabled:hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-9"
578
+ );
579
+ function smallPrintClass(isApp) {
580
+ return isApp ? "text-[12px] text-muted-foreground tabular-nums" : "font-mono text-[10.5px] text-muted-foreground";
581
+ }
571
582
  var ERROR_OUTLINE = "rounded-lg outline-solid outline-1 outline-offset-4 outline-destructive";
572
583
  function FieldShell({
573
584
  name,
@@ -575,18 +586,19 @@ function FieldShell({
575
586
  conceptRef,
576
587
  category,
577
588
  description,
589
+ hint,
578
590
  required,
579
591
  error,
580
592
  htmlFor,
581
593
  children,
582
594
  className
583
595
  }) {
584
- const s = useFieldStrings();
585
596
  const presentation = useFieldPresentation();
586
597
  const isApp = presentation === "app";
587
598
  const Label3 = htmlFor ? "label" : "div";
588
599
  const labelText = fieldLabel(title, name, presentation);
589
600
  const showLabel = labelText.trim() !== "";
601
+ const shownText = shownDescription(description, labelText, [category], presentation) ?? hint;
590
602
  const outlined = isApp && !!error;
591
603
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
592
604
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
@@ -602,20 +614,9 @@ function FieldShell({
602
614
  }
603
615
  ),
604
616
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef, category }),
605
- !required && // In `app` the mark is in the body face, as the label beside it is:
606
- // a monospace tag was a third face on a host's page.
607
- /* @__PURE__ */ jsx(
608
- "span",
609
- {
610
- className: cn(
611
- "ml-auto text-[10px] uppercase tracking-wider text-muted-foreground",
612
- !isApp && "font-mono"
613
- ),
614
- children: s.optionalBadge
615
- }
616
- )
617
+ !required && /* @__PURE__ */ jsx(OptionalMark, {})
617
618
  ] }),
618
- description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: description }),
619
+ shownText && /* @__PURE__ */ jsx(FieldDescription, { children: shownText }),
619
620
  /* @__PURE__ */ jsx(
620
621
  "div",
621
622
  {
@@ -627,18 +628,123 @@ function FieldShell({
627
628
  error && /* @__PURE__ */ jsx("p", { className: "flex items-center gap-1 text-[11px] text-destructive", role: "alert", children: error })
628
629
  ] });
629
630
  }
631
+ function OptionalMark() {
632
+ const s = useFieldStrings();
633
+ const isApp = useFieldPresentation() === "app";
634
+ return /* @__PURE__ */ jsx(
635
+ "span",
636
+ {
637
+ className: cn(
638
+ "ml-auto",
639
+ isApp ? smallPrintClass(true) : "font-mono text-[10px] uppercase tracking-wider text-muted-foreground"
640
+ ),
641
+ children: s.optionalBadge
642
+ }
643
+ );
644
+ }
645
+ function FieldDescription({ id, children }) {
646
+ return /* @__PURE__ */ jsx("p", { id, className: "text-[12px] leading-relaxed text-muted-foreground", children });
647
+ }
630
648
 
631
- // src/react/field-styles.ts
632
- var fieldControlClass = cn(
633
- "w-full rounded-md border border-border bg-input text-sm text-foreground",
634
- "placeholder:text-muted-foreground/70 transition-colors",
635
- "focus-visible:outline-hidden focus-visible:ring-1",
636
- "focus-visible:border-ring focus-visible:ring-ring/40",
637
- "disabled:cursor-not-allowed disabled:opacity-50",
638
- "aria-invalid:border-destructive aria-invalid:ring-destructive/30"
639
- );
649
+ // src/react/role-tokens.ts
650
+ var linkTextClass = "text-[color:var(--link,var(--primary,hsl(240_5.9%_10%)))]";
651
+ var selectedFillClass = "bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
652
+ var selectedStateFillClass = "data-[state=on]:bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
653
+ var selectedCheckedFillClass = "aria-checked:bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_10%,transparent))]";
654
+ var selectedFillStrongClass = "bg-[color:var(--selected,color-mix(in_oklab,var(--primary,hsl(240_5.9%_10%))_15%,transparent))]";
655
+ var selectedBorderClass = "border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
656
+ var selectedStateBorderClass = "data-[state=on]:border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
657
+ var selectedHoverBorderClass = "hover:border-[color:color-mix(in_oklab,var(--link,var(--primary,hsl(240_5.9%_10%)))_40%,transparent)]";
658
+ var checkedAccentClass = "accent-[color:var(--link,var(--primary,hsl(240_5.9%_10%)))]";
659
+ var focusOutlineClass = "focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring";
660
+ var focusWithinOutlineClass = "focus-within:outline-solid focus-within:outline-2 focus-within:outline-offset-1 focus-within:outline-ring";
661
+ var focusOutlineInsetClass = "focus-visible:outline-solid focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring";
662
+ var frameFocusOutlineClass = "data-focus-visible:outline-solid data-focus-visible:outline-2 data-focus-visible:-outline-offset-2 data-focus-visible:outline-ring";
663
+ var Switch = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
664
+ SwitchPrimitives.Root,
665
+ {
666
+ className: cn(
667
+ "peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-muted-foreground",
668
+ focusOutlineClass,
669
+ "max-sm:relative max-sm:after:absolute max-sm:after:-inset-x-0.5 max-sm:after:-inset-y-3 max-sm:after:content-['']",
670
+ className
671
+ ),
672
+ ...props,
673
+ ref,
674
+ children: /* @__PURE__ */ jsx(
675
+ SwitchPrimitives.Thumb,
676
+ {
677
+ className: cn(
678
+ "pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0"
679
+ )
680
+ }
681
+ )
682
+ }
683
+ ));
684
+ Switch.displayName = SwitchPrimitives.Root.displayName;
685
+ var FieldDomIdContext = createContext(null);
686
+ function FieldDomIdProvider({
687
+ prefix,
688
+ children
689
+ }) {
690
+ const generated = useId();
691
+ return /* @__PURE__ */ jsx(FieldDomIdContext, { value: prefix ?? generated, children });
692
+ }
693
+ function useFieldDomId(path) {
694
+ const scoped = use(FieldDomIdContext);
695
+ const local = useId();
696
+ const prefix = scoped ?? local;
697
+ return prefix === "" ? path : `${prefix}-${path}`;
698
+ }
699
+ function BooleanField({ field, value, onChange, id, disabled }) {
700
+ const presentation = useFieldPresentation();
701
+ const isApp = presentation === "app";
702
+ const domId = useFieldDomId(id);
703
+ const label = fieldLabel(field.title, field.name, presentation);
704
+ const description = shownDescription(field.description, label, ["boolean"], presentation);
705
+ return /* @__PURE__ */ jsxs(
706
+ "div",
707
+ {
708
+ className: cn(
709
+ "flex items-center justify-between gap-4 rounded-md border border-border bg-input px-3.5 py-3",
710
+ disabled && "opacity-50"
711
+ ),
712
+ children: [
713
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-1", children: [
714
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
715
+ /* @__PURE__ */ jsx(
716
+ "label",
717
+ {
718
+ htmlFor: domId,
719
+ className: cn(
720
+ "text-[13px] font-medium leading-none text-foreground",
721
+ isApp ? "text-[13.5px]" : "font-mono"
722
+ ),
723
+ children: label
724
+ }
725
+ ),
726
+ !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "boolean" })
727
+ ] }),
728
+ description && /* @__PURE__ */ jsx(FieldDescription, { children: description })
729
+ ] }),
730
+ /* @__PURE__ */ jsx(
731
+ Switch,
732
+ {
733
+ id: domId,
734
+ checked: value ?? false,
735
+ onCheckedChange: onChange,
736
+ disabled,
737
+ className: "shrink-0"
738
+ }
739
+ )
740
+ ]
741
+ }
742
+ );
743
+ }
640
744
  function DateField({ field, value, onChange, id, error, disabled }) {
641
745
  const domId = useFieldDomId(id);
746
+ const isApp = useFieldPresentation() === "app";
747
+ const shown = toDateInputValue(value);
642
748
  return /* @__PURE__ */ jsx(
643
749
  FieldShell,
644
750
  {
@@ -655,11 +761,17 @@ function DateField({ field, value, onChange, id, error, disabled }) {
655
761
  {
656
762
  id: domId,
657
763
  type: "date",
658
- value: toDateInputValue(value),
764
+ value: shown,
659
765
  disabled,
660
766
  "aria-invalid": !!error,
767
+ "data-empty": asCalendarDate(shown) === null ? "" : void 0,
661
768
  onChange: (e) => onChange(toStoredDateValue(e.target.value, field.datetime)),
662
- className: cn(fieldControlClass, "h-11 px-3 font-mono tabular-nums")
769
+ className: cn(
770
+ fieldControlClass,
771
+ "h-11 px-3 tabular-nums data-empty:text-muted-foreground dark:scheme-dark",
772
+ "[&::-webkit-calendar-picker-indicator]:cursor-pointer",
773
+ !isApp && "font-mono"
774
+ )
663
775
  }
664
776
  )
665
777
  }
@@ -1001,10 +1113,7 @@ function FilePickerEntry({ onOpen, label, disabled, ref, id }) {
1001
1113
  disabled,
1002
1114
  "aria-haspopup": "dialog",
1003
1115
  "aria-label": s.chooseFromSourceAria(label),
1004
- className: cn(
1005
- "flex min-h-11 w-full items-center justify-center gap-2 rounded-md border border-border px-3 text-[14px] text-foreground transition-colors hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-9",
1006
- focusOutlineClass
1007
- ),
1116
+ className: cn(rowActionClass, focusOutlineClass),
1008
1117
  children: [
1009
1118
  /* @__PURE__ */ jsx(Folder, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
1010
1119
  s.chooseFromSource
@@ -1390,6 +1499,7 @@ function FileField({
1390
1499
  }) {
1391
1500
  const s = useFieldStrings();
1392
1501
  const presentation = useFieldPresentation();
1502
+ const isApp = presentation === "app";
1393
1503
  const domId = useFieldDomId(id);
1394
1504
  const [showUrl, setShowUrl] = useState(false);
1395
1505
  const [typedUrl, setTypedUrl] = useState(null);
@@ -1651,7 +1761,7 @@ function FileField({
1651
1761
  s.uploading
1652
1762
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1653
1763
  noUploadLineShown && /* @__PURE__ */ jsx("p", { id: noUploadLineId, className: "text-[12px] text-muted-foreground", children: s.uploadUnavailable }),
1654
- hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
1764
+ hint && /* @__PURE__ */ jsx("p", { className: smallPrintClass(isApp), children: hint })
1655
1765
  ] }) })
1656
1766
  ) : (
1657
1767
  // `role="presentation"` and no tab stop: this div is the drop TARGET and
@@ -1689,7 +1799,7 @@ function FileField({
1689
1799
  /* @__PURE__ */ jsx(Upload, { className: "h-5 w-5 text-muted-foreground", strokeWidth: 1.75 }),
1690
1800
  /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
1691
1801
  /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropToUpload : s.dropOrBrowse }),
1692
- hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
1802
+ hint && /* @__PURE__ */ jsx("p", { className: smallPrintClass(isApp), children: hint })
1693
1803
  ] })
1694
1804
  ] })
1695
1805
  ]
@@ -1697,8 +1807,9 @@ function FileField({
1697
1807
  )
1698
1808
  ),
1699
1809
  !busy && (refusal ? /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[12px] text-destructive", children: refusal.kind === "type" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1700
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: refusal.name }),
1701
- " \u2014 ",
1810
+ /* @__PURE__ */ jsx("span", { className: isApp ? "font-medium" : "font-mono", children: refusal.name }),
1811
+ " \u2014",
1812
+ " ",
1702
1813
  s.unsupportedFileType(hint)
1703
1814
  ] }) : dropArea?.open ? (
1704
1815
  // Pointed to only while it takes files: a full list's area is
@@ -1723,7 +1834,12 @@ function FileField({
1723
1834
  setTypedUrl(e.target.value);
1724
1835
  onChange(e.target.value ? { url: e.target.value } : void 0);
1725
1836
  },
1726
- className: cn(fieldControlClass, "h-9 px-3 font-mono text-[12px]")
1837
+ className: cn(
1838
+ fieldControlClass,
1839
+ // In an app, a control of the field family like any other; in a
1840
+ // studio, a reference set as code, under the field's mono name.
1841
+ isApp ? "h-11 px-3" : "h-9 px-3 font-mono text-[12px]"
1842
+ )
1727
1843
  }
1728
1844
  ) : allowUrl && /* @__PURE__ */ jsxs(
1729
1845
  "button",
@@ -1732,13 +1848,21 @@ function FileField({
1732
1848
  disabled: busy,
1733
1849
  onClick: () => setShowUrl(true),
1734
1850
  className: cn(
1735
- "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",
1736
- presentation !== "app" && "font-mono",
1851
+ "flex w-fit items-center rounded-sm text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 max-sm:min-h-11",
1852
+ // A studio's aside is a mono footnote; an app's is a quiet text
1853
+ // action in the page's face, underlined under the pointer.
1854
+ isApp ? "gap-1.5 text-[13px] underline-offset-4 hover:underline" : "gap-1 font-mono text-[11px]",
1737
1855
  focusOutlineClass,
1738
1856
  "focus-visible:outline-offset-2"
1739
1857
  ),
1740
1858
  children: [
1741
- /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
1859
+ /* @__PURE__ */ jsx(
1860
+ Link2,
1861
+ {
1862
+ className: cn("shrink-0", isApp ? "h-3.5 w-3.5" : "h-3 w-3"),
1863
+ "aria-hidden": "true"
1864
+ }
1865
+ ),
1742
1866
  s.pasteUrlInstead
1743
1867
  ]
1744
1868
  }
@@ -1838,7 +1962,16 @@ function FileChip({
1838
1962
  /* @__PURE__ */ jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-muted", children: /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground" }) }),
1839
1963
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 basis-32 [contain:inline-size]", children: [
1840
1964
  /* @__PURE__ */ jsx("p", { className: "truncate text-[13px] font-medium text-foreground", children: value?.filename ?? s.uploadedFile }),
1841
- subtitle && /* @__PURE__ */ jsx("p", { className: "truncate font-mono text-[10.5px] text-muted-foreground", children: subtitle })
1965
+ subtitle && /* @__PURE__ */ jsx(
1966
+ "p",
1967
+ {
1968
+ className: cn(
1969
+ "truncate text-muted-foreground",
1970
+ presentation === "app" ? "text-[12px]" : "font-mono text-[10.5px]"
1971
+ ),
1972
+ children: subtitle
1973
+ }
1974
+ )
1842
1975
  ] }),
1843
1976
  /* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-3", children: [
1844
1977
  canPreview && /* @__PURE__ */ jsxs(
@@ -1847,12 +1980,12 @@ function FileChip({
1847
1980
  type: "button",
1848
1981
  onClick: onTogglePreview,
1849
1982
  className: cn(
1850
- "inline-flex h-7 shrink-0 items-center gap-1 rounded-md border border-border px-2 text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground max-sm:h-11 max-sm:px-3",
1983
+ "inline-flex h-7 shrink-0 items-center gap-1 rounded-md border border-border px-2 text-[11px] text-muted-foreground transition-colors hover:bg-muted hover:text-foreground max-sm:size-11 max-sm:justify-center max-sm:px-0",
1851
1984
  focusOutlineClass
1852
1985
  ),
1853
1986
  children: [
1854
1987
  previewOpen ? /* @__PURE__ */ jsx(EyeOff, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" }),
1855
- s.preview
1988
+ /* @__PURE__ */ jsx("span", { className: "max-sm:sr-only", children: s.preview })
1856
1989
  ]
1857
1990
  }
1858
1991
  ),
@@ -2057,6 +2190,7 @@ function EnumField({ field, value, onChange, id, error, disabled }) {
2057
2190
  function NumberField({ field, value, onChange, id, error, disabled }) {
2058
2191
  const s = useFieldStrings();
2059
2192
  const domId = useFieldDomId(id);
2193
+ const isApp = useFieldPresentation() === "app";
2060
2194
  const step = field.integer ? 1 : 0.1;
2061
2195
  const clamp = (n) => {
2062
2196
  let next = n;
@@ -2073,7 +2207,8 @@ function NumberField({ field, value, onChange, id, error, disabled }) {
2073
2207
  title: field.title,
2074
2208
  conceptRef: field.conceptRef,
2075
2209
  category: "number",
2076
- description: field.description ?? rangeHint,
2210
+ description: field.description,
2211
+ hint: rangeHint,
2077
2212
  required: !shownAsOptional(field),
2078
2213
  error,
2079
2214
  htmlFor: domId,
@@ -2094,7 +2229,10 @@ function NumberField({ field, value, onChange, id, error, disabled }) {
2094
2229
  onChange: (e) => onChange(e.target.value === "" ? void 0 : Number(e.target.value)),
2095
2230
  className: cn(
2096
2231
  fieldControlClass,
2097
- "h-11 px-3 font-mono tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none"
2232
+ "h-11 px-3 tabular-nums [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none",
2233
+ // A number is a value, not an identifier: mono in a studio, beside
2234
+ // the field's mono name, and the page's own face in an app.
2235
+ !isApp && "font-mono"
2098
2236
  )
2099
2237
  }
2100
2238
  ),
@@ -2232,6 +2370,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2232
2370
  const depth = use(StructureDepthContext);
2233
2371
  const data = value ?? {};
2234
2372
  const label = fieldLabel(field.title, field.name, presentation);
2373
+ const description = shownDescription(field.description, label, ["structured"], presentation);
2235
2374
  const grouping = structureGrouping(depth, label.trim() !== "", use(FramedFormContext));
2236
2375
  const domId = useFieldDomId(id);
2237
2376
  const refused = use(RefusedStructureContext) === id;
@@ -2261,7 +2400,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2261
2400
  const group = grouping === "none" ? {} : {
2262
2401
  role: "group",
2263
2402
  "aria-labelledby": nameId,
2264
- "aria-describedby": field.description ? descriptionId : void 0
2403
+ "aria-describedby": description ? descriptionId : void 0
2265
2404
  };
2266
2405
  const section = grouping === "section";
2267
2406
  const caption = error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error });
@@ -2271,7 +2410,7 @@ function ObjectField({ field, value, onChange, id, error, env }) {
2271
2410
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "structured" }),
2272
2411
  section && caption
2273
2412
  ] }),
2274
- field.description && /* @__PURE__ */ jsx("p", { id: descriptionId, className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2413
+ description && /* @__PURE__ */ jsx(FieldDescription, { id: descriptionId, children: description }),
2275
2414
  /* @__PURE__ */ jsxs(
2276
2415
  "div",
2277
2416
  {
@@ -2361,6 +2500,7 @@ function firstFocusable(within2) {
2361
2500
  var NAMED = 3;
2362
2501
  function ListFileDrop({ label, formats, shut, onPlace }) {
2363
2502
  const s = useFieldStrings();
2503
+ const isApp = useFieldPresentation() === "app";
2364
2504
  const hint = acceptLabel(formats);
2365
2505
  const accept = useMemo(() => acceptMap(formats), [formats]);
2366
2506
  const [notice, setNotice] = useState(null);
@@ -2409,7 +2549,7 @@ function ListFileDrop({ label, formats, shut, onPlace }) {
2409
2549
  ),
2410
2550
  /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4 text-muted-foreground", strokeWidth: 1.75 }),
2411
2551
  /* @__PURE__ */ jsx("p", { className: "text-[13px] text-foreground", children: isDragActive ? s.dropFilesToAdd : s.dropFilesOrBrowse }),
2412
- hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
2552
+ hint && /* @__PURE__ */ jsx("p", { className: smallPrintClass(isApp), children: hint })
2413
2553
  ]
2414
2554
  }
2415
2555
  ),
@@ -2569,6 +2709,18 @@ function ListField({ field, value, onChange, id, error, env }) {
2569
2709
  }
2570
2710
  return count;
2571
2711
  };
2712
+ const label = fieldLabel(field.title, field.name, presentation);
2713
+ const exactCount = field.itemCount !== void 0 && field.maxItemCount === field.itemCount ? field.itemCount : void 0;
2714
+ const countShown = !isApp || items.length > 0 || exactCount !== void 0;
2715
+ const kinds = ["list", conceptCategory(field.item)];
2716
+ const description = shownDescription(field.description, label, kinds, presentation);
2717
+ const itemDescription = shownItemDescription(
2718
+ field.item.description,
2719
+ label,
2720
+ field.description,
2721
+ kinds,
2722
+ presentation
2723
+ );
2572
2724
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2573
2725
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
2574
2726
  /* @__PURE__ */ jsx(
@@ -2578,25 +2730,17 @@ function ListField({ field, value, onChange, id, error, env }) {
2578
2730
  "text-[13px] font-medium leading-none text-foreground",
2579
2731
  isApp ? "text-[13.5px]" : "font-mono"
2580
2732
  ),
2581
- children: fieldLabel(field.title, field.name, presentation)
2733
+ children: label
2582
2734
  }
2583
2735
  ),
2584
2736
  !isApp && /* @__PURE__ */ jsx(ConceptPill, { conceptRef: field.conceptRef, category: "list" }),
2585
- /* @__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) }),
2586
- shownAsOptional(field) && /* @__PURE__ */ jsx(
2587
- "span",
2588
- {
2589
- className: cn(
2590
- "ml-auto text-[10px] uppercase tracking-wider text-muted-foreground",
2591
- !isApp && "font-mono"
2592
- ),
2593
- children: s.optionalBadge
2594
- }
2595
- )
2737
+ countShown && /* @__PURE__ */ jsx("span", { className: smallPrintClass(isApp), children: exactCount !== void 0 ? s.itemsCountOf(items.length, exactCount) : s.itemsCount(items.length) }),
2738
+ shownAsOptional(field) && /* @__PURE__ */ jsx(OptionalMark, {})
2596
2739
  ] }),
2597
- field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
2740
+ description && /* @__PURE__ */ jsx(FieldDescription, { children: description }),
2741
+ itemDescription && /* @__PURE__ */ jsx(FieldDescription, { children: itemDescription }),
2598
2742
  /* @__PURE__ */ jsxs("div", { ref: rowsRef, className: "space-y-2", children: [
2599
- items.length === 0 && !dropOffered && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
2743
+ items.length === 0 && !dropOffered && !isApp && /* @__PURE__ */ jsx("p", { className: "rounded-md border border-dashed border-border bg-input/50 px-3 py-4 text-center text-[12px] text-muted-foreground", children: s.noItemsYet }),
2600
2744
  /* @__PURE__ */ jsx(ListDropAreaContext.Provider, { value: dropArea, children: items.map((item, index) => /* @__PURE__ */ jsxs(
2601
2745
  "div",
2602
2746
  {
@@ -2614,7 +2758,8 @@ function ListField({ field, value, onChange, id, error, env }) {
2614
2758
  "span",
2615
2759
  {
2616
2760
  className: cn(
2617
- "shrink-0 font-mono text-[11px] text-muted-foreground",
2761
+ "shrink-0 text-muted-foreground",
2762
+ isApp ? "text-[12px] tabular-nums" : "font-mono text-[11px]",
2618
2763
  structuredRows ? "col-start-1 row-start-1" : "mt-1 w-5 text-center"
2619
2764
  ),
2620
2765
  children: index + 1
@@ -2657,7 +2802,7 @@ function ListField({ field, value, onChange, id, error, env }) {
2657
2802
  dropOffered && fileItem && /* @__PURE__ */ jsx("div", { ref: dropRef, children: /* @__PURE__ */ jsx(
2658
2803
  ListFileDrop,
2659
2804
  {
2660
- label: fieldLabel(field.title, field.name, presentation),
2805
+ label,
2661
2806
  formats: fileItem.formats,
2662
2807
  shut: !dropOpen,
2663
2808
  onPlace: placeFiles
@@ -2670,15 +2815,27 @@ function ListField({ field, value, onChange, id, error, env }) {
2670
2815
  onClick: addItem,
2671
2816
  disabled: env?.disabled || isFull,
2672
2817
  className: cn(
2673
- "inline-flex h-9 w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-border text-[13px] text-muted-foreground transition-colors max-sm:h-11",
2674
- // The edge under the pointer is the accent's, as a selected pill's
2675
- // is; the fill under it stays neutral.
2676
- "hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
2677
- selectedHoverBorderClass,
2818
+ isApp ? (
2819
+ // A secondary action of the same family as the team's files
2820
+ // row under it, and the field above it.
2821
+ rowActionClass
2822
+ ) : [
2823
+ "inline-flex h-9 w-full items-center justify-center gap-1.5 rounded-md border border-dashed border-border text-[13px] text-muted-foreground transition-colors max-sm:h-11",
2824
+ // The edge under the pointer is the accent's, as a selected
2825
+ // pill's is; the fill under it stays neutral.
2826
+ "hover:bg-muted hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50",
2827
+ selectedHoverBorderClass
2828
+ ],
2678
2829
  focusOutlineClass
2679
2830
  ),
2680
2831
  children: [
2681
- /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
2832
+ /* @__PURE__ */ jsx(
2833
+ Plus,
2834
+ {
2835
+ className: cn("h-4 w-4", isApp && "shrink-0 text-muted-foreground"),
2836
+ "aria-hidden": "true"
2837
+ }
2838
+ ),
2682
2839
  s.addItem
2683
2840
  ]
2684
2841
  }
@@ -2688,7 +2845,7 @@ function ListField({ field, value, onChange, id, error, env }) {
2688
2845
  FilePickerEntry,
2689
2846
  {
2690
2847
  ref: pickerEntryRef,
2691
- label: fieldLabel(field.title, field.name, presentation),
2848
+ label,
2692
2849
  disabled: env?.disabled || busy || room === 0,
2693
2850
  onOpen: () => setPickerOpen(true)
2694
2851
  }
@@ -2854,7 +3011,8 @@ function UnknownField({ field, value, onChange, id, error, disabled }) {
2854
3011
  title: field.title,
2855
3012
  conceptRef: field.conceptRef,
2856
3013
  category: "structured",
2857
- description: field.description ?? s.jsonHint,
3014
+ description: field.description,
3015
+ hint: s.jsonHint,
2858
3016
  required: !shownAsOptional(field),
2859
3017
  error,
2860
3018
  htmlFor: domId,
@@ -3208,7 +3366,7 @@ function FillWithExample({
3208
3366
  },
3209
3367
  className: cn(secondaryClass, className),
3210
3368
  children: [
3211
- /* @__PURE__ */ jsx(Sparkles, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }),
3369
+ /* @__PURE__ */ jsx(ClipboardPaste, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }),
3212
3370
  s.fillWithExample
3213
3371
  ]
3214
3372
  }
@@ -4583,11 +4741,11 @@ function useFileNamePrompt() {
4583
4741
  const asking = useAskFileName();
4584
4742
  const [pending, setPending] = useState(null);
4585
4743
  const ask = useCallback(
4586
- (stem2, extension) => {
4587
- if (!asking) return Promise.resolve(`${stem2}${extension}`);
4744
+ (stem3, extension) => {
4745
+ if (!asking) return Promise.resolve(`${stem3}${extension}`);
4588
4746
  return new Promise((resolve) => {
4589
4747
  setPending({
4590
- stem: stem2,
4748
+ stem: stem3,
4591
4749
  extension,
4592
4750
  settle: (name) => {
4593
4751
  setPending(null);
@@ -4611,11 +4769,11 @@ function useFileNamePrompt() {
4611
4769
  ) : null;
4612
4770
  return { ask, dialog };
4613
4771
  }
4614
- function FileNameDialog({ stem: stem2, extension, onConfirm, onCancel }) {
4772
+ function FileNameDialog({ stem: stem3, extension, onConfirm, onCancel }) {
4615
4773
  const s = useFieldStrings();
4616
4774
  const titleId = useId();
4617
4775
  const extensionId = useId();
4618
- const [value, setValue] = useState(stem2);
4776
+ const [value, setValue] = useState(stem3);
4619
4777
  const panel = useRef(null);
4620
4778
  const field = useRef(null);
4621
4779
  const pressedBackdrop = useRef(false);
@@ -7016,8 +7174,8 @@ ${planned.url ?? planned.text}`;
7016
7174
  {
7017
7175
  type: "button",
7018
7176
  onClick: async () => {
7019
- const { stem: stem2, extension } = splitFileName(planned.name);
7020
- const name = await ask(stem2, extension);
7177
+ const { stem: stem3, extension } = splitFileName(planned.name);
7178
+ const name = await ask(stem3, extension);
7021
7179
  if (name === null) return;
7022
7180
  const started = { identity, state: "saving" };
7023
7181
  setAttempt(started);
@@ -8288,7 +8446,7 @@ function ResultNode({
8288
8446
  const pagePath = location?.path.join(".");
8289
8447
  const pageFile = pagePath === void 0 ? void 0 : readStuffFile("markup", unwrapped, pagePath);
8290
8448
  const page = pageDownloadShown ? pageFile : void 0;
8291
- const pdfName = pdfShown && content && pageFile && location ? stem(planFileSave(pageFile, { baseName: location.baseName })?.name) : void 0;
8449
+ const pdfName = pdfShown && content && pageFile && location ? stem2(planFileSave(pageFile, { baseName: location.baseName })?.name) : void 0;
8292
8450
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
8293
8451
  page || pdfName !== void 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
8294
8452
  /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: header }),
@@ -8506,7 +8664,7 @@ function ResultNode({
8506
8664
  }
8507
8665
  return null;
8508
8666
  }
8509
- function stem(name) {
8667
+ function stem2(name) {
8510
8668
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
8511
8669
  }
8512
8670
  var ALL_VIEWS = ["rendered", "json"];
@@ -8667,15 +8825,15 @@ function StuffPanel({
8667
8825
  const only = plan.files.length === 1 ? plan.files[0] : void 0;
8668
8826
  const json = plan.files.length > 1 ? plan.files[plan.files.length - 1] : void 0;
8669
8827
  if (only) {
8670
- const { stem: stem2, extension } = splitFileName(only.name);
8671
- const name2 = await ask(stem2, extension);
8828
+ const { stem: stem3, extension } = splitFileName(only.name);
8829
+ const name2 = await ask(stem3, extension);
8672
8830
  if (name2 === null) return;
8673
8831
  if (name2 !== only.name) plan = { ...plan, files: [{ ...only, name: name2 }] };
8674
8832
  } else if (json) {
8675
- const { stem: stem2 } = splitFileName(json.name);
8676
- const name2 = await ask(stem2, "");
8833
+ const { stem: stem3 } = splitFileName(json.name);
8834
+ const name2 = await ask(stem3, "");
8677
8835
  if (name2 === null) return;
8678
- if (name2 !== stem2) plan = planStuffSave(field, value, { baseName: name2, ...options });
8836
+ if (name2 !== stem3) plan = planStuffSave(field, value, { baseName: name2, ...options });
8679
8837
  }
8680
8838
  const attempt = {
8681
8839
  field,
@@ -8812,5 +8970,5 @@ function pdfFileName(field, value, baseName) {
8812
8970
  }
8813
8971
 
8814
8972
  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, fieldLabel, fieldStringsFor, focusOutlineClass, headingClass, isThenable, linkTextClass, panelFlatShadowClass, panelRaisedShadowClass, panelSurfaceClass, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, selectedBorderClass, selectedFillClass, selectedFillStrongClass, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveRendition, useResolveShareUrl, useResolveUrl, useResolvedUrl };
8815
- //# sourceMappingURL=chunk-5RV3ASMO.js.map
8816
- //# sourceMappingURL=chunk-5RV3ASMO.js.map
8973
+ //# sourceMappingURL=chunk-TD62R3P6.js.map
8974
+ //# sourceMappingURL=chunk-TD62R3P6.js.map