@pipelex/mthds-form 0.19.0 → 0.20.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,26 +1,30 @@
1
1
  import { planFileSave, planStuffSave, splitFileName, typedFileStem } from './chunk-ICUEHNEH.js';
2
2
  import { seedInputs, closeOptionalStructures, openOptionalStructures } from './chunk-VH5CDQDR.js';
3
+ import { sourceFileValue, roomForChosenFiles, addChosenFiles, chosenFilesAdded } from './chunk-EQJBM3NS.js';
3
4
  import { readStuffFile } from './chunk-RRTI7AIR.js';
4
- import { setValueAtPath, apiInputsFromRunValues } from './chunk-MEXWXLR7.js';
5
+ import { setValueAtPath, apiInputsFromRunValues, sameRunValues, runValuesFromInputs } from './chunk-ZX52CQJ4.js';
5
6
  import { viewableUrl, isNativeDateNode, isNativeHtmlNode, readHtmlContent, isNativeCompositeNode, readCompositeContent, isNativeValueNode, readDateContent, formatDateContent, readImageContent, readDataUrl, readDocumentContent } from './chunk-CM4KVG53.js';
6
7
  import { buildRunFields } from './chunk-ZB4RYUKV.js';
7
8
  import { conceptCategory } from './chunk-FABY3DEY.js';
8
- import { acceptLabel, acceptMap, isAcceptedFile, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
9
+ import { acceptLabel, acceptMap, isAcceptedFile, normalizeMimeType, formatOfFilename, DOCUMENT_FORMATS, IMAGE_FORMATS } from './chunk-XCO6GTEC.js';
9
10
  import { shownAsOptional, isFoldedOptional, anyOptionalStructureOpen, isOptionalStructure, computeReadiness, optionalStructureOpen, mustBeFilled, fieldFilled, isFilled } from './chunk-XO5OENNF.js';
10
11
  import { ownProp } from './chunk-V3OV2XQT.js';
11
12
  import { toStoredDateValue, toDateInputValue } from './chunk-GKV77RAS.js';
12
13
  import { clsx } from 'clsx';
13
14
  import { twMerge } from 'tailwind-merge';
14
- import { ChevronDown, ChevronUp, Check, List, Braces, ListChecks, ToggleLeft, Hash, Image as Image$1, FileText, CalendarDays, Type, Loader2, ImageOff, Upload, Link2, EyeOff, Eye, X, Minus, Plus, Trash2, TriangleAlert, FileDown, Copy, Download, ChevronRight } from 'lucide-react';
15
+ 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 } from 'lucide-react';
15
16
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
16
- import * as React2 from 'react';
17
- import { createContext, use, useId, useMemo, useContext, useState, useRef, useCallback, useEffect, Fragment as Fragment$1, useLayoutEffect } from 'react';
17
+ import * as React5 from 'react';
18
+ import { createContext, use, useId, useMemo, useContext, useState, useEffect, useRef, useLayoutEffect, useCallback, Fragment as Fragment$1 } from 'react';
18
19
  import * as SwitchPrimitives from '@radix-ui/react-switch';
19
20
  import { useDropzone } from 'react-dropzone';
20
- import * as SelectPrimitive from '@radix-ui/react-select';
21
+ import * as DialogPrimitive from '@radix-ui/react-dialog';
21
22
  import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group';
22
23
  import * as TogglePrimitive from '@radix-ui/react-toggle';
23
24
  import { cva } from 'class-variance-authority';
25
+ import * as SelectPrimitive from '@radix-ui/react-select';
26
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
27
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
24
28
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
25
29
  import { marked } from 'marked';
26
30
 
@@ -107,7 +111,7 @@ function fieldLabel(title, name, presentation) {
107
111
  if (title !== void 0) return title;
108
112
  return presentation === "app" ? humanizeFieldName(name) : name;
109
113
  }
110
- var Switch = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
114
+ var Switch = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
111
115
  SwitchPrimitives.Root,
112
116
  {
113
117
  className: cn(
@@ -186,12 +190,38 @@ function BooleanField({ field, value, onChange, id, disabled }) {
186
190
  );
187
191
  }
188
192
 
193
+ // src/react/locales/calendar.ts
194
+ function dayNumber(date) {
195
+ return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()) / 864e5;
196
+ }
197
+ function calendarDaysBefore(date, now = /* @__PURE__ */ new Date()) {
198
+ return dayNumber(now) - dayNumber(date);
199
+ }
200
+ function inCurrentYear(date, now = /* @__PURE__ */ new Date()) {
201
+ return date.getFullYear() === now.getFullYear();
202
+ }
203
+
189
204
  // src/react/locales/en.ts
190
205
  function formatBytes(bytes) {
191
206
  if (bytes < 1024) return bytes === 1 ? "1 byte" : `${bytes} bytes`;
192
207
  if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
193
208
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
194
209
  }
210
+ var AND = new Intl.ListFormat("en-GB", { type: "conjunction" });
211
+ var OR = new Intl.ListFormat("en-GB", { type: "disjunction" });
212
+ var TIME = new Intl.DateTimeFormat("en-GB", { hour: "2-digit", minute: "2-digit" });
213
+ var DAY = new Intl.DateTimeFormat("en-GB", { day: "numeric", month: "short" });
214
+ var DAY_YEAR = new Intl.DateTimeFormat("en-GB", {
215
+ day: "numeric",
216
+ month: "short",
217
+ year: "numeric"
218
+ });
219
+ function formatFileDate(date) {
220
+ const days = calendarDaysBefore(date);
221
+ if (days === 0) return `Today, ${TIME.format(date)}`;
222
+ if (days === 1) return "Yesterday";
223
+ return inCurrentYear(date) ? DAY.format(date) : DAY_YEAR.format(date);
224
+ }
195
225
  var EN_FIELD_STRINGS = {
196
226
  optionalBadge: "optional",
197
227
  selectPlaceholder: "Select\u2026",
@@ -252,7 +282,61 @@ var EN_FIELD_STRINGS = {
252
282
  optionalInputsCount: (count) => count === 1 ? "1 optional input" : `${count} optional inputs`,
253
283
  requiredField: "Required",
254
284
  incompleteField: "Incomplete: fill in its required fields",
255
- uploadFailed: "This file could not be uploaded. Try again."
285
+ uploadFailed: "This file could not be uploaded. Try again.",
286
+ chooseFromSource: "Choose from your team's files",
287
+ chooseFromSourceAria: (label) => label ? `Choose from your team's files for ${label}` : "Choose from your team's files",
288
+ filePickerTitle: "Your team's files",
289
+ filePickerTakes: (formats) => `Showing ${AND.format(formats)} files, which this input takes`,
290
+ filePickerSearch: "Search files",
291
+ filePickerSearchPlaceholder: "Search by name",
292
+ filePickerScope: "Whose files",
293
+ filePickerMine: "Mine",
294
+ filePickerEveryone: "Everyone",
295
+ filePickerUse: (count, name) => {
296
+ if (count === 0) return "Choose a file";
297
+ if (count === 1 && name) return `Use ${name}`;
298
+ return count === 1 ? "Use 1 file" : `Use ${count} files`;
299
+ },
300
+ filePickerLoading: "Loading your files\u2026",
301
+ filePickerFailed: "Couldn't load your files.",
302
+ filePickerNoMatch: "No files match.",
303
+ filePickerClearSearch: "Clear search",
304
+ filePickerNoneTeam: (formats) => `Your team has no ${OR.format(formats)} files yet.`,
305
+ filePickerNoneMine: (formats) => `You haven't added or made any ${OR.format(formats)} files yet.`,
306
+ filePickerShowEveryone: "Show everyone's",
307
+ filePickerUpload: "Upload a file",
308
+ filePickerShown: (count) => count === 1 ? "1 file" : `${count} files`,
309
+ testFile: "Test file",
310
+ fileSize: formatBytes,
311
+ fileDate: formatFileDate,
312
+ fillWithExample: "Fill with an example",
313
+ examplesTitle: "Examples",
314
+ examplesHint: "Choosing one fills the form. Nothing runs until you run it.",
315
+ examplesLoading: "Loading the examples\u2026",
316
+ examplesFailed: "Couldn't load the examples.",
317
+ examplesNone: "No examples yet. Run it, then save the run as an example.",
318
+ examplesShown: (count) => count === 1 ? "1 example" : `${count} examples`,
319
+ replaceEnteredTitle: "Replace what you entered?",
320
+ replaceEnteredDescription: (name) => `Filling the form with \u201C${name}\u201D replaces every value in it.`,
321
+ replaceEnteredConfirm: "Replace",
322
+ exampleActions: (name) => `Actions for ${name}`,
323
+ renameExample: "Rename",
324
+ deleteExample: "Delete",
325
+ renameExampleTitle: "Rename the example",
326
+ exampleName: "Name",
327
+ renaming: "Renaming\u2026",
328
+ renameFailed: "Couldn't rename the example.",
329
+ deleteExampleTitle: (name) => `Delete \u201C${name}\u201D?`,
330
+ deleteExampleDescription: "This cannot be undone.",
331
+ deleting: "Deleting\u2026",
332
+ deleteFailed: "Couldn't delete the example.",
333
+ close: "Close",
334
+ cancel: "Cancel",
335
+ save: "Save",
336
+ tryAgain: "Try again",
337
+ showMore: "Show more",
338
+ loadingMore: "Loading more\u2026",
339
+ loadMoreFailed: "Couldn't load more."
256
340
  };
257
341
 
258
342
  // src/react/locales/fr.ts
@@ -270,6 +354,21 @@ var MEGABYTES = new Intl.NumberFormat("fr-FR", {
270
354
  unitDisplay: "short",
271
355
  maximumFractionDigits: 1
272
356
  });
357
+ var AND2 = new Intl.ListFormat("fr", { type: "conjunction" });
358
+ var OR2 = new Intl.ListFormat("fr", { type: "disjunction" });
359
+ var TIME2 = new Intl.DateTimeFormat("fr-FR", { hour: "2-digit", minute: "2-digit" });
360
+ var DAY2 = new Intl.DateTimeFormat("fr-FR", { day: "numeric", month: "short" });
361
+ var DAY_YEAR2 = new Intl.DateTimeFormat("fr-FR", {
362
+ day: "numeric",
363
+ month: "short",
364
+ year: "numeric"
365
+ });
366
+ function formatFileDate2(date) {
367
+ const days = calendarDaysBefore(date);
368
+ if (days === 0) return `Aujourd\u2019hui, ${TIME2.format(date)}`;
369
+ if (days === 1) return "Hier";
370
+ return inCurrentYear(date) ? DAY2.format(date) : DAY_YEAR2.format(date);
371
+ }
273
372
  function plural(count, one, other) {
274
373
  return PLURALS.select(count) === "one" ? one : other;
275
374
  }
@@ -338,7 +437,61 @@ var FR_FIELD_STRINGS = {
338
437
  optionalInputsCount: (count) => `${count} ${plural(count, "entr\xE9e facultative", "entr\xE9es facultatives")}`,
339
438
  requiredField: "Obligatoire",
340
439
  incompleteField: "Incomplet\xA0: remplissez ses champs obligatoires",
341
- uploadFailed: "Ce fichier n\u2019a pas pu \xEAtre envoy\xE9. R\xE9essayez."
440
+ uploadFailed: "Ce fichier n\u2019a pas pu \xEAtre envoy\xE9. R\xE9essayez.",
441
+ chooseFromSource: "Choisir parmi les fichiers de l\u2019\xE9quipe",
442
+ chooseFromSourceAria: (label) => label ? `Choisir parmi les fichiers de l\u2019\xE9quipe pour ${label}` : "Choisir parmi les fichiers de l\u2019\xE9quipe",
443
+ filePickerTitle: "Les fichiers de l\u2019\xE9quipe",
444
+ filePickerTakes: (formats) => `Fichiers ${AND2.format(formats)} uniquement, ceux que cette entr\xE9e accepte`,
445
+ filePickerSearch: "Rechercher des fichiers",
446
+ filePickerSearchPlaceholder: "Rechercher par nom",
447
+ filePickerScope: "Quels fichiers",
448
+ filePickerMine: "Les miens",
449
+ filePickerEveryone: "Toute l\u2019\xE9quipe",
450
+ filePickerUse: (count, name) => {
451
+ if (count === 0) return "Choisissez un fichier";
452
+ if (count === 1 && name) return `Utiliser ${name}`;
453
+ return `Utiliser ${count} ${plural(count, "fichier", "fichiers")}`;
454
+ },
455
+ filePickerLoading: "Chargement de vos fichiers\u2026",
456
+ filePickerFailed: "Impossible de charger vos fichiers.",
457
+ filePickerNoMatch: "Aucun fichier ne correspond.",
458
+ filePickerClearSearch: "Effacer la recherche",
459
+ filePickerNoneTeam: (formats) => `L\u2019\xE9quipe n\u2019a encore aucun fichier ${OR2.format(formats)}.`,
460
+ filePickerNoneMine: (formats) => `Vous n\u2019avez encore ni ajout\xE9 ni cr\xE9\xE9 de fichier ${OR2.format(formats)}.`,
461
+ filePickerShowEveryone: "Voir ceux de l\u2019\xE9quipe",
462
+ filePickerUpload: "Envoyer un fichier",
463
+ filePickerShown: (count) => `${count} ${plural(count, "fichier", "fichiers")}`,
464
+ testFile: "Fichier de test",
465
+ fileSize: formatBytes2,
466
+ fileDate: formatFileDate2,
467
+ fillWithExample: "Remplir avec un exemple",
468
+ examplesTitle: "Exemples",
469
+ examplesHint: "En choisir un remplit le formulaire. Rien ne s\u2019ex\xE9cute avant que vous le lanciez.",
470
+ examplesLoading: "Chargement des exemples\u2026",
471
+ examplesFailed: "Impossible de charger les exemples.",
472
+ examplesNone: "Aucun exemple pour le moment. Lancez-le, puis enregistrez l\u2019ex\xE9cution comme exemple.",
473
+ examplesShown: (count) => `${count} ${plural(count, "exemple", "exemples")}`,
474
+ replaceEnteredTitle: "Remplacer ce que vous avez saisi\xA0?",
475
+ replaceEnteredDescription: (name) => `Remplir le formulaire avec \xAB\xA0${name}\xA0\xBB remplace toutes ses valeurs.`,
476
+ replaceEnteredConfirm: "Remplacer",
477
+ exampleActions: (name) => `Actions pour ${name}`,
478
+ renameExample: "Renommer",
479
+ deleteExample: "Supprimer",
480
+ renameExampleTitle: "Renommer l\u2019exemple",
481
+ exampleName: "Nom",
482
+ renaming: "Renommage\u2026",
483
+ renameFailed: "Impossible de renommer l\u2019exemple.",
484
+ deleteExampleTitle: (name) => `Supprimer \xAB\xA0${name}\xA0\xBB\xA0?`,
485
+ deleteExampleDescription: "Cette action est irr\xE9versible.",
486
+ deleting: "Suppression\u2026",
487
+ deleteFailed: "Impossible de supprimer l\u2019exemple.",
488
+ close: "Fermer",
489
+ cancel: "Annuler",
490
+ save: "Enregistrer",
491
+ tryAgain: "R\xE9essayer",
492
+ showMore: "Afficher plus",
493
+ loadingMore: "Chargement de la suite\u2026",
494
+ loadMoreFailed: "Impossible de charger la suite."
342
495
  };
343
496
 
344
497
  // src/react/locales/index.ts
@@ -457,6 +610,622 @@ function encodedFileSummary(url, strings) {
457
610
  const format = [...DOCUMENT_FORMATS, ...IMAGE_FORMATS].find((known) => known.mimeType === view.mediaType)?.label ?? view.mediaType;
458
611
  return strings.encodedFileSummary(format, view.bytes);
459
612
  }
613
+ function startFor(key) {
614
+ return { key, status: key === null ? "idle" : "loading", items: [], cursor: null, more: "idle" };
615
+ }
616
+ function useSourcePages(load, key) {
617
+ const [stored, setState] = useState(() => startFor(key));
618
+ if (stored.key !== key) setState(startFor(key));
619
+ const state = stored.key === key ? stored : startFor(key);
620
+ const loadRef = useRef(load);
621
+ const inFlight = useRef(null);
622
+ const followed = useRef(/* @__PURE__ */ new Set());
623
+ useEffect(() => {
624
+ loadRef.current = load;
625
+ });
626
+ const read = useCallback(
627
+ (forKey, cursor, phase) => {
628
+ inFlight.current?.abort();
629
+ const controller = new AbortController();
630
+ inFlight.current = controller;
631
+ const seen = new Set(phase === "first" ? [] : followed.current);
632
+ const follow = (from) => {
633
+ if (from !== void 0) seen.add(from);
634
+ let page;
635
+ try {
636
+ page = Promise.resolve(loadRef.current(from, controller.signal));
637
+ } catch (error) {
638
+ page = Promise.reject(error);
639
+ }
640
+ page.then(
641
+ (answer) => {
642
+ if (inFlight.current !== controller) return;
643
+ const offered = answer.nextCursor ?? null;
644
+ const next = offered !== null && seen.has(offered) ? null : offered;
645
+ const items = [...answer.items];
646
+ if (items.length === 0 && next !== null) {
647
+ follow(next);
648
+ return;
649
+ }
650
+ inFlight.current = null;
651
+ followed.current = seen;
652
+ setState(
653
+ (previous) => previous.key !== forKey ? previous : {
654
+ key: forKey,
655
+ status: "ready",
656
+ items: phase === "first" ? items : [...previous.items, ...items],
657
+ cursor: next,
658
+ more: "idle"
659
+ }
660
+ );
661
+ },
662
+ () => {
663
+ if (inFlight.current !== controller) return;
664
+ inFlight.current = null;
665
+ setState(
666
+ (previous) => previous.key !== forKey ? previous : phase === "first" ? { key: forKey, status: "failed", items: [], cursor: null, more: "idle" } : { ...previous, more: "failed" }
667
+ );
668
+ }
669
+ );
670
+ };
671
+ follow(cursor);
672
+ },
673
+ []
674
+ );
675
+ useEffect(() => {
676
+ if (key === null) {
677
+ inFlight.current?.abort();
678
+ inFlight.current = null;
679
+ return;
680
+ }
681
+ read(key, void 0, "first");
682
+ }, [key, read]);
683
+ useEffect(
684
+ () => () => {
685
+ inFlight.current?.abort();
686
+ inFlight.current = null;
687
+ },
688
+ []
689
+ );
690
+ const loadMore = useCallback(() => {
691
+ if (state.status !== "ready" || state.cursor === null || state.more === "loading") return;
692
+ setState((previous) => ({ ...previous, more: "loading" }));
693
+ read(state.key, state.cursor, "more");
694
+ }, [state.key, state.status, state.cursor, state.more, read]);
695
+ const retry = useCallback(() => {
696
+ if (state.status === "failed") {
697
+ setState((previous) => startFor(previous.key));
698
+ read(state.key, void 0, "first");
699
+ } else if (state.more === "failed") {
700
+ loadMore();
701
+ }
702
+ }, [state.key, state.status, state.more, read, loadMore]);
703
+ const updateItems = useCallback((update) => {
704
+ setState((previous) => ({ ...previous, items: update(previous.items) }));
705
+ }, []);
706
+ const drained = state.status === "ready" && state.items.length === 0 && state.cursor !== null && state.more === "idle";
707
+ useEffect(() => {
708
+ if (drained) loadMore();
709
+ }, [drained, loadMore]);
710
+ return {
711
+ status: state.status,
712
+ items: state.items,
713
+ hasMore: state.cursor !== null,
714
+ empty: state.status === "ready" && state.items.length === 0 && state.cursor === null,
715
+ more: state.more,
716
+ loadMore,
717
+ retry,
718
+ updateItems
719
+ };
720
+ }
721
+ function DimRows({ count = 4 }) {
722
+ return /* @__PURE__ */ jsx("div", { "aria-hidden": "true", "data-dim-rows": true, className: "divide-y divide-border", children: Array.from({ length: count }, (_, index) => /* @__PURE__ */ jsxs("div", { className: "flex min-h-16 items-center gap-3 px-4 py-3 opacity-60", children: [
723
+ /* @__PURE__ */ jsx("span", { className: "size-8 shrink-0 rounded-md bg-muted" }),
724
+ /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-2", children: [
725
+ /* @__PURE__ */ jsx("span", { className: "h-3.5 w-2/5 rounded-sm bg-muted" }),
726
+ /* @__PURE__ */ jsx("span", { className: "h-3 w-3/5 rounded-sm bg-muted" })
727
+ ] })
728
+ ] }, index)) });
729
+ }
730
+ function ListAction({
731
+ onClick,
732
+ children,
733
+ className
734
+ }) {
735
+ return /* @__PURE__ */ jsx(
736
+ "button",
737
+ {
738
+ type: "button",
739
+ onClick,
740
+ className: cn(
741
+ "inline-flex min-h-11 items-center justify-center rounded-md border border-border px-4 text-[14px] text-foreground transition-colors hover:bg-muted sm:min-h-9",
742
+ className
743
+ ),
744
+ children
745
+ }
746
+ );
747
+ }
748
+ function ListMessage({ text, children }) {
749
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 px-4 py-10 text-center", children: [
750
+ /* @__PURE__ */ jsx("p", { className: "text-[14px] text-foreground", children: text }),
751
+ children && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap justify-center gap-2", children })
752
+ ] });
753
+ }
754
+ function MoreFooter({ pages }) {
755
+ const s = useFieldStrings();
756
+ if (pages.more === "failed") {
757
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-center gap-3 px-4 py-4", children: [
758
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] text-muted-foreground", children: s.loadMoreFailed }),
759
+ /* @__PURE__ */ jsx(ListAction, { onClick: pages.retry, children: s.tryAgain })
760
+ ] });
761
+ }
762
+ if (pages.more === "loading") {
763
+ return /* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-2 px-4 py-4 text-[13px] text-muted-foreground", children: [
764
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }),
765
+ s.loadingMore
766
+ ] });
767
+ }
768
+ if (!pages.hasMore) return null;
769
+ return /* @__PURE__ */ jsx("div", { className: "flex justify-center px-4 py-4", children: /* @__PURE__ */ jsx(ListAction, { onClick: pages.loadMore, children: s.showMore }) });
770
+ }
771
+ var Dialog = DialogPrimitive.Root;
772
+ var DialogClose = DialogPrimitive.Close;
773
+ function closeAutoFocus(onCloseAutoFocus, returnFocus) {
774
+ return (event) => {
775
+ onCloseAutoFocus?.(event);
776
+ if (event.defaultPrevented || !returnFocus) return;
777
+ const target = returnFocus();
778
+ if (!target?.isConnected) return;
779
+ event.preventDefault();
780
+ target.focus();
781
+ };
782
+ }
783
+ var DialogOverlay = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
784
+ DialogPrimitive.Overlay,
785
+ {
786
+ ref,
787
+ className: cn("fixed inset-0 z-50 bg-black/50", className),
788
+ ...props
789
+ }
790
+ ));
791
+ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
792
+ var DialogContent = React5.forwardRef(({ className, layout = "dialog", returnFocus, onCloseAutoFocus, children, ...props }, ref) => /* @__PURE__ */ jsxs(Fragment, { children: [
793
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
794
+ /* @__PURE__ */ jsx(
795
+ DialogPrimitive.Content,
796
+ {
797
+ ref,
798
+ className: cn(
799
+ "fixed z-50 flex flex-col border-border bg-popover text-popover-foreground shadow-lg outline-hidden",
800
+ layout === "sheet" ? [
801
+ "inset-x-0 top-6 bottom-0 rounded-t-xl border-t",
802
+ "xl:inset-0 xl:m-auto xl:h-fit xl:max-h-[min(720px,calc(100dvh-4rem))] xl:w-[calc(100%-2rem)] xl:max-w-[560px] xl:rounded-xl xl:border"
803
+ ] : "inset-0 m-auto h-fit w-[calc(100%-2rem)] max-w-sm gap-3 rounded-lg border p-4",
804
+ className
805
+ ),
806
+ onCloseAutoFocus: closeAutoFocus(onCloseAutoFocus, returnFocus),
807
+ ...props,
808
+ children
809
+ }
810
+ )
811
+ ] }));
812
+ DialogContent.displayName = DialogPrimitive.Content.displayName;
813
+ var DialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
814
+ DialogPrimitive.Title,
815
+ {
816
+ ref,
817
+ className: cn("text-[16px] font-semibold text-foreground", className),
818
+ ...props
819
+ }
820
+ ));
821
+ DialogTitle.displayName = DialogPrimitive.Title.displayName;
822
+ var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
823
+ DialogPrimitive.Description,
824
+ {
825
+ ref,
826
+ className: cn("text-[13px] text-muted-foreground", className),
827
+ ...props
828
+ }
829
+ ));
830
+ DialogDescription.displayName = DialogPrimitive.Description.displayName;
831
+ var toggleVariants = cva(
832
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
833
+ {
834
+ variants: {
835
+ variant: {
836
+ default: "bg-transparent",
837
+ outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground"
838
+ },
839
+ size: {
840
+ default: "h-10 px-3 min-w-10",
841
+ sm: "h-9 px-2.5 min-w-9",
842
+ lg: "h-11 px-5 min-w-11"
843
+ }
844
+ },
845
+ defaultVariants: {
846
+ variant: "default",
847
+ size: "default"
848
+ }
849
+ }
850
+ );
851
+ var Toggle = React5.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
852
+ TogglePrimitive.Root,
853
+ {
854
+ ref,
855
+ className: cn(toggleVariants({ variant, size, className })),
856
+ ...props
857
+ }
858
+ ));
859
+ Toggle.displayName = TogglePrimitive.Root.displayName;
860
+ var ToggleGroupContext = React5.createContext({
861
+ size: "default",
862
+ variant: "default"
863
+ });
864
+ var ToggleGroup = React5.forwardRef(({ className, variant, size, children, ...props }, ref) => /* @__PURE__ */ jsx(
865
+ ToggleGroupPrimitive.Root,
866
+ {
867
+ ref,
868
+ className: cn("flex items-center justify-center gap-1", className),
869
+ ...props,
870
+ children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
871
+ }
872
+ ));
873
+ ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
874
+ var ToggleGroupItem = React5.forwardRef(({ className, children, variant, size, ...props }, ref) => {
875
+ const context = React5.useContext(ToggleGroupContext);
876
+ return /* @__PURE__ */ jsx(
877
+ ToggleGroupPrimitive.Item,
878
+ {
879
+ ref,
880
+ className: cn(
881
+ toggleVariants({
882
+ variant: context.variant || variant,
883
+ size: context.size || size
884
+ }),
885
+ className
886
+ ),
887
+ ...props,
888
+ children
889
+ }
890
+ );
891
+ });
892
+ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
893
+ var SEARCH_PAUSE_MS = 250;
894
+ var SWIPE_CLOSE_PX = 96;
895
+ function FilePickerEntry({ onOpen, label, disabled, ref, id }) {
896
+ const s = useFieldStrings();
897
+ return /* @__PURE__ */ jsxs(
898
+ "button",
899
+ {
900
+ ref,
901
+ id,
902
+ type: "button",
903
+ onClick: onOpen,
904
+ disabled,
905
+ "aria-haspopup": "dialog",
906
+ "aria-label": s.chooseFromSourceAria(label),
907
+ className: "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",
908
+ children: [
909
+ /* @__PURE__ */ jsx(Folder, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
910
+ s.chooseFromSource
911
+ ]
912
+ }
913
+ );
914
+ }
915
+ function FilePicker({
916
+ open,
917
+ onOpenChange,
918
+ returnFocusTo,
919
+ onCloseAutoFocus,
920
+ disabled = false,
921
+ ...props
922
+ }) {
923
+ const [drag, setDrag] = useState(0);
924
+ const shown = open && !disabled;
925
+ useEffect(() => {
926
+ if (open && disabled) onOpenChange(false);
927
+ }, [open, disabled, onOpenChange]);
928
+ const heldFocus = useRef(null);
929
+ useLayoutEffect(() => {
930
+ if (!open) return;
931
+ const active = document.activeElement;
932
+ heldFocus.current = active instanceof HTMLElement && active !== document.body ? active : null;
933
+ }, [open]);
934
+ return /* @__PURE__ */ jsx(Dialog, { open: shown, onOpenChange, children: shown && /* @__PURE__ */ jsx(
935
+ DialogContent,
936
+ {
937
+ layout: "sheet",
938
+ onCloseAutoFocus,
939
+ returnFocus: () => returnFocusTo?.current ?? heldFocus.current,
940
+ style: drag > 0 ? { transform: `translateY(${drag}px)` } : void 0,
941
+ onClick: (event) => event.stopPropagation(),
942
+ children: /* @__PURE__ */ jsx(FilePickerBody, { ...props, onDrag: setDrag, onClose: () => onOpenChange(false) })
943
+ }
944
+ ) });
945
+ }
946
+ function FilePickerBody({
947
+ source,
948
+ formats,
949
+ multiple = false,
950
+ maxChoices,
951
+ held,
952
+ onUse,
953
+ onUpload,
954
+ onDrag,
955
+ onClose
956
+ }) {
957
+ const s = useFieldStrings();
958
+ const searchId = useId();
959
+ const [search, setSearch] = useState("");
960
+ const [query, setQuery] = useState("");
961
+ const [scope, setScope] = useState(source.initialScope);
962
+ const [chosen, setChosen] = useState(/* @__PURE__ */ new Map());
963
+ useEffect(() => {
964
+ const next = search.trim();
965
+ if (next === query) return;
966
+ const timer = setTimeout(() => setQuery(next), SEARCH_PAUSE_MS);
967
+ return () => clearTimeout(timer);
968
+ }, [search, query]);
969
+ const mediaTypes = useMemo(() => formats.map((format) => format.mimeType), [formats]);
970
+ const labels = useMemo(() => formats.map((format) => format.label), [formats]);
971
+ const load = (cursor, signal) => source.list({ search: query, scope, mediaTypes, cursor, signal }).then((page) => ({ items: page.files, nextCursor: page.nextCursor }));
972
+ const pages = useSourcePages(load, JSON.stringify([query, scope, mediaTypes]));
973
+ const fits = (files) => maxChoices === void 0 || chosenFilesAdded(held, files) <= maxChoices;
974
+ const toggle = (file) => setChosen((previous) => {
975
+ if (!multiple) return previous.has(file.id) ? previous : /* @__PURE__ */ new Map([[file.id, file]]);
976
+ const next = new Map(previous);
977
+ if (next.has(file.id)) next.delete(file.id);
978
+ else if (fits([...next.values(), file])) next.set(file.id, file);
979
+ return next;
980
+ });
981
+ const choices = [...chosen.values()];
982
+ const use6 = () => {
983
+ if (choices.length === 0) return;
984
+ onUse(choices);
985
+ onClose();
986
+ };
987
+ const clearSearch = () => {
988
+ setSearch("");
989
+ setQuery("");
990
+ };
991
+ const status = pickerStatus(s, pages, query, scope, labels);
992
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
993
+ /* @__PURE__ */ jsxs(SheetHeader, { onDrag, onClose, children: [
994
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-[20px]", children: s.filePickerTitle }),
995
+ /* @__PURE__ */ jsx(DialogDescription, { children: s.filePickerTakes(labels) })
996
+ ] }),
997
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-4 pb-3", children: [
998
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
999
+ /* @__PURE__ */ jsx("label", { htmlFor: searchId, className: "sr-only", children: s.filePickerSearch }),
1000
+ /* @__PURE__ */ jsx(
1001
+ Search,
1002
+ {
1003
+ className: "pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground",
1004
+ "aria-hidden": "true"
1005
+ }
1006
+ ),
1007
+ /* @__PURE__ */ jsx(
1008
+ "input",
1009
+ {
1010
+ id: searchId,
1011
+ type: "search",
1012
+ value: search,
1013
+ onChange: (event) => setSearch(event.target.value),
1014
+ onKeyDown: (event) => {
1015
+ if (event.key === "Enter") event.preventDefault();
1016
+ },
1017
+ placeholder: s.filePickerSearchPlaceholder,
1018
+ autoComplete: "off",
1019
+ className: "h-11 w-full rounded-md border border-border bg-input pr-3 pl-9 text-[16px] text-foreground placeholder:text-muted-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring"
1020
+ }
1021
+ )
1022
+ ] }),
1023
+ /* @__PURE__ */ jsxs(
1024
+ ToggleGroup,
1025
+ {
1026
+ type: "single",
1027
+ value: scope,
1028
+ onValueChange: (value) => {
1029
+ if (value === "mine" || value === "everyone") setScope(value);
1030
+ },
1031
+ "aria-label": s.filePickerScope,
1032
+ className: "grid grid-cols-2 gap-1 rounded-md border border-border p-1",
1033
+ children: [
1034
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "mine", className: "h-11 w-full text-[15px]", children: s.filePickerMine }),
1035
+ /* @__PURE__ */ jsx(ToggleGroupItem, { value: "everyone", className: "h-11 w-full text-[15px]", children: s.filePickerEveryone })
1036
+ ]
1037
+ }
1038
+ )
1039
+ ] }),
1040
+ /* @__PURE__ */ jsx("p", { role: "status", className: "sr-only", children: status }),
1041
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto border-t border-border", children: pages.status === "idle" || pages.status === "loading" ? /* @__PURE__ */ jsx(DimRows, {}) : pages.status === "failed" ? /* @__PURE__ */ jsx(ListMessage, { text: s.filePickerFailed, children: /* @__PURE__ */ jsx(ListAction, { onClick: pages.retry, children: s.tryAgain }) }) : pages.empty ? query !== "" ? /* @__PURE__ */ jsx(ListMessage, { text: s.filePickerNoMatch, children: /* @__PURE__ */ jsx(ListAction, { onClick: clearSearch, children: s.filePickerClearSearch }) }) : /* @__PURE__ */ jsxs(ListMessage, { text: status, children: [
1042
+ scope === "mine" && /* @__PURE__ */ jsx(ListAction, { onClick: () => setScope("everyone"), children: s.filePickerShowEveryone }),
1043
+ onUpload && /* @__PURE__ */ jsx(
1044
+ ListAction,
1045
+ {
1046
+ onClick: () => {
1047
+ onClose();
1048
+ onUpload();
1049
+ },
1050
+ children: s.filePickerUpload
1051
+ }
1052
+ )
1053
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
1054
+ pages.items.length > 0 && /* @__PURE__ */ jsx(
1055
+ "div",
1056
+ {
1057
+ role: multiple ? "group" : "radiogroup",
1058
+ "aria-label": s.filePickerTitle,
1059
+ className: "divide-y divide-border",
1060
+ children: pages.items.map((file) => {
1061
+ const isChosen = chosen.has(file.id);
1062
+ return /* @__PURE__ */ jsx(
1063
+ FileRow,
1064
+ {
1065
+ file,
1066
+ formats,
1067
+ role: multiple ? "checkbox" : "radio",
1068
+ chosen: isChosen,
1069
+ disabled: !isChosen && !fits([...choices, file]),
1070
+ onToggle: () => toggle(file)
1071
+ },
1072
+ file.id
1073
+ );
1074
+ })
1075
+ }
1076
+ ),
1077
+ /* @__PURE__ */ jsx(MoreFooter, { pages })
1078
+ ] }) }),
1079
+ /* @__PURE__ */ jsx("div", { className: "border-t border-border px-4 pt-3 pb-[max(0.75rem,env(safe-area-inset-bottom))]", children: /* @__PURE__ */ jsx(
1080
+ "button",
1081
+ {
1082
+ type: "button",
1083
+ onClick: use6,
1084
+ disabled: choices.length === 0,
1085
+ className: "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",
1086
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: s.filePickerUse(choices.length, choices[0]?.name) })
1087
+ }
1088
+ ) })
1089
+ ] });
1090
+ }
1091
+ function pickerStatus(s, pages, query, scope, labels) {
1092
+ if (pages.status === "idle" || pages.status === "loading") return s.filePickerLoading;
1093
+ if (pages.status === "failed") return s.filePickerFailed;
1094
+ if (pages.more === "failed") return s.loadMoreFailed;
1095
+ if (pages.items.length > 0) return s.filePickerShown(pages.items.length);
1096
+ if (!pages.empty) return s.filePickerLoading;
1097
+ if (query !== "") return s.filePickerNoMatch;
1098
+ return scope === "mine" ? s.filePickerNoneMine(labels) : s.filePickerNoneTeam(labels);
1099
+ }
1100
+ function SheetHeader({
1101
+ onDrag,
1102
+ onClose,
1103
+ children
1104
+ }) {
1105
+ const s = useFieldStrings();
1106
+ const start = useRef(null);
1107
+ const offset = useRef(0);
1108
+ const onPointerDown = (event) => {
1109
+ if (event.pointerType === "mouse") return;
1110
+ if (window.matchMedia?.("(min-width: 80rem)").matches) return;
1111
+ start.current = event.clientY;
1112
+ offset.current = 0;
1113
+ };
1114
+ const onPointerMove = (event) => {
1115
+ if (start.current === null) return;
1116
+ offset.current = Math.max(0, event.clientY - start.current);
1117
+ onDrag(offset.current);
1118
+ };
1119
+ const onPointerEnd = () => {
1120
+ if (start.current === null) return;
1121
+ const closes = offset.current > SWIPE_CLOSE_PX;
1122
+ start.current = null;
1123
+ offset.current = 0;
1124
+ onDrag(0);
1125
+ if (closes) onClose();
1126
+ };
1127
+ return /* @__PURE__ */ jsxs(
1128
+ "div",
1129
+ {
1130
+ className: "touch-none px-4 pt-2 pb-3 select-none",
1131
+ onPointerDown,
1132
+ onPointerMove,
1133
+ onPointerUp: onPointerEnd,
1134
+ onPointerCancel: onPointerEnd,
1135
+ children: [
1136
+ /* @__PURE__ */ jsx(
1137
+ "span",
1138
+ {
1139
+ "aria-hidden": "true",
1140
+ className: "mx-auto mb-2 block h-1.5 w-12 rounded-full bg-muted-foreground/40 xl:hidden"
1141
+ }
1142
+ ),
1143
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
1144
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1 space-y-1 pt-1", children }),
1145
+ /* @__PURE__ */ jsx(
1146
+ DialogClose,
1147
+ {
1148
+ "aria-label": s.close,
1149
+ className: "flex size-11 shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
1150
+ children: /* @__PURE__ */ jsx(X, { className: "size-5", "aria-hidden": "true" })
1151
+ }
1152
+ )
1153
+ ] })
1154
+ ]
1155
+ }
1156
+ );
1157
+ }
1158
+ function rowFormat(file, formats) {
1159
+ const type = file.mediaType ? normalizeMimeType(file.mediaType) : "";
1160
+ return formats.find((format) => format.mimeType === type) ?? formatOfFilename(formats, file.name);
1161
+ }
1162
+ function rowMeta(s, file, format) {
1163
+ const parts = [];
1164
+ if (format) parts.push(format.label);
1165
+ if (file.size !== void 0) parts.push(s.fileSize(file.size));
1166
+ if (file.origin) parts.push(file.origin);
1167
+ if (file.createdAt) {
1168
+ const date = new Date(file.createdAt);
1169
+ if (!Number.isNaN(date.getTime())) parts.push(s.fileDate(date));
1170
+ }
1171
+ return parts.join(" \xB7 ");
1172
+ }
1173
+ function FileRow({
1174
+ file,
1175
+ formats,
1176
+ role,
1177
+ chosen,
1178
+ disabled,
1179
+ onToggle
1180
+ }) {
1181
+ const s = useFieldStrings();
1182
+ const format = rowFormat(file, formats);
1183
+ const isImage = format?.mimeType.startsWith("image/") ?? false;
1184
+ const meta = rowMeta(s, file, format);
1185
+ const Glyph = isImage ? ImageIcon : FileText;
1186
+ return /* @__PURE__ */ jsxs(
1187
+ "button",
1188
+ {
1189
+ type: "button",
1190
+ role,
1191
+ "aria-checked": chosen,
1192
+ disabled,
1193
+ onClick: onToggle,
1194
+ className: cn(
1195
+ "flex min-h-16 w-full items-center gap-3 px-4 py-2.5 text-left transition-colors hover:bg-muted/60 disabled:cursor-not-allowed disabled:opacity-50",
1196
+ "focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring",
1197
+ chosen && "bg-muted"
1198
+ ),
1199
+ children: [
1200
+ /* @__PURE__ */ jsx(
1201
+ Glyph,
1202
+ {
1203
+ className: "size-6 shrink-0 text-muted-foreground",
1204
+ strokeWidth: 1.5,
1205
+ "aria-hidden": "true"
1206
+ }
1207
+ ),
1208
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1209
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-[16px] font-medium text-foreground", children: file.name }),
1210
+ (file.kind === "test" || meta) && /* @__PURE__ */ jsxs("span", { className: "block truncate text-[13px] text-muted-foreground", children: [
1211
+ file.kind === "test" && /* @__PURE__ */ jsxs("span", { className: "text-foreground", children: [
1212
+ s.testFile,
1213
+ meta && " \xB7 "
1214
+ ] }),
1215
+ meta
1216
+ ] })
1217
+ ] }),
1218
+ /* @__PURE__ */ jsx(
1219
+ Check,
1220
+ {
1221
+ className: cn("size-5 shrink-0 text-foreground", !chosen && "invisible"),
1222
+ "aria-hidden": "true"
1223
+ }
1224
+ )
1225
+ ]
1226
+ }
1227
+ );
1228
+ }
460
1229
  var IMAGE_EXT_RE = /\.(jpg|jpeg|png|gif|webp|bmp|svg|avif)(\?|$)/i;
461
1230
  var PDF_EXT_RE = /\.pdf(\?|$)/i;
462
1231
  var DATA_URL_MIME_RE = /^data:([^;,]+)/i;
@@ -484,6 +1253,7 @@ function FileField({
484
1253
  uploading,
485
1254
  uploadError,
486
1255
  resolveUrl,
1256
+ fileSource,
487
1257
  error,
488
1258
  disabled,
489
1259
  category
@@ -497,6 +1267,9 @@ function FileField({
497
1267
  const [localPreview, setLocalPreview] = useState(null);
498
1268
  const [resolved, setResolved] = useState(null);
499
1269
  const localUrlRef = useRef(null);
1270
+ const [pickerOpen, setPickerOpen] = useState(false);
1271
+ const cardRef = useRef(null);
1272
+ const pickerEntryRef = useRef(null);
500
1273
  const setLocal = useCallback((next) => {
501
1274
  if (localUrlRef.current) URL.revokeObjectURL(localUrlRef.current);
502
1275
  localUrlRef.current = next?.objectUrl ?? null;
@@ -532,7 +1305,12 @@ function FileField({
532
1305
  [formats, setLocal, onDropFile]
533
1306
  );
534
1307
  const busy = disabled || uploading;
535
- const { getRootProps, getInputProps, isDragActive } = useDropzone({
1308
+ const {
1309
+ getRootProps,
1310
+ getInputProps,
1311
+ isDragActive,
1312
+ open: openFileDialog
1313
+ } = useDropzone({
536
1314
  maxFiles: 1,
537
1315
  multiple: false,
538
1316
  // Disabled, not merely unrendered, when there is no upload path: the hook
@@ -570,7 +1348,30 @@ function FileField({
570
1348
  dismissUploadError();
571
1349
  onChange(void 0);
572
1350
  }, [setLocal, onChange, dismissUploadError]);
1351
+ const chooseFromSource = useCallback(
1352
+ (file) => {
1353
+ setLocal(null);
1354
+ setResolved(null);
1355
+ setRejected(null);
1356
+ dismissUploadError();
1357
+ onChange(sourceFileValue(file));
1358
+ },
1359
+ [setLocal, onChange, dismissUploadError]
1360
+ );
573
1361
  const localIsCurrent = !!localPreview && (localPreview.boundUrl === void 0 || localPreview.boundUrl === value?.url);
1362
+ const wasUploading = useRef(!!uploading);
1363
+ useEffect(() => {
1364
+ const ended = wasUploading.current && !uploading;
1365
+ wasUploading.current = !!uploading;
1366
+ if (!localPreview || localPreview.boundUrl !== void 0) return;
1367
+ if (failure !== null || ended && !value?.url) setLocal(null);
1368
+ }, [uploading, failure, localPreview, value?.url, setLocal]);
1369
+ const lastUrl = useRef(value?.url);
1370
+ useEffect(() => {
1371
+ if (lastUrl.current === value?.url) return;
1372
+ lastUrl.current = value?.url;
1373
+ if (uploadError !== void 0) setDismissedUploadError(uploadError);
1374
+ }, [value?.url, uploadError]);
574
1375
  useEffect(() => {
575
1376
  if (!localPreview) return;
576
1377
  if (localPreview.boundUrl === void 0) {
@@ -611,13 +1412,15 @@ function FileField({
611
1412
  };
612
1413
  const urlValue = value?.url ?? "";
613
1414
  const urlText = presentation === "studio" || isReadableLink(urlValue) || urlValue === typedUrl ? urlValue : "";
614
- if (!canUpload && !allowUrl) {
1415
+ const canChoose = fileSource !== void 0;
1416
+ if (!canUpload && !allowUrl && !canChoose) {
615
1417
  throw new Error(
616
- `The file field at "${id}" has no way in: the form's environment supplies no onDropFile and sets allowUrl to false. Supply onDropFile so the file can be uploaded, or leave allowUrl unset so a link can be pasted.`
1418
+ `The file field at "${id}" has no way in: the form's environment supplies no onDropFile and no fileSource, and sets allowUrl to false. Supply onDropFile so the file can be uploaded, leave allowUrl unset so a link can be pasted, or supply a fileSource so a stored file can be chosen.`
617
1419
  );
618
1420
  }
619
1421
  const linkOpen = allowUrl && (!canUpload || showUrl);
620
- const noUploadLineShown = !canUpload && !hasFile && !uploading;
1422
+ const noUploadLineShown = !canUpload && allowUrl && !hasFile && !uploading;
1423
+ const choosingOnly = !canUpload && !allowUrl;
621
1424
  const noUploadLineId = `${domId}-no-upload`;
622
1425
  return /* @__PURE__ */ jsxs(
623
1426
  FieldShell,
@@ -631,7 +1434,7 @@ function FileField({
631
1434
  error,
632
1435
  htmlFor: domId,
633
1436
  children: [
634
- hasFile && !uploading ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1437
+ hasFile && !uploading ? /* @__PURE__ */ jsxs("div", { ref: cardRef, tabIndex: -1, className: "space-y-2 outline-none", children: [
635
1438
  /* @__PURE__ */ jsx(
636
1439
  FileChip,
637
1440
  {
@@ -661,7 +1464,7 @@ function FileField({
661
1464
  /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
662
1465
  s.uploading
663
1466
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
664
- /* @__PURE__ */ jsx("p", { id: noUploadLineId, className: "text-[12px] text-muted-foreground", children: s.uploadUnavailable }),
1467
+ noUploadLineShown && /* @__PURE__ */ jsx("p", { id: noUploadLineId, className: "text-[12px] text-muted-foreground", children: s.uploadUnavailable }),
665
1468
  hint && /* @__PURE__ */ jsx("p", { className: "font-mono text-[10.5px] text-muted-foreground", children: hint })
666
1469
  ] }) })
667
1470
  ) : (
@@ -734,14 +1537,45 @@ function FileField({
734
1537
  ) : allowUrl && /* @__PURE__ */ jsxs(
735
1538
  "button",
736
1539
  {
737
- type: "button",
1540
+ type: "button",
1541
+ disabled: busy,
1542
+ onClick: () => setShowUrl(true),
1543
+ className: "flex w-fit items-center gap-1 font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 max-sm:min-h-11",
1544
+ children: [
1545
+ /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
1546
+ s.pasteUrlInstead
1547
+ ]
1548
+ }
1549
+ ),
1550
+ fileSource && !hasFile && /* @__PURE__ */ jsx(
1551
+ FilePickerEntry,
1552
+ {
1553
+ ref: pickerEntryRef,
1554
+ id: choosingOnly ? domId : void 0,
1555
+ label: fieldLabel(field.title, field.name, presentation),
1556
+ disabled: busy,
1557
+ onOpen: () => setPickerOpen(true)
1558
+ }
1559
+ ),
1560
+ fileSource && /* @__PURE__ */ jsx(
1561
+ FilePicker,
1562
+ {
1563
+ source: fileSource,
1564
+ formats,
1565
+ open: pickerOpen,
1566
+ onOpenChange: setPickerOpen,
1567
+ onUse: (files) => {
1568
+ const file = files[0];
1569
+ if (file) chooseFromSource(file);
1570
+ },
1571
+ onUpload: canUpload ? openFileDialog : void 0,
738
1572
  disabled: busy,
739
- onClick: () => setShowUrl(true),
740
- className: "flex w-fit items-center gap-1 font-mono text-[11px] text-muted-foreground transition-colors hover:text-foreground disabled:opacity-50 max-sm:min-h-11",
741
- children: [
742
- /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3" }),
743
- s.pasteUrlInstead
744
- ]
1573
+ returnFocusTo: pickerEntryRef,
1574
+ onCloseAutoFocus: (event) => {
1575
+ if (!cardRef.current) return;
1576
+ event.preventDefault();
1577
+ cardRef.current.focus();
1578
+ }
745
1579
  }
746
1580
  )
747
1581
  ]
@@ -843,7 +1677,7 @@ function ClearButton({ disabled, onClick }) {
843
1677
  }
844
1678
  var Select = SelectPrimitive.Root;
845
1679
  var SelectValue = SelectPrimitive.Value;
846
- var SelectTrigger = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1680
+ var SelectTrigger = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
847
1681
  SelectPrimitive.Trigger,
848
1682
  {
849
1683
  ref,
@@ -862,7 +1696,7 @@ var SelectTrigger = React2.forwardRef(({ className, children, ...props }, ref) =
862
1696
  }
863
1697
  ));
864
1698
  SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
865
- var SelectScrollUpButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1699
+ var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
866
1700
  SelectPrimitive.ScrollUpButton,
867
1701
  {
868
1702
  ref,
@@ -872,7 +1706,7 @@ var SelectScrollUpButton = React2.forwardRef(({ className, ...props }, ref) => /
872
1706
  }
873
1707
  ));
874
1708
  SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
875
- var SelectScrollDownButton = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1709
+ var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
876
1710
  SelectPrimitive.ScrollDownButton,
877
1711
  {
878
1712
  ref,
@@ -882,7 +1716,7 @@ var SelectScrollDownButton = React2.forwardRef(({ className, ...props }, ref) =>
882
1716
  }
883
1717
  ));
884
1718
  SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
885
- var SelectContent = React2.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
1719
+ var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
886
1720
  SelectPrimitive.Content,
887
1721
  {
888
1722
  ref,
@@ -910,7 +1744,7 @@ var SelectContent = React2.forwardRef(({ className, children, position = "popper
910
1744
  }
911
1745
  ) }));
912
1746
  SelectContent.displayName = SelectPrimitive.Content.displayName;
913
- var SelectLabel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1747
+ var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
914
1748
  SelectPrimitive.Label,
915
1749
  {
916
1750
  ref,
@@ -919,7 +1753,7 @@ var SelectLabel = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE
919
1753
  }
920
1754
  ));
921
1755
  SelectLabel.displayName = SelectPrimitive.Label.displayName;
922
- var SelectItem = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1756
+ var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
923
1757
  SelectPrimitive.Item,
924
1758
  {
925
1759
  ref,
@@ -935,7 +1769,7 @@ var SelectItem = React2.forwardRef(({ className, children, ...props }, ref) => /
935
1769
  }
936
1770
  ));
937
1771
  SelectItem.displayName = SelectPrimitive.Item.displayName;
938
- var SelectSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1772
+ var SelectSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
939
1773
  SelectPrimitive.Separator,
940
1774
  {
941
1775
  ref,
@@ -944,68 +1778,6 @@ var SelectSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__
944
1778
  }
945
1779
  ));
946
1780
  SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
947
- var toggleVariants = cva(
948
- "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
949
- {
950
- variants: {
951
- variant: {
952
- default: "bg-transparent",
953
- outline: "border border-input bg-transparent hover:bg-accent hover:text-accent-foreground"
954
- },
955
- size: {
956
- default: "h-10 px-3 min-w-10",
957
- sm: "h-9 px-2.5 min-w-9",
958
- lg: "h-11 px-5 min-w-11"
959
- }
960
- },
961
- defaultVariants: {
962
- variant: "default",
963
- size: "default"
964
- }
965
- }
966
- );
967
- var Toggle = React2.forwardRef(({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
968
- TogglePrimitive.Root,
969
- {
970
- ref,
971
- className: cn(toggleVariants({ variant, size, className })),
972
- ...props
973
- }
974
- ));
975
- Toggle.displayName = TogglePrimitive.Root.displayName;
976
- var ToggleGroupContext = React2.createContext({
977
- size: "default",
978
- variant: "default"
979
- });
980
- var ToggleGroup = React2.forwardRef(({ className, variant, size, children, ...props }, ref) => /* @__PURE__ */ jsx(
981
- ToggleGroupPrimitive.Root,
982
- {
983
- ref,
984
- className: cn("flex items-center justify-center gap-1", className),
985
- ...props,
986
- children: /* @__PURE__ */ jsx(ToggleGroupContext.Provider, { value: { variant, size }, children })
987
- }
988
- ));
989
- ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName;
990
- var ToggleGroupItem = React2.forwardRef(({ className, children, variant, size, ...props }, ref) => {
991
- const context = React2.useContext(ToggleGroupContext);
992
- return /* @__PURE__ */ jsx(
993
- ToggleGroupPrimitive.Item,
994
- {
995
- ref,
996
- className: cn(
997
- toggleVariants({
998
- variant: context.variant || variant,
999
- size: context.size || size
1000
- }),
1001
- className
1002
- ),
1003
- ...props,
1004
- children
1005
- }
1006
- );
1007
- });
1008
- ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
1009
1781
  var CLEAR_VALUE = "__none__";
1010
1782
  function EnumField({ field, value, onChange, id, error, disabled }) {
1011
1783
  const s = useFieldStrings();
@@ -1288,12 +2060,39 @@ function ObjectField({ field, value, onChange, id, error, env }) {
1288
2060
  error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
1289
2061
  ] });
1290
2062
  }
2063
+
2064
+ // src/react/focus.ts
2065
+ var FOCUSABLE = "a[href], button, input, select, textarea, [tabindex]";
2066
+ function takesFocus(element) {
2067
+ if (element.tabIndex < 0 || element.matches(":disabled")) return false;
2068
+ return element.closest("[hidden], [inert]") === null;
2069
+ }
2070
+ function followingFocusable(from, skip) {
2071
+ if (!from) return null;
2072
+ for (const element of from.ownerDocument.querySelectorAll(FOCUSABLE)) {
2073
+ if (!(from.compareDocumentPosition(element) & Node.DOCUMENT_POSITION_FOLLOWING)) continue;
2074
+ if (skip?.contains(element)) continue;
2075
+ if (takesFocus(element)) return element;
2076
+ }
2077
+ return null;
2078
+ }
2079
+ function firstFocusable(within) {
2080
+ if (!within) return null;
2081
+ for (const element of within.querySelectorAll(FOCUSABLE)) {
2082
+ if (takesFocus(element)) return element;
2083
+ }
2084
+ return null;
2085
+ }
1291
2086
  function ListField({ field, value, onChange, id, error, env }) {
1292
2087
  const s = useFieldStrings();
1293
2088
  const presentation = useFieldPresentation();
1294
2089
  const isApp = presentation === "app";
1295
2090
  const items = value ?? [];
1296
2091
  const [rowKeys, setRowKeys] = useRowKeys(items.length);
2092
+ const [pickerOpen, setPickerOpen] = useState(false);
2093
+ const pickerEntryRef = useRef(null);
2094
+ const rowsRef = useRef(null);
2095
+ const landedRow = useRef(null);
1297
2096
  const [staleErrors, setStaleErrors] = useState(NO_ERRORS);
1298
2097
  const setItem = (index, itemValue) => {
1299
2098
  const next = items.slice();
@@ -1337,6 +2136,9 @@ function ListField({ field, value, onChange, id, error, env }) {
1337
2136
  };
1338
2137
  }, [env, staleErrors]);
1339
2138
  const isFull = field.maxItemCount !== void 0 && items.length >= field.maxItemCount;
2139
+ const fileSource = env?.fileSource;
2140
+ const fileItem = field.item.kind === "document" || field.item.kind === "image" ? field.item : null;
2141
+ const room = roomForChosenFiles(items, field.maxItemCount);
1340
2142
  return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1341
2143
  /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-1", children: [
1342
2144
  /* @__PURE__ */ jsx(
@@ -1354,11 +2156,12 @@ function ListField({ field, value, onChange, id, error, env }) {
1354
2156
  shownAsOptional(field) && /* @__PURE__ */ jsx("span", { className: "ml-auto font-mono text-[10px] uppercase tracking-wider text-muted-foreground", children: s.optionalBadge })
1355
2157
  ] }),
1356
2158
  field.description && /* @__PURE__ */ jsx("p", { className: "text-[12px] leading-relaxed text-muted-foreground", children: field.description }),
1357
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2159
+ /* @__PURE__ */ jsxs("div", { ref: rowsRef, className: "space-y-2", children: [
1358
2160
  items.length === 0 && /* @__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 }),
1359
2161
  items.map((item, index) => /* @__PURE__ */ jsxs(
1360
2162
  "div",
1361
2163
  {
2164
+ "data-list-row": true,
1362
2165
  className: "flex items-start gap-2 rounded-md border border-border bg-card/40 p-3",
1363
2166
  children: [
1364
2167
  /* @__PURE__ */ jsx("span", { className: "mt-1 w-5 shrink-0 text-center font-mono text-[11px] text-muted-foreground", children: index + 1 }),
@@ -1407,11 +2210,56 @@ function ListField({ field, value, onChange, id, error, env }) {
1407
2210
  s.addItem
1408
2211
  ]
1409
2212
  }
1410
- )
2213
+ ),
2214
+ fileSource && fileItem && /* @__PURE__ */ jsxs(Fragment, { children: [
2215
+ /* @__PURE__ */ jsx(
2216
+ FilePickerEntry,
2217
+ {
2218
+ ref: pickerEntryRef,
2219
+ label: fieldLabel(field.title, field.name, presentation),
2220
+ disabled: env?.disabled || busy || room === 0,
2221
+ onOpen: () => setPickerOpen(true)
2222
+ }
2223
+ ),
2224
+ /* @__PURE__ */ jsx(
2225
+ FilePicker,
2226
+ {
2227
+ source: fileSource,
2228
+ formats: fileItem.formats,
2229
+ multiple: true,
2230
+ maxChoices: room,
2231
+ held: items,
2232
+ open: pickerOpen,
2233
+ onOpenChange: setPickerOpen,
2234
+ disabled: env?.disabled || busy,
2235
+ returnFocusTo: pickerEntryRef,
2236
+ onCloseAutoFocus: (event) => {
2237
+ const landed = landedRow.current;
2238
+ landedRow.current = null;
2239
+ if (landed === null || !pickerEntryRef.current?.disabled) return;
2240
+ const rows = rowsRef.current?.querySelectorAll(":scope > [data-list-row]");
2241
+ const target = firstFocusable(rows?.[landed]);
2242
+ if (!target) return;
2243
+ event.preventDefault();
2244
+ target.focus();
2245
+ },
2246
+ onUse: (files) => {
2247
+ const next = addChosenFiles(items, files);
2248
+ landedRow.current = lastWrittenRow(items, next);
2249
+ onChange(next);
2250
+ }
2251
+ }
2252
+ )
2253
+ ] })
1411
2254
  ] }),
1412
2255
  error && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-destructive", role: "alert", children: error })
1413
2256
  ] });
1414
2257
  }
2258
+ function lastWrittenRow(before, after) {
2259
+ let index = after.length - 1;
2260
+ while (index >= 0 && after[index] === before[index]) index -= 1;
2261
+ return index >= 0 ? index : null;
2262
+ }
1415
2263
  function listIsBusy(id, uploadingIds) {
1416
2264
  if (!uploadingIds?.size) return false;
1417
2265
  const prefix = `${id}.`;
@@ -1581,6 +2429,7 @@ function FieldRenderer({ field, value, onChange, id, error, env }) {
1581
2429
  uploading: env?.uploadingIds?.has(id),
1582
2430
  uploadError: env?.uploadErrors?.get(id),
1583
2431
  resolveUrl: env?.resolveUrl,
2432
+ fileSource: env?.fileSource,
1584
2433
  id,
1585
2434
  error,
1586
2435
  disabled
@@ -1598,6 +2447,7 @@ function FieldRenderer({ field, value, onChange, id, error, env }) {
1598
2447
  uploading: env?.uploadingIds?.has(id),
1599
2448
  uploadError: env?.uploadErrors?.get(id),
1600
2449
  resolveUrl: env?.resolveUrl,
2450
+ fileSource: env?.fileSource,
1601
2451
  id,
1602
2452
  error,
1603
2453
  disabled
@@ -1641,6 +2491,450 @@ function FieldRenderer({ field, value, onChange, id, error, env }) {
1641
2491
  );
1642
2492
  }
1643
2493
  }
2494
+ var AlertDialog = AlertDialogPrimitive.Root;
2495
+ var AlertDialogContent = React5.forwardRef(({ className, returnFocus, onCloseAutoFocus, ...props }, ref) => /* @__PURE__ */ jsxs(Fragment, { children: [
2496
+ /* @__PURE__ */ jsx(AlertDialogPrimitive.Overlay, { className: "fixed inset-0 z-50 bg-black/50" }),
2497
+ /* @__PURE__ */ jsx(
2498
+ AlertDialogPrimitive.Content,
2499
+ {
2500
+ ref,
2501
+ className: cn(
2502
+ "fixed inset-0 z-50 m-auto grid h-fit w-[calc(100%-2rem)] max-w-sm gap-3 rounded-lg border border-border bg-popover p-4 text-popover-foreground shadow-lg outline-hidden",
2503
+ className
2504
+ ),
2505
+ onCloseAutoFocus: closeAutoFocus(onCloseAutoFocus, returnFocus),
2506
+ ...props
2507
+ }
2508
+ )
2509
+ ] }));
2510
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
2511
+ var AlertDialogTitle = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2512
+ AlertDialogPrimitive.Title,
2513
+ {
2514
+ ref,
2515
+ className: cn("text-[16px] font-semibold text-foreground", className),
2516
+ ...props
2517
+ }
2518
+ ));
2519
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
2520
+ var AlertDialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2521
+ AlertDialogPrimitive.Description,
2522
+ {
2523
+ ref,
2524
+ className: cn("text-[13px] text-muted-foreground", className),
2525
+ ...props
2526
+ }
2527
+ ));
2528
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
2529
+ var AlertDialogCancel = AlertDialogPrimitive.Cancel;
2530
+ var AlertDialogAction = AlertDialogPrimitive.Action;
2531
+ var DropdownMenu = DropdownMenuPrimitive.Root;
2532
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
2533
+ var DropdownMenuContent = React5.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
2534
+ DropdownMenuPrimitive.Content,
2535
+ {
2536
+ ref,
2537
+ sideOffset,
2538
+ className: cn(
2539
+ "z-50 min-w-36 overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
2540
+ className
2541
+ ),
2542
+ ...props
2543
+ }
2544
+ ));
2545
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
2546
+ var DropdownMenuItem = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
2547
+ DropdownMenuPrimitive.Item,
2548
+ {
2549
+ ref,
2550
+ className: cn(
2551
+ "flex min-h-11 cursor-default select-none items-center gap-2 rounded-sm px-3 text-[14px] outline-hidden data-highlighted:bg-accent data-highlighted:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 sm:min-h-9",
2552
+ className
2553
+ ),
2554
+ ...props
2555
+ }
2556
+ ));
2557
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
2558
+ var buttonClass = "inline-flex min-h-11 items-center justify-center gap-2 rounded-md px-4 text-[14px] transition-colors disabled:cursor-not-allowed disabled:opacity-50 sm:min-h-9";
2559
+ var secondaryClass = cn(buttonClass, "border border-border text-foreground hover:bg-muted");
2560
+ var primaryClass = cn(
2561
+ buttonClass,
2562
+ "bg-primary font-medium text-primary-foreground hover:bg-primary/90"
2563
+ );
2564
+ var dangerClass = cn(
2565
+ buttonClass,
2566
+ "bg-destructive font-medium text-destructive-foreground hover:bg-destructive/90"
2567
+ );
2568
+ function FillWithExample({
2569
+ source,
2570
+ fields,
2571
+ touched,
2572
+ onFill,
2573
+ disabled,
2574
+ className
2575
+ }) {
2576
+ const s = useFieldStrings();
2577
+ const [open, setOpen] = useState(false);
2578
+ const [opening, setOpening] = useState(0);
2579
+ const [closing, setClosing] = useState(0);
2580
+ const [replacing, setReplacing] = useState(null);
2581
+ const [renaming, setRenaming] = useState(null);
2582
+ const [deleting, setDeleting] = useState(null);
2583
+ const control = useRef(null);
2584
+ const closeButton = useRef(null);
2585
+ const sheet = useRef(null);
2586
+ const afterControl = useRef(null);
2587
+ const rows = useRef(/* @__PURE__ */ new Map());
2588
+ const menus = useRef(/* @__PURE__ */ new Map());
2589
+ const afterRowDialog = useRef(null);
2590
+ const register = (map, id) => (element) => {
2591
+ map.set(id, element);
2592
+ return () => {
2593
+ if (map.get(id) === element) map.delete(id);
2594
+ };
2595
+ };
2596
+ const load = (cursor, signal) => source.list({ cursor, signal }).then((page) => ({ items: page.examples, nextCursor: page.nextCursor }));
2597
+ const probe = useSourcePages(load, source.hideWhenEmpty ? `probe-${closing}` : null);
2598
+ const pages = useSourcePages(load, open ? `open-${opening}` : null);
2599
+ const answer = probe.status === "failed" || probe.more === "failed" ? true : probe.items.length > 0 ? true : probe.empty ? false : null;
2600
+ const [hasExamples, setHasExamples] = useState(null);
2601
+ if (answer !== null && answer !== hasExamples) setHasExamples(answer);
2602
+ if (disabled && (open || replacing || renaming || deleting)) {
2603
+ if (open) setClosing((count) => count + 1);
2604
+ setOpen(false);
2605
+ setReplacing(null);
2606
+ setRenaming(null);
2607
+ setDeleting(null);
2608
+ }
2609
+ if (source.hideWhenEmpty && !open && (answer ?? hasExamples) !== true) return null;
2610
+ const closeList = () => {
2611
+ if (source.hideWhenEmpty && pages.status === "ready") {
2612
+ if (pages.items.length > 0) setHasExamples(true);
2613
+ else if (pages.empty) {
2614
+ afterControl.current = followingFocusable(control.current, sheet.current);
2615
+ setHasExamples(false);
2616
+ }
2617
+ }
2618
+ setOpen(false);
2619
+ setClosing((count) => count + 1);
2620
+ };
2621
+ const fill = (example) => {
2622
+ if (disabled) return;
2623
+ onFill(runValuesFromInputs(example.inputs, fields), example);
2624
+ setReplacing(null);
2625
+ closeList();
2626
+ };
2627
+ const choose = (example) => {
2628
+ if (touched) setReplacing(example);
2629
+ else fill(example);
2630
+ };
2631
+ const manages = source.rename !== void 0 || source.delete !== void 0;
2632
+ const status = pages.status === "idle" || pages.status === "loading" ? s.examplesLoading : pages.status === "failed" ? s.examplesFailed : pages.more === "failed" ? s.loadMoreFailed : pages.items.length > 0 ? s.examplesShown(pages.items.length) : pages.empty ? s.examplesNone : s.examplesLoading;
2633
+ const returnFromRowDialog = () => {
2634
+ const next = afterRowDialog.current;
2635
+ afterRowDialog.current = null;
2636
+ if (!next) return null;
2637
+ const target = next.id === void 0 ? void 0 : (next.to === "menu" ? menus : rows).current.get(next.id);
2638
+ return target?.isConnected ? target : closeButton.current;
2639
+ };
2640
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
2641
+ /* @__PURE__ */ jsxs(
2642
+ "button",
2643
+ {
2644
+ ref: control,
2645
+ type: "button",
2646
+ disabled,
2647
+ "aria-haspopup": "dialog",
2648
+ onClick: () => {
2649
+ setOpening((count) => count + 1);
2650
+ setOpen(true);
2651
+ },
2652
+ className: cn(secondaryClass, className),
2653
+ children: [
2654
+ /* @__PURE__ */ jsx(Sparkles, { className: "size-4 text-muted-foreground", "aria-hidden": "true" }),
2655
+ s.fillWithExample
2656
+ ]
2657
+ }
2658
+ ),
2659
+ /* @__PURE__ */ jsx(
2660
+ Dialog,
2661
+ {
2662
+ open,
2663
+ onOpenChange: (next) => {
2664
+ if (next) setOpen(true);
2665
+ else closeList();
2666
+ },
2667
+ children: open && /* @__PURE__ */ jsxs(
2668
+ DialogContent,
2669
+ {
2670
+ layout: "sheet",
2671
+ className: "top-auto max-h-[calc(100dvh-1.5rem)]",
2672
+ ref: sheet,
2673
+ returnFocus: () => control.current ?? afterControl.current,
2674
+ onClick: (event) => event.stopPropagation(),
2675
+ children: [
2676
+ /* @__PURE__ */ jsx("div", { className: "px-4 pt-4 pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
2677
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1 pt-1", children: [
2678
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-[20px]", children: s.examplesTitle }),
2679
+ /* @__PURE__ */ jsx(DialogDescription, { children: s.examplesHint })
2680
+ ] }),
2681
+ /* @__PURE__ */ jsx(
2682
+ DialogClose,
2683
+ {
2684
+ ref: closeButton,
2685
+ "aria-label": s.close,
2686
+ className: "flex size-11 shrink-0 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
2687
+ children: /* @__PURE__ */ jsx(X, { className: "size-5", "aria-hidden": "true" })
2688
+ }
2689
+ )
2690
+ ] }) }),
2691
+ /* @__PURE__ */ jsx("p", { role: "status", className: "sr-only", children: status }),
2692
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto border-t border-border pb-[env(safe-area-inset-bottom)]", children: pages.status === "idle" || pages.status === "loading" ? /* @__PURE__ */ jsx(DimRows, { count: 3 }) : pages.status === "failed" ? /* @__PURE__ */ jsx(ListMessage, { text: s.examplesFailed, children: /* @__PURE__ */ jsx(ListAction, { onClick: pages.retry, children: s.tryAgain }) }) : pages.empty ? /* @__PURE__ */ jsx(ListMessage, { text: s.examplesNone }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2693
+ pages.items.length > 0 && /* @__PURE__ */ jsx("ul", { className: "divide-y divide-border", children: pages.items.map((example) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1 pr-2", children: [
2694
+ /* @__PURE__ */ jsx(
2695
+ "button",
2696
+ {
2697
+ ref: register(rows.current, example.id),
2698
+ type: "button",
2699
+ onClick: () => choose(example),
2700
+ className: "flex min-h-16 min-w-0 flex-1 items-center px-4 py-2.5 text-left transition-colors hover:bg-muted/60 focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-ring",
2701
+ children: /* @__PURE__ */ jsx("span", { className: "truncate text-[16px] font-medium text-foreground", children: example.name })
2702
+ }
2703
+ ),
2704
+ manages && // Not modal, so the question a menu item opens takes
2705
+ // the focus without the menu fighting it for the page.
2706
+ /* @__PURE__ */ jsxs(DropdownMenu, { modal: false, children: [
2707
+ /* @__PURE__ */ jsx(
2708
+ DropdownMenuTrigger,
2709
+ {
2710
+ ref: register(menus.current, example.id),
2711
+ "aria-label": s.exampleActions(example.name),
2712
+ className: "flex size-11 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
2713
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "size-5", "aria-hidden": "true" })
2714
+ }
2715
+ ),
2716
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
2717
+ source.rename && /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: () => setRenaming(example), children: s.renameExample }),
2718
+ source.delete && /* @__PURE__ */ jsx(
2719
+ DropdownMenuItem,
2720
+ {
2721
+ onSelect: () => setDeleting(example),
2722
+ className: "text-destructive data-highlighted:text-destructive",
2723
+ children: s.deleteExample
2724
+ }
2725
+ )
2726
+ ] })
2727
+ ] })
2728
+ ] }, example.id)) }),
2729
+ /* @__PURE__ */ jsx(MoreFooter, { pages })
2730
+ ] }) })
2731
+ ]
2732
+ }
2733
+ )
2734
+ }
2735
+ ),
2736
+ /* @__PURE__ */ jsx(
2737
+ AlertDialog,
2738
+ {
2739
+ open: replacing !== null,
2740
+ onOpenChange: (next) => {
2741
+ if (!next) setReplacing(null);
2742
+ },
2743
+ children: replacing && /* @__PURE__ */ jsxs(
2744
+ AlertDialogContent,
2745
+ {
2746
+ returnFocus: () => {
2747
+ const row = rows.current.get(replacing.id);
2748
+ return row?.isConnected ? row : control.current;
2749
+ },
2750
+ onClick: (event) => event.stopPropagation(),
2751
+ children: [
2752
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: s.replaceEnteredTitle }),
2753
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: s.replaceEnteredDescription(replacing.name) }),
2754
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
2755
+ /* @__PURE__ */ jsx(AlertDialogCancel, { className: secondaryClass, children: s.cancel }),
2756
+ /* @__PURE__ */ jsx(AlertDialogAction, { className: primaryClass, onClick: () => fill(replacing), children: s.replaceEnteredConfirm })
2757
+ ] })
2758
+ ]
2759
+ }
2760
+ )
2761
+ }
2762
+ ),
2763
+ source.rename && renaming && /* @__PURE__ */ jsx(
2764
+ RenameExampleDialog,
2765
+ {
2766
+ example: renaming,
2767
+ rename: source.rename,
2768
+ returnFocus: returnFromRowDialog,
2769
+ onDone: (renamed) => {
2770
+ pages.updateItems(
2771
+ (items) => items.map((item) => item.id === renaming.id ? renamed : item)
2772
+ );
2773
+ afterRowDialog.current = { id: renamed.id, to: "menu" };
2774
+ setRenaming(null);
2775
+ },
2776
+ onCancel: () => {
2777
+ afterRowDialog.current = { id: renaming.id, to: "menu" };
2778
+ setRenaming(null);
2779
+ }
2780
+ }
2781
+ ),
2782
+ source.delete && deleting && /* @__PURE__ */ jsx(
2783
+ DeleteExampleDialog,
2784
+ {
2785
+ example: deleting,
2786
+ remove: source.delete,
2787
+ returnFocus: returnFromRowDialog,
2788
+ onDone: (deleted) => {
2789
+ const index = pages.items.findIndex((item) => item.id === deleted.id);
2790
+ const neighbour = pages.items[index + 1] ?? pages.items[index - 1];
2791
+ pages.updateItems((items) => items.filter((item) => item.id !== deleted.id));
2792
+ afterRowDialog.current = { id: neighbour?.id, to: "row" };
2793
+ setDeleting(null);
2794
+ },
2795
+ onCancel: () => {
2796
+ afterRowDialog.current = { id: deleting.id, to: "menu" };
2797
+ setDeleting(null);
2798
+ }
2799
+ }
2800
+ )
2801
+ ] });
2802
+ }
2803
+ function RenameExampleDialog({
2804
+ example,
2805
+ rename,
2806
+ returnFocus,
2807
+ onDone,
2808
+ onCancel
2809
+ }) {
2810
+ const s = useFieldStrings();
2811
+ const fieldId = useId();
2812
+ const [name, setName] = useState(example.name);
2813
+ const [state, setState] = useState("idle");
2814
+ const trimmed = name.trim();
2815
+ const save = () => {
2816
+ if (trimmed === "" || state === "busy") return;
2817
+ setState("busy");
2818
+ let renamed;
2819
+ try {
2820
+ renamed = Promise.resolve(rename(example, trimmed));
2821
+ } catch (error) {
2822
+ renamed = Promise.reject(error);
2823
+ }
2824
+ renamed.then(
2825
+ (stored) => onDone(stored ?? { ...example, name: trimmed }),
2826
+ () => setState("failed")
2827
+ );
2828
+ };
2829
+ const onKeyDown = (event) => {
2830
+ if (event.key !== "Enter") return;
2831
+ event.preventDefault();
2832
+ save();
2833
+ };
2834
+ return /* @__PURE__ */ jsx(
2835
+ Dialog,
2836
+ {
2837
+ open: true,
2838
+ onOpenChange: (next) => {
2839
+ if (!next && state !== "busy") onCancel();
2840
+ },
2841
+ children: /* @__PURE__ */ jsxs(DialogContent, { returnFocus, onClick: (event) => event.stopPropagation(), children: [
2842
+ /* @__PURE__ */ jsx(DialogTitle, { children: s.renameExampleTitle }),
2843
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: example.name }),
2844
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
2845
+ /* @__PURE__ */ jsx("label", { htmlFor: fieldId, className: "text-[13px] font-medium text-foreground", children: s.exampleName }),
2846
+ /* @__PURE__ */ jsx(
2847
+ "input",
2848
+ {
2849
+ id: fieldId,
2850
+ type: "text",
2851
+ value: name,
2852
+ onChange: (event) => setName(event.target.value),
2853
+ onKeyDown,
2854
+ disabled: state === "busy",
2855
+ autoComplete: "off",
2856
+ className: "h-11 w-full rounded-md border border-border bg-input px-3 text-[16px] text-foreground focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-ring"
2857
+ }
2858
+ )
2859
+ ] }),
2860
+ state === "failed" && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[13px] text-destructive", children: s.renameFailed }),
2861
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
2862
+ /* @__PURE__ */ jsx(
2863
+ "button",
2864
+ {
2865
+ type: "button",
2866
+ onClick: onCancel,
2867
+ disabled: state === "busy",
2868
+ className: secondaryClass,
2869
+ children: s.cancel
2870
+ }
2871
+ ),
2872
+ /* @__PURE__ */ jsx(
2873
+ "button",
2874
+ {
2875
+ type: "button",
2876
+ onClick: save,
2877
+ disabled: trimmed === "" || state === "busy",
2878
+ className: primaryClass,
2879
+ children: state === "busy" ? s.renaming : state === "failed" ? s.tryAgain : s.save
2880
+ }
2881
+ )
2882
+ ] })
2883
+ ] })
2884
+ }
2885
+ );
2886
+ }
2887
+ function DeleteExampleDialog({
2888
+ example,
2889
+ remove,
2890
+ returnFocus,
2891
+ onDone,
2892
+ onCancel
2893
+ }) {
2894
+ const s = useFieldStrings();
2895
+ const [state, setState] = useState("idle");
2896
+ const confirm = () => {
2897
+ if (state === "busy") return;
2898
+ setState("busy");
2899
+ let removed;
2900
+ try {
2901
+ removed = Promise.resolve(remove(example));
2902
+ } catch (error) {
2903
+ removed = Promise.reject(error);
2904
+ }
2905
+ removed.then(
2906
+ () => onDone(example),
2907
+ () => setState("failed")
2908
+ );
2909
+ };
2910
+ return /* @__PURE__ */ jsx(
2911
+ AlertDialog,
2912
+ {
2913
+ open: true,
2914
+ onOpenChange: (next) => {
2915
+ if (!next && state !== "busy") onCancel();
2916
+ },
2917
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, { returnFocus, onClick: (event) => event.stopPropagation(), children: [
2918
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: s.deleteExampleTitle(example.name) }),
2919
+ /* @__PURE__ */ jsx(AlertDialogDescription, { children: s.deleteExampleDescription }),
2920
+ state === "failed" && /* @__PURE__ */ jsx("p", { role: "alert", className: "text-[13px] text-destructive", children: s.deleteFailed }),
2921
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap justify-end gap-2", children: [
2922
+ /* @__PURE__ */ jsx(AlertDialogCancel, { disabled: state === "busy", className: secondaryClass, children: s.cancel }),
2923
+ /* @__PURE__ */ jsx(
2924
+ "button",
2925
+ {
2926
+ type: "button",
2927
+ onClick: confirm,
2928
+ disabled: state === "busy",
2929
+ className: dangerClass,
2930
+ children: state === "busy" ? s.deleting : state === "failed" ? s.tryAgain : s.deleteExample
2931
+ }
2932
+ )
2933
+ ] })
2934
+ ] })
2935
+ }
2936
+ );
2937
+ }
1644
2938
  var EMPTY_IDS = /* @__PURE__ */ new Set();
1645
2939
  function withId(ids, id) {
1646
2940
  if (ids.has(id)) return ids;
@@ -1668,7 +2962,9 @@ function useMethodForm({
1668
2962
  prepareFields,
1669
2963
  order,
1670
2964
  initialValues,
1671
- uploadFile
2965
+ uploadFile,
2966
+ fileSource,
2967
+ exampleSource
1672
2968
  }) {
1673
2969
  const orderKey = order ? order.join("\0") : "";
1674
2970
  const fields = useMemo(() => {
@@ -1681,22 +2977,29 @@ function useMethodForm({
1681
2977
  return initialValues ?? seedInputs(fields);
1682
2978
  }, [initialValues, fields]);
1683
2979
  const [edited, setEdited] = useState(null);
2980
+ const [given, setGiven] = useState(null);
1684
2981
  const [uploadingIds, setUploadingIds] = useState(EMPTY_IDS);
1685
2982
  const [failedUploadIds, setFailedUploadIds] = useState(EMPTY_IDS);
1686
2983
  const [attempted, setAttempted] = useState(false);
2984
+ const [resets, setResets] = useState(0);
1687
2985
  const pendingUploads = useRef(/* @__PURE__ */ new Map());
1688
2986
  const uploadCounter = useRef(0);
1689
2987
  const values = edited ?? initial;
1690
2988
  const setValues = useCallback(
1691
- (next) => setEdited((previous) => typeof next === "function" ? next(previous ?? initial) : next),
2989
+ (next) => {
2990
+ setGiven((previous) => previous ?? initial);
2991
+ setEdited((previous) => typeof next === "function" ? next(previous ?? initial) : next);
2992
+ },
1692
2993
  [initial]
1693
2994
  );
1694
2995
  const reset = useCallback((next) => {
1695
2996
  pendingUploads.current.clear();
1696
2997
  setEdited(next ?? null);
2998
+ setGiven(next ?? null);
1697
2999
  setUploadingIds(EMPTY_IDS);
1698
3000
  setFailedUploadIds(EMPTY_IDS);
1699
3001
  setAttempted(false);
3002
+ setResets((count) => count + 1);
1700
3003
  }, []);
1701
3004
  const dropFile = useMemo(() => {
1702
3005
  if (!uploadFile) return void 0;
@@ -1717,6 +3020,7 @@ function useMethodForm({
1717
3020
  (stored) => {
1718
3021
  if (!current()) return;
1719
3022
  const value = typeof stored === "string" ? { url: stored, filename: file.name } : { url: stored.url, filename: stored.filename ?? file.name };
3023
+ setGiven((previous) => previous ?? initial);
1720
3024
  setEdited((previous) => setValueAtPath(previous ?? initial, path, value));
1721
3025
  },
1722
3026
  () => {
@@ -1736,6 +3040,10 @@ function useMethodForm({
1736
3040
  );
1737
3041
  const uploading = uploadingIds.size > 0;
1738
3042
  const ready = readiness.ready === readiness.total && !uploading;
3043
+ const touched = useMemo(
3044
+ () => uploading || edited !== null && given !== null && !sameRunValues(edited, given, fields),
3045
+ [uploading, edited, given, fields]
3046
+ );
1739
3047
  const attempt = useCallback(() => {
1740
3048
  setAttempted(true);
1741
3049
  return ready;
@@ -1748,12 +3056,16 @@ function useMethodForm({
1748
3056
  uploading,
1749
3057
  ready,
1750
3058
  attempted,
3059
+ touched,
1751
3060
  setValues,
1752
3061
  reset,
1753
3062
  attempt,
3063
+ resets,
1754
3064
  uploadingIds,
1755
3065
  failedUploadIds,
1756
- dropFile
3066
+ dropFile,
3067
+ fileSource,
3068
+ exampleSource
1757
3069
  };
1758
3070
  }
1759
3071
  function MethodForm({ locale, strings, presentation, ...props }) {
@@ -1777,14 +3089,35 @@ function MethodFormFields({
1777
3089
  }) {
1778
3090
  const s = useFieldStrings();
1779
3091
  const [showOptional, setShowOptional] = useState(false);
1780
- const { fields, values, setValues, attempted, uploadingIds, failedUploadIds, dropFile } = form;
3092
+ const {
3093
+ fields,
3094
+ values,
3095
+ setValues,
3096
+ reset,
3097
+ attempted,
3098
+ touched,
3099
+ resets,
3100
+ uploadingIds,
3101
+ failedUploadIds,
3102
+ dropFile,
3103
+ fileSource,
3104
+ exampleSource
3105
+ } = form;
1781
3106
  const uploadErrors = useMemo(
1782
3107
  () => new Map([...failedUploadIds].map((id) => [id, s.uploadFailed])),
1783
3108
  [failedUploadIds, s.uploadFailed]
1784
3109
  );
1785
3110
  const env = useMemo(
1786
- () => ({ disabled, onDropFile: dropFile, uploadingIds, uploadErrors, allowUrl, resolveUrl }),
1787
- [disabled, dropFile, uploadingIds, uploadErrors, allowUrl, resolveUrl]
3111
+ () => ({
3112
+ disabled,
3113
+ onDropFile: dropFile,
3114
+ uploadingIds,
3115
+ uploadErrors,
3116
+ allowUrl,
3117
+ resolveUrl,
3118
+ fileSource
3119
+ }),
3120
+ [disabled, dropFile, uploadingIds, uploadErrors, allowUrl, resolveUrl, fileSource]
1788
3121
  );
1789
3122
  const folds = (field) => {
1790
3123
  const value = ownProp(values, field.name);
@@ -1812,6 +3145,16 @@ function MethodFormFields({
1812
3145
  return isFilled(value) && !fieldFilled(field, value) ? s.incompleteField : void 0;
1813
3146
  };
1814
3147
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-6", className), children: [
3148
+ exampleSource && /* @__PURE__ */ jsx(
3149
+ FillWithExample,
3150
+ {
3151
+ source: exampleSource,
3152
+ fields,
3153
+ touched,
3154
+ onFill: (next) => reset(next),
3155
+ disabled
3156
+ }
3157
+ ),
1815
3158
  visible.map((field) => /* @__PURE__ */ jsx(
1816
3159
  FieldRenderer,
1817
3160
  {
@@ -1822,7 +3165,7 @@ function MethodFormFields({
1822
3165
  env,
1823
3166
  onChange: (next) => setValues((previous) => ({ ...previous, [field.name]: next }))
1824
3167
  },
1825
- field.name
3168
+ `${field.name}:${resets}`
1826
3169
  )),
1827
3170
  (folded.length > 0 || structureOpen) && /* @__PURE__ */ jsx(OptionalToggle, { count: folded.length, expanded, onToggle: toggle, noun: "input" })
1828
3171
  ] });
@@ -2319,7 +3662,7 @@ function ResultAt({ segment, children }) {
2319
3662
  var TooltipProvider = TooltipPrimitive.Provider;
2320
3663
  var TooltipRoot = TooltipPrimitive.Root;
2321
3664
  var TooltipTrigger = TooltipPrimitive.Trigger;
2322
- var TooltipContent = React2.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
3665
+ var TooltipContent = React5.forwardRef(({ className, sideOffset = 6, ...props }, ref) => /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsx(
2323
3666
  TooltipPrimitive.Content,
2324
3667
  {
2325
3668
  ref,
@@ -3301,12 +4644,12 @@ function useResolvedMarkup(markup) {
3301
4644
  }
3302
4645
  var LEX_OPTIONS = { gfm: true, breaks: true };
3303
4646
  function Markdown({ text, className, proseImages }) {
3304
- const tokens = React2.useMemo(() => marked.lexer(text, LEX_OPTIONS), [text]);
4647
+ const tokens = React5.useMemo(() => marked.lexer(text, LEX_OPTIONS), [text]);
3305
4648
  const fromEnv = useProseImages();
3306
4649
  const mode = proseImages ?? fromEnv;
3307
4650
  return /* @__PURE__ */ jsx(ProseImagesContext, { value: mode, children: /* @__PURE__ */ jsx("div", { className: cn("space-y-2 text-[13px] leading-relaxed text-foreground", className), children: /* @__PURE__ */ jsx(Blocks, { tokens }) }) });
3308
4651
  }
3309
- var ProseImagesContext = React2.createContext("link");
4652
+ var ProseImagesContext = React5.createContext("link");
3310
4653
  var HEADING_CLASS = {
3311
4654
  1: "text-[16px] font-semibold",
3312
4655
  2: "text-[14.5px] font-semibold",
@@ -3463,8 +4806,8 @@ function InlineToken({ token }) {
3463
4806
  }
3464
4807
  }
3465
4808
  function ProseImage({ token }) {
3466
- const mode = React2.use(ProseImagesContext);
3467
- const insideLink = React2.use(InsideLinkContext);
4809
+ const mode = React5.use(ProseImagesContext);
4810
+ const insideLink = React5.use(InsideLinkContext);
3468
4811
  const src = proseImageUrl(token.href);
3469
4812
  if (!src) return /* @__PURE__ */ jsx(Fragment, { children: token.text });
3470
4813
  if (mode === "load") {
@@ -3490,7 +4833,7 @@ function ProseImage({ token }) {
3490
4833
  }
3491
4834
  );
3492
4835
  }
3493
- var InsideLinkContext = React2.createContext(false);
4836
+ var InsideLinkContext = React5.createContext(false);
3494
4837
  function proseImageUrl(href) {
3495
4838
  const url = viewableUrl(href);
3496
4839
  return url && /^https?:/.test(url) ? url : void 0;
@@ -4854,6 +6197,6 @@ function pdfFileName(field, value, baseName) {
4854
6197
  return name === void 0 ? "" : name.replace(/\.[^.]+$/, "");
4855
6198
  }
4856
6199
 
4857
- export { BooleanField, ConceptPill, DEFAULT_FIELD_STRINGS, DateField, DocumentField, EN_FIELD_STRINGS, EnumField, FIELD_STRINGS_BY_LOCALE, FR_FIELD_STRINGS, FieldDomIdProvider, FieldPresentationProvider, FieldRenderer, FieldShell, FieldStringsProvider, 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, cn, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
4858
- //# sourceMappingURL=chunk-QCGIH3WY.js.map
4859
- //# sourceMappingURL=chunk-QCGIH3WY.js.map
6200
+ 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, cn, fieldControlClass, fieldStringsFor, humanizeFieldName, renderHtmlPdf, resolveHtmlStorageUrls, saveInBrowser, useFieldDomId, useFieldPresentation, useFieldStrings, useMethodForm, useProseImages, useResolveShareUrl, useResolveUrl, useResolvedUrl };
6201
+ //# sourceMappingURL=chunk-CSLDIWB6.js.map
6202
+ //# sourceMappingURL=chunk-CSLDIWB6.js.map