@ai-matrx/design-system 0.17.3 → 0.18.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.
Files changed (54) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +1 -0
  3. package/dist/content-transfer/alchemy.svg +1 -0
  4. package/dist/content-transfer/icon.cjs +28 -0
  5. package/dist/content-transfer/icon.cjs.map +1 -0
  6. package/dist/content-transfer/icon.d.cts +4 -0
  7. package/dist/content-transfer/icon.d.ts +4 -0
  8. package/dist/content-transfer/icon.js +8 -0
  9. package/dist/content-transfer/icon.js.map +1 -0
  10. package/dist/content-transfer.cjs +1978 -0
  11. package/dist/content-transfer.cjs.map +1 -0
  12. package/dist/content-transfer.css +382 -0
  13. package/dist/content-transfer.d.cts +208 -0
  14. package/dist/content-transfer.d.ts +208 -0
  15. package/dist/content-transfer.js +1974 -0
  16. package/dist/content-transfer.js.map +1 -0
  17. package/dist/data-table/copy-helpers.d.cts +1 -1
  18. package/dist/data-table/copy-helpers.d.ts +1 -1
  19. package/dist/data-table/copy-types.cjs.map +1 -1
  20. package/dist/data-table/copy-types.d.cts +2 -0
  21. package/dist/data-table/copy-types.d.ts +2 -0
  22. package/dist/data-table/data-row-inspector.cjs +1603 -319
  23. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  24. package/dist/data-table/data-row-inspector.js +1634 -324
  25. package/dist/data-table/data-row-inspector.js.map +1 -1
  26. package/dist/data-table/data-row-window.cjs +1531 -245
  27. package/dist/data-table/data-row-window.cjs.map +1 -1
  28. package/dist/data-table/data-row-window.js +1561 -249
  29. package/dist/data-table/data-row-window.js.map +1 -1
  30. package/dist/data-table/filter-engine.d.cts +1 -1
  31. package/dist/data-table/filter-engine.d.ts +1 -1
  32. package/dist/data-table/index.cjs +2558 -1146
  33. package/dist/data-table/index.cjs.map +1 -1
  34. package/dist/data-table/index.d.cts +2 -2
  35. package/dist/data-table/index.d.ts +2 -2
  36. package/dist/data-table/index.js +2589 -1151
  37. package/dist/data-table/index.js.map +1 -1
  38. package/dist/data-table/infer-filter.d.cts +1 -1
  39. package/dist/data-table/infer-filter.d.ts +1 -1
  40. package/dist/data-table/layered-filters.d.cts +1 -1
  41. package/dist/data-table/layered-filters.d.ts +1 -1
  42. package/dist/data-table/query-control.d.cts +1 -1
  43. package/dist/data-table/query-control.d.ts +1 -1
  44. package/dist/data-table/types.cjs.map +1 -1
  45. package/dist/data-table/types.d.cts +1 -1
  46. package/dist/data-table/types.d.ts +1 -1
  47. package/dist/data-table/url-state.d.cts +1 -1
  48. package/dist/data-table/url-state.d.ts +1 -1
  49. package/dist/data-table/xlsx.d.cts +1 -1
  50. package/dist/data-table/xlsx.d.ts +1 -1
  51. package/dist/styles.css +2 -0
  52. package/dist/{types-pZ95iFk5.d.ts → types-BhSkeZQ_.d.ts} +14 -0
  53. package/dist/{types-4fmLG5AN.d.cts → types-CD4xGL5Z.d.cts} +14 -0
  54. package/package.json +25 -2
@@ -2,7 +2,7 @@
2
2
  "use client";
3
3
 
4
4
  // src/data-table/DataRowWindow.tsx
5
- import { useEffect as useEffect3, useState as useState6 } from "react";
5
+ import { useEffect as useEffect4, useState as useState7 } from "react";
6
6
 
7
7
  // src/dialog.tsx
8
8
  import * as DialogPrimitive from "@radix-ui/react-dialog";
@@ -62,6 +62,48 @@ function XIcon(props) {
62
62
  /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
63
63
  ] });
64
64
  }
65
+ function radixProps(props) {
66
+ return {
67
+ xmlns: "http://www.w3.org/2000/svg",
68
+ viewBox: "0 0 15 15",
69
+ width: 15,
70
+ height: 15,
71
+ fill: "none",
72
+ "aria-hidden": true,
73
+ ...props
74
+ };
75
+ }
76
+ function CheckIcon(props) {
77
+ return /* @__PURE__ */ jsx("svg", { ...radixProps(props), children: /* @__PURE__ */ jsx(
78
+ "path",
79
+ {
80
+ d: "M11.4669 3.72684C11.7558 3.91574 11.8369 4.30308 11.648 4.59198L7.39799 11.092C7.29783 11.2452 7.13556 11.3467 6.95402 11.3699C6.77247 11.3931 6.58989 11.3355 6.45446 11.2124L3.70446 8.71241C3.44905 8.48022 3.43023 8.08494 3.66242 7.82953C3.89461 7.57412 4.28989 7.55529 4.5453 7.78749L6.75292 9.79441L10.6018 3.90792C10.7907 3.61902 11.178 3.53795 11.4669 3.72684Z",
81
+ fill: "currentColor",
82
+ fillRule: "evenodd",
83
+ clipRule: "evenodd"
84
+ }
85
+ ) });
86
+ }
87
+ function DotFilledIcon(props) {
88
+ return /* @__PURE__ */ jsx("svg", { ...radixProps(props), children: /* @__PURE__ */ jsx(
89
+ "path",
90
+ {
91
+ d: "M9.875 7.5C9.875 8.81168 8.81168 9.875 7.5 9.875C6.18832 9.875 5.125 8.81168 5.125 7.5C5.125 6.18832 6.18832 5.125 7.5 5.125C8.81168 5.125 9.875 6.18832 9.875 7.5Z",
92
+ fill: "currentColor"
93
+ }
94
+ ) });
95
+ }
96
+ function RadixChevronRightIcon(props) {
97
+ return /* @__PURE__ */ jsx("svg", { ...radixProps(props), children: /* @__PURE__ */ jsx(
98
+ "path",
99
+ {
100
+ d: "M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z",
101
+ fill: "currentColor",
102
+ fillRule: "evenodd",
103
+ clipRule: "evenodd"
104
+ }
105
+ ) });
106
+ }
65
107
 
66
108
  // src/portal-container.tsx
67
109
  import * as React from "react";
@@ -348,7 +390,6 @@ function Icon({ children, ...props }) {
348
390
  return /* @__PURE__ */ jsx6("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", ...props, children });
349
391
  }
350
392
  var Check = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "m5 12 4 4L19 6" }) });
351
- var SlidersHorizontal = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "M4 7h16M4 17h16M9 7v0M15 17v0" }) });
352
393
  var Pencil = (p) => /* @__PURE__ */ jsx6(Icon, { ...p, children: /* @__PURE__ */ jsx6("path", { d: "m4 20 4-1 11-11-3-3L5 16l-1 4z" }) });
353
394
  var Eye = (p) => /* @__PURE__ */ jsxs4(Icon, { ...p, children: [
354
395
  /* @__PURE__ */ jsx6("path", { d: "M2 12s3-6 10-6 10 6 10 6-3 6-10 6S2 12 2 12z" }),
@@ -376,9 +417,39 @@ function useMatrxDataTableHost() {
376
417
  return useContext4(HostContext);
377
418
  }
378
419
 
379
- // src/data-table/copy.tsx
380
- import { useState as useState4 } from "react";
381
- import { useClipboard } from "@ai-matrx/kit/clipboard";
420
+ // src/content-transfer/matrx-copy.tsx
421
+ import * as React8 from "react";
422
+ import {
423
+ buildAgentPayload,
424
+ createBrowserTransport as createBrowserTransport2,
425
+ directSource as directSource2,
426
+ normalizeTransferJson,
427
+ sealTransferArtifact as sealTransferArtifact2
428
+ } from "@ai-matrx/kit/content-transfer";
429
+
430
+ // src/content-transfer/index.tsx
431
+ import * as React7 from "react";
432
+ import {
433
+ applySectionSelection,
434
+ applyTransferLimits,
435
+ capture,
436
+ collectAllMatching,
437
+ createBrowserTransport,
438
+ createDraft,
439
+ directSource,
440
+ editDraft,
441
+ projectSnapshot,
442
+ readTransferCell,
443
+ reduceJson,
444
+ resetDraft,
445
+ resolveTransferPreferences,
446
+ resolveTransferRegistry,
447
+ sealTransferArtifact,
448
+ serialize,
449
+ serializeMarkdownRich,
450
+ transformRows,
451
+ undoDraft
452
+ } from "@ai-matrx/kit/content-transfer";
382
453
 
383
454
  // src/button.tsx
384
455
  import { Slot } from "@radix-ui/react-slot";
@@ -451,234 +522,1475 @@ var Button = React5.forwardRef(
451
522
  );
452
523
  Button.displayName = "Button";
453
524
 
454
- // src/popover.tsx
455
- import * as PopoverPrimitive from "@radix-ui/react-popover";
525
+ // src/dropdown-menu.tsx
526
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
456
527
  import * as React6 from "react";
457
- import { jsx as jsx9 } from "react/jsx-runtime";
458
- var Popover = PopoverPrimitive.Root;
459
- var PopoverTrigger = PopoverPrimitive.Trigger;
460
- var PopoverContent = React6.forwardRef(
461
- ({
462
- className,
463
- align = "center",
464
- sideOffset = 4,
465
- container,
466
- animated = true,
528
+ import { jsx as jsx9, jsxs as jsxs5 } from "react/jsx-runtime";
529
+ var DropdownMenu = DropdownMenuPrimitive.Root;
530
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
531
+ var MENU_SURFACE_CLASS = "z-[10001] min-w-[8rem] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hidden overscroll-contain rounded-md border bg-popover p-1 text-popover-foreground";
532
+ var MENU_ITEM_CLASS = "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
533
+ var MENU_INDICATOR_ITEM_CLASS = "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
534
+ var DropdownMenuSubTrigger = React6.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs5(
535
+ DropdownMenuPrimitive.SubTrigger,
536
+ {
537
+ ref,
538
+ "data-slot": "dropdown-menu-sub-trigger",
539
+ className: cn(
540
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
541
+ inset && "pl-8",
542
+ className
543
+ ),
544
+ ...props,
545
+ children: [
546
+ children,
547
+ /* @__PURE__ */ jsx9(RadixChevronRightIcon, { className: "ml-auto h-4 w-4" })
548
+ ]
549
+ }
550
+ ));
551
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
552
+ var DropdownMenuSubContent = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
553
+ DropdownMenuPrimitive.SubContent,
554
+ {
555
+ ref,
556
+ "data-slot": "dropdown-menu-sub-content",
557
+ className: cn(MENU_SURFACE_CLASS, "shadow-lg", MOTION_POPPER, className),
558
+ ...props
559
+ }
560
+ ));
561
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
562
+ var DropdownMenuContent = React6.forwardRef(({ className, sideOffset = 4, container, ...props }, ref) => {
563
+ const portalContainer = usePortalContainer(container);
564
+ return /* @__PURE__ */ jsx9(DropdownMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx9(
565
+ DropdownMenuPrimitive.Content,
566
+ {
567
+ ref,
568
+ "data-slot": "dropdown-menu-content",
569
+ sideOffset,
570
+ className: cn(
571
+ MENU_SURFACE_CLASS,
572
+ "shadow-md",
573
+ MOTION_POPPER,
574
+ className
575
+ ),
576
+ ...props
577
+ }
578
+ ) });
579
+ });
580
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
581
+ var DropdownMenuItem = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx9(
582
+ DropdownMenuPrimitive.Item,
583
+ {
584
+ ref,
585
+ "data-slot": "dropdown-menu-item",
586
+ className: cn(MENU_ITEM_CLASS, inset && "pl-8", className),
587
+ ...props
588
+ }
589
+ ));
590
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
591
+ var DropdownMenuCheckboxItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs5(
592
+ DropdownMenuPrimitive.CheckboxItem,
593
+ {
594
+ ref,
595
+ "data-slot": "dropdown-menu-checkbox-item",
596
+ className: cn(MENU_INDICATOR_ITEM_CLASS, className),
597
+ ...props,
598
+ children: [
599
+ /* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(CheckIcon, { className: "h-4 w-4" }) }) }),
600
+ children
601
+ ]
602
+ }
603
+ ));
604
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
605
+ var DropdownMenuRadioItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs5(
606
+ DropdownMenuPrimitive.RadioItem,
607
+ {
608
+ ref,
609
+ "data-slot": "dropdown-menu-radio-item",
610
+ className: cn(MENU_INDICATOR_ITEM_CLASS, className),
611
+ ...props,
612
+ children: [
613
+ /* @__PURE__ */ jsx9("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx9(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx9(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
614
+ children
615
+ ]
616
+ }
617
+ ));
618
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
619
+ var DropdownMenuLabel = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx9(
620
+ DropdownMenuPrimitive.Label,
621
+ {
622
+ ref,
623
+ "data-slot": "dropdown-menu-label",
624
+ className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
625
+ ...props
626
+ }
627
+ ));
628
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
629
+ var DropdownMenuSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
630
+ DropdownMenuPrimitive.Separator,
631
+ {
632
+ ref,
633
+ "data-slot": "dropdown-menu-separator",
634
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
635
+ ...props
636
+ }
637
+ ));
638
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
639
+ var DropdownMenuShortcut = ({
640
+ className,
641
+ ...props
642
+ }) => /* @__PURE__ */ jsx9(
643
+ "span",
644
+ {
645
+ "data-slot": "dropdown-menu-shortcut",
646
+ className: cn("ml-auto text-xs tracking-widest opacity-60", className),
467
647
  ...props
468
- }, ref) => {
469
- const portalContainer = usePortalContainer(container);
470
- return /* @__PURE__ */ jsx9(PopoverPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ jsx9(
471
- PopoverPrimitive.Content,
472
- {
473
- ref,
474
- align,
475
- sideOffset,
476
- className: cn(
477
- // Cap to the viewport space Radix measured and scroll — a popover taller
478
- // than the screen must never trap the user with unreachable content.
479
- // z must EQUAL the dialog layer (z-[10000]), never exceed it: with equal
480
- // z, DOM portal order decides, so a dialog opened FROM a popover stacks
481
- // above it, and a popover opened from a dialog still stacks above the
482
- // dialog. z-[10001] buried dialogs behind fullscreen popovers.
483
- "z-[10000] w-72 max-h-[var(--radix-popover-content-available-height)] overflow-y-auto overscroll-contain rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none",
484
- animated && MOTION_POPPER,
485
- className
486
- ),
487
- ...props
488
- }
489
- ) });
490
648
  }
491
649
  );
492
- PopoverContent.displayName = PopoverPrimitive.Content.displayName;
650
+ DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
493
651
 
494
- // src/data-table/useTableNotice.tsx
495
- import { useState as useState3 } from "react";
496
- import { jsx as jsx10, jsxs as jsxs5 } from "react/jsx-runtime";
497
- function useTableNotice() {
498
- const host = useMatrxDataTableHost();
499
- const [message, setMessage] = useState3(null);
500
- const notify = {
501
- success(text) {
502
- if (host.notify) host.notify.success(text);
503
- else setMessage({ text, error: false });
504
- },
505
- error(text) {
506
- if (host.notify) host.notify.error(text);
507
- else setMessage({ text, error: true });
652
+ // src/content-transfer/index.tsx
653
+ import { Fragment, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
654
+ function AlchemyGlyph(props) {
655
+ return /* @__PURE__ */ jsxs6("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", ...props, children: [
656
+ /* @__PURE__ */ jsx10("path", { d: "M4.5 5.5h8v11h-8z" }),
657
+ /* @__PURE__ */ jsx10("path", { d: "M8.5 9.5h8v9h-8z" }),
658
+ /* @__PURE__ */ jsx10("path", { d: "m18.5 3 .65 1.85L21 5.5l-1.85.65L18.5 8l-.65-1.85L16 5.5l1.85-.65z" })
659
+ ] });
660
+ }
661
+ var CapabilityContext = React7.createContext({});
662
+ var useContentTransferCapabilities = () => React7.useContext(CapabilityContext);
663
+ var SurfaceContext = React7.createContext(null);
664
+ function MenuPresentation({ label, hint, icon: Icon2 }) {
665
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
666
+ Icon2 ? /* @__PURE__ */ jsx10(Icon2, { className: "matrx-alchemy-menu-icon" }) : null,
667
+ /* @__PURE__ */ jsxs6("span", { children: [
668
+ label,
669
+ hint ? /* @__PURE__ */ jsx10("small", { className: "matrx-alchemy-menu-hint", children: hint }) : null
670
+ ] })
671
+ ] });
672
+ }
673
+ var LABEL = { plain: "Plain text", markdown: "Markdown", html: "Formatted", json: "JSON", "compact-json": "Compact JSON", csv: "CSV", tsv: "Google Sheets", xlsx: "Excel workbook" };
674
+ var BUILT_INS = Object.freeze(Object.keys(LABEL).map((id) => ({ id })));
675
+ var COPIES = ["plain", "markdown", "html", "json", "compact-json", "csv", "tsv"];
676
+ var DOWNLOADS = [...COPIES, "xlsx"];
677
+ var EMPTY_VARIANTS = [];
678
+ var SCOPE_LABEL = { target: "This content", row: "This row", selected: "Selected rows", view: "Current view", loaded: "Loaded rows", "all-matching": "All matching rows" };
679
+ var filenameFor = (label) => label.toLocaleLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "") || "export";
680
+ var estimateTokens = (text) => Math.ceil(text.length / 4);
681
+ var errorMessage = (error, fallback) => error instanceof Error ? error.message : fallback;
682
+ var isAbort = (error) => error instanceof DOMException && error.name === "AbortError";
683
+ var supportsBuiltIn = (format, payload) => !["csv", "tsv", "xlsx"].includes(format) || payload.kind === "rows";
684
+ var outcomeMessage = (result) => {
685
+ if (result.status === "success") {
686
+ const success = result;
687
+ return success.delivered === "action" ? success.message ?? "Action completed" : success.delivered === "download-started" ? "Download started" : success.message ?? "Copied";
688
+ }
689
+ return result.status === "degraded" ? `Copied as plain text: ${result.reason}` : result.status === "cancelled" ? "Cancelled" : result.message;
690
+ };
691
+ var materialIds = /* @__PURE__ */ new WeakMap();
692
+ var nextMaterialId = 0;
693
+ function materialIdentity(material) {
694
+ if (!material) return "missing";
695
+ if ("instanceId" in material) return `surface:${material.instanceId}`;
696
+ if ("id" in material) return `source:${material.id}`;
697
+ let id = materialIds.get(material);
698
+ if (id === void 0) {
699
+ id = ++nextMaterialId;
700
+ materialIds.set(material, id);
701
+ }
702
+ return `payload:${id}`;
703
+ }
704
+ function useFence() {
705
+ const mounted = React7.useRef(true);
706
+ const nextId = React7.useRef(0);
707
+ const active = React7.useRef(/* @__PURE__ */ new Set());
708
+ React7.useEffect(() => {
709
+ mounted.current = true;
710
+ return () => {
711
+ mounted.current = false;
712
+ nextId.current += 1;
713
+ active.current.forEach((c) => c.abort());
714
+ active.current.clear();
715
+ };
716
+ }, []);
717
+ const begin = React7.useCallback(() => {
718
+ const controller = new AbortController();
719
+ active.current.add(controller);
720
+ return { controller, id: ++nextId.current };
721
+ }, []);
722
+ const finish = React7.useCallback((controller) => active.current.delete(controller), []);
723
+ const valid = React7.useCallback((id, controller) => mounted.current && !controller.signal.aborted && nextId.current === id, []);
724
+ const abortAll = React7.useCallback(() => {
725
+ nextId.current += 1;
726
+ active.current.forEach((c) => c.abort());
727
+ active.current.clear();
728
+ }, []);
729
+ return { begin, finish, valid, abortAll };
730
+ }
731
+ async function xlsxArtifact(draft, label) {
732
+ if (draft.payload.kind !== "rows") throw new Error("Excel workbook export requires tabular content.");
733
+ const XLSX = await import("xlsx");
734
+ const columns = draft.payload.columns.filter((c) => c.visible && c.exportable !== false && c.classification !== "secret" && c.classification !== "credential");
735
+ const cells = draft.payload.rows.map((row) => columns.map((column) => {
736
+ const value = readTransferCell(row, column);
737
+ return value === null || ["string", "number", "boolean"].includes(typeof value) ? value : value === void 0 ? "" : JSON.stringify(value);
738
+ }));
739
+ const sheet = XLSX.utils.aoa_to_sheet([columns.map((c) => c.label), ...cells]);
740
+ const book = XLSX.utils.book_new();
741
+ XLSX.utils.book_append_sheet(book, sheet, label.replace(/[\\/?*[\]:]/g, " ").trim().slice(0, 31) || "Data");
742
+ const written = XLSX.write(book, { type: "array", bookType: "xlsx", compression: true });
743
+ return sealTransferArtifact({ snapshotId: draft.snapshotId, draftRevision: draft.revision, format: "xlsx", plainText: serialize(draft, "tsv", { spreadsheetSafe: true }).plainText, file: { filename: `${filenameFor(label)}.xlsx`, mime: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", bytes: written instanceof Uint8Array ? written : new Uint8Array(written) }, omissions: [...draft.omissions] });
744
+ }
745
+ async function buildArtifact(draft, format, label) {
746
+ if (!supportsBuiltIn(format, draft.payload)) throw new Error(`${LABEL[format]} is available for tabular content only.`);
747
+ if (format === "xlsx") return xlsxArtifact(draft, label);
748
+ if (format === "html" && draft.payload.kind === "markdown") return serializeMarkdownRich(draft, `${filenameFor(label)}.html`);
749
+ const extension = format === "plain" ? "txt" : format === "compact-json" ? "json" : format;
750
+ return serialize(draft, format, { spreadsheetSafe: format === "csv" || format === "tsv", filename: `${filenameFor(label)}.${extension}` });
751
+ }
752
+ function ContentTransferMenu({ source, surface, label, projection, sections, preferences, table, formatSources, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef }) {
753
+ const triggerRef = React7.useRef(null);
754
+ const inherited = React7.useContext(CapabilityContext);
755
+ const nearestSurface = React7.useContext(SurfaceContext);
756
+ const fence = useFence();
757
+ const [menuCapture, setMenuCapture] = React7.useState(null);
758
+ const [menuStatus, setMenuStatus] = React7.useState("idle");
759
+ const [workspaces, setWorkspaces] = React7.useState({});
760
+ const [workspaceKey, setWorkspaceKey] = React7.useState(null);
761
+ const [workspaceOpen, setWorkspaceOpen] = React7.useState(false);
762
+ const workspaceCaptures = React7.useRef(/* @__PURE__ */ new Map());
763
+ const externalWorkspaceKeys = React7.useRef(/* @__PURE__ */ new Set());
764
+ const [notice, setNotice] = React7.useState(null);
765
+ const scopes = React7.useMemo(() => table?.availableScopes ?? ["target"], [table]);
766
+ const initialScope = table?.initialScope ?? "target";
767
+ if (!scopes.includes(initialScope)) throw new Error("Alchemy initialScope must be included in availableScopes.");
768
+ const primary = surface ?? source ?? nearestSurface;
769
+ const sourceLabel = label ?? (surface?.surfaceName ?? (source && "label" in source ? source.label : nearestSurface?.surfaceName) ?? "content");
770
+ const primaryWorkspaceKey = `primary:${materialIdentity(primary)}`;
771
+ const nearestWorkspaceKey = source && !surface && nearestSurface ? `surface:${materialIdentity(nearestSurface)}` : null;
772
+ const variantWorkspaceKeys = React7.useMemo(() => new Map(variants.map((variant) => [variant.id, `variant:${variant.id}:${materialIdentity(variant.source)}`])), [variants]);
773
+ const transport = local?.transport ?? inherited.transport ?? createBrowserTransport();
774
+ const ai = local?.ai ?? inherited.ai;
775
+ const providerPreferences = React7.useMemo(() => resolveTransferPreferences(inherited.preferences, local?.preferences), [inherited.preferences, local?.preferences]);
776
+ const adapters = React7.useMemo(() => resolveTransferRegistry(BUILT_INS, inherited.formats ?? [], local?.formats ?? []).filter((x) => "build" in x), [inherited.formats, local?.formats]);
777
+ const registeredActions = React7.useMemo(() => resolveTransferRegistry(inherited.actions ?? [], local?.actions ?? [], actions), [actions, inherited.actions, local?.actions]);
778
+ React7.useMemo(() => resolveTransferRegistry(variants), [variants]);
779
+ const report = React7.useCallback((result) => {
780
+ inherited.onOutcome?.(result);
781
+ local?.onOutcome?.(result);
782
+ setNotice(outcomeMessage(result));
783
+ }, [inherited, local]);
784
+ React7.useEffect(() => {
785
+ const valid = /* @__PURE__ */ new Set([primaryWorkspaceKey, ...nearestWorkspaceKey ? [nearestWorkspaceKey] : [], ...variantWorkspaceKeys.values(), ...externalWorkspaceKeys.current]);
786
+ setWorkspaces((current) => Object.fromEntries(Object.entries(current).filter(([key]) => valid.has(key))));
787
+ for (const key of workspaceCaptures.current.keys()) if (!valid.has(key)) workspaceCaptures.current.delete(key);
788
+ if (workspaceKey && !valid.has(workspaceKey)) {
789
+ setWorkspaceKey(null);
790
+ setWorkspaceOpen(false);
508
791
  }
509
- };
510
- const notice = message ? /* @__PURE__ */ jsxs5("span", { role: message.error ? "alert" : "status", className: message.error ? "text-xs text-destructive" : "text-xs text-muted-foreground", children: [
511
- message.text,
512
- /* @__PURE__ */ jsx10("button", { type: "button", "aria-label": "Dismiss notification", className: "ml-2 underline", onClick: () => setMessage(null), children: "Dismiss" })
513
- ] }) : null;
514
- return { notify, notice };
792
+ }, [nearestWorkspaceKey, primaryWorkspaceKey, variantWorkspaceKeys, workspaceKey]);
793
+ const captureOne = React7.useCallback(async (material, scope, signal, applyMenuProjection = false, collectPages = true) => {
794
+ if (!material) throw new Error("Alchemy needs a source or a mounted transfer surface.");
795
+ let sourceMaterial;
796
+ if ("instanceId" in material) {
797
+ if (!material.isMounted()) throw new Error("The requested transfer surface is no longer mounted.");
798
+ const captured = await material.capture(signal);
799
+ sourceMaterial = { id: captured.snapshot.sourceId, label: captured.snapshot.label, capture: async () => captured };
800
+ } else if ("kind" in material) {
801
+ sourceMaterial = applyMenuProjection ? { id: "direct", label: sourceLabel, capture: async () => directSource(material, { label: sourceLabel, ...projection ? { projection } : {}, ...sections ? { sections } : {} }) } : material;
802
+ } else if (applyMenuProjection && (projection || sections)) {
803
+ sourceMaterial = {
804
+ id: material.id,
805
+ label: material.label,
806
+ capture: async (request) => {
807
+ const captured = await material.capture(request);
808
+ return {
809
+ ...captured,
810
+ projection: { rules: [...captured.projection.rules, ...projection?.rules ?? []] },
811
+ snapshot: { ...captured.snapshot, ...sections ? { sections } : {} }
812
+ };
813
+ }
814
+ };
815
+ } else {
816
+ sourceMaterial = material;
817
+ }
818
+ const raw = "kind" in sourceMaterial ? directSource(sourceMaterial) : await sourceMaterial.capture({ scope, signal });
819
+ const constrained = resolveTransferPreferences(providerPreferences, preferences, raw.snapshot.limits);
820
+ const result = await capture({ id: raw.snapshot.sourceId, label: raw.snapshot.label, capture: async () => ({ ...raw, snapshot: { ...raw.snapshot, limits: constrained.limits } }) }, signal, scope);
821
+ if (scope !== "all-matching" || !collectPages) return result;
822
+ if (!result.allMatching) throw new Error("This source cannot provide a stable all-matching export.");
823
+ const collected = await collectAllMatching(result.allMatching, signal);
824
+ if (result.snapshot.payload.kind !== "rows") throw new Error("All-matching export requires tabular content.");
825
+ return { snapshot: projectSnapshot({ ...result.snapshot, payload: { kind: "rows", rows: collected.rows, columns: result.snapshot.payload.columns }, coverage: collected.coverage, omissions: [...collected.omissions] }, { rules: [] }), allMatching: result.allMatching, ...collected.error ? { collectionError: collected.error.message } : {} };
826
+ }, [preferences, projection, providerPreferences, sections, sourceLabel]);
827
+ const capturePrimary = React7.useCallback((scope, signal) => captureOne(primary, scope, signal, true), [captureOne, primary]);
828
+ workspaceCaptures.current.set(primaryWorkspaceKey, capturePrimary);
829
+ if (nearestWorkspaceKey && nearestSurface) workspaceCaptures.current.set(nearestWorkspaceKey, (scope, signal) => captureOne(nearestSurface, scope, signal, true));
830
+ for (const variant of variants) {
831
+ const key = variantWorkspaceKeys.get(variant.id);
832
+ workspaceCaptures.current.set(key, (scope, signal) => captureOne(variant.source, scope, signal, true));
833
+ }
834
+ const preflight = React7.useCallback(async () => {
835
+ const { controller, id } = fence.begin();
836
+ setMenuCapture(null);
837
+ setMenuStatus("loading");
838
+ try {
839
+ const result = await captureOne(primary, initialScope, controller.signal, true, false);
840
+ if (fence.valid(id, controller)) {
841
+ setMenuCapture(result);
842
+ setMenuStatus("idle");
843
+ }
844
+ } catch (error) {
845
+ if (fence.valid(id, controller)) {
846
+ setMenuCapture(null);
847
+ setMenuStatus(errorMessage(error, "Could not inspect content"));
848
+ }
849
+ } finally {
850
+ fence.finish(controller);
851
+ }
852
+ }, [captureOne, fence, initialScope, primary]);
853
+ const transfer = React7.useCallback(async (format, download, alternate) => {
854
+ const { controller, id } = fence.begin();
855
+ try {
856
+ const explicit = alternate?.source ?? (typeof format === "string" && format !== "xlsx" ? formatSources?.[format] : void 0);
857
+ const result = await (explicit ? captureOne(explicit, initialScope, controller.signal, true) : capturePrimary(initialScope, controller.signal));
858
+ if (result.collectionError) throw new Error(`The complete source could not be collected: ${result.collectionError}`);
859
+ if (result.snapshot.coverage.status !== "complete" || result.snapshot.omissions.length > 0) {
860
+ const reason = result.snapshot.coverage.reason ?? `${result.snapshot.omissions.length} omission${result.snapshot.omissions.length === 1 ? "" : "s"}`;
861
+ throw new Error(`Quick transfer is partial (${reason}). Open Prepare for AI to review it before delivery.`);
862
+ }
863
+ const resolved = resolveTransferPreferences(providerPreferences, preferences, result.snapshot.limits);
864
+ const draft = applyTransferLimits(createDraft(result.snapshot), resolved.limits);
865
+ const revision = draft.revision;
866
+ if (draft.omissions.length > result.snapshot.omissions.length) throw new Error("Quick transfer requires content reductions. Open Prepare for AI to review them before delivery.");
867
+ const artifact = typeof format === "string" ? await buildArtifact(draft, format, result.snapshot.label) : await format.build(draft, controller.signal);
868
+ if (artifact.omissions.length > draft.omissions.length) throw new Error(`Quick format requires ${artifact.omissions.length - draft.omissions.length} adjustment${artifact.omissions.length - draft.omissions.length === 1 ? "" : "s"}. Open Prepare for AI to review before delivery.`);
869
+ if (!fence.valid(id, controller) || artifact.snapshotId !== result.snapshot.id || artifact.draftRevision !== revision) return;
870
+ const outcome = await transport[download ? "download" : "copy"](artifact, controller.signal);
871
+ if (fence.valid(id, controller)) report(outcome.status === "success" && alternate?.successMessage ? { ...outcome, message: alternate.successMessage } : outcome);
872
+ } catch (error) {
873
+ if (fence.valid(id, controller)) report({ status: "error", code: "prepare-failed", message: errorMessage(error, "Preparation failed"), retryable: true });
874
+ } finally {
875
+ fence.finish(controller);
876
+ }
877
+ }, [captureOne, capturePrimary, fence, formatSources, initialScope, preferences, providerPreferences, report, transport]);
878
+ const openWorkspace = React7.useCallback(async (key = primaryWorkspaceKey, variant) => {
879
+ const variantMaterial = variant && "kind" in variant.source ? { id: `variant:${key}`, label: variant.label, capture: async () => directSource(variant.source, { label: variant.label }) } : variant?.source;
880
+ const captureWorkspace = variantMaterial ? (scope, signal) => captureOne(variantMaterial, scope, signal, true) : capturePrimary;
881
+ workspaceCaptures.current.set(key, captureWorkspace);
882
+ if (workspaces[key]) {
883
+ setWorkspaceKey(key);
884
+ setWorkspaceOpen(true);
885
+ return;
886
+ }
887
+ const { controller, id } = fence.begin();
888
+ try {
889
+ const result = await captureWorkspace(initialScope, controller.signal);
890
+ if (!fence.valid(id, controller)) return;
891
+ const resolved = resolveTransferPreferences(providerPreferences, preferences, result.snapshot.limits);
892
+ const original = createDraft(result.snapshot);
893
+ const base = { ...result, key, original, draft: original, scope: initialScope, preferences: resolved, ...result.collectionError ? { error: result.collectionError } : {} };
894
+ setWorkspaces((current) => ({ ...current, [key]: { ...base, draft: rebuild(base, initialControls(base)) } }));
895
+ setWorkspaceKey(key);
896
+ setWorkspaceOpen(true);
897
+ } catch (error) {
898
+ if (fence.valid(id, controller)) setNotice(errorMessage(error, "Could not capture content"));
899
+ } finally {
900
+ fence.finish(controller);
901
+ }
902
+ }, [captureOne, capturePrimary, fence, initialScope, preferences, primaryWorkspaceKey, providerPreferences, workspaces]);
903
+ React7.useImperativeHandle(controllerRef, () => ({
904
+ focusTrigger: () => triggerRef.current?.focus(),
905
+ prepare: async (requestedSource, options) => {
906
+ const requestedLabel = options?.label ?? ("label" in requestedSource ? requestedSource.label : "content");
907
+ const requestedKey = `controller:${options?.id ?? "source"}:${materialIdentity(requestedSource)}`;
908
+ externalWorkspaceKeys.current.add(requestedKey);
909
+ await openWorkspace(requestedKey, { source: requestedSource, label: requestedLabel });
910
+ }
911
+ }), [openWorkspace]);
912
+ const runAction = React7.useCallback(async (action) => {
913
+ const { controller, id } = fence.begin();
914
+ try {
915
+ const actionSource = action.source;
916
+ const { snapshot } = actionSource ? await captureOne(actionSource, initialScope, controller.signal, true) : await capturePrimary(initialScope, controller.signal);
917
+ const resolved = resolveTransferPreferences(providerPreferences, preferences, snapshot.limits);
918
+ const draft = applyTransferLimits(createDraft(snapshot), resolved.limits);
919
+ if (!action.supports(snapshot)) throw new Error(`${action.label} is unavailable for this content.`);
920
+ const result = await action.run({ snapshot, draft, signal: controller.signal });
921
+ if (fence.valid(id, controller) && draft.snapshotId === snapshot.id) report(result);
922
+ } catch (error) {
923
+ if (fence.valid(id, controller)) report({ status: "error", code: "action-failed", message: errorMessage(error, "Action failed"), retryable: true });
924
+ } finally {
925
+ fence.finish(controller);
926
+ }
927
+ }, [captureOne, capturePrimary, fence, initialScope, preferences, providerPreferences, report]);
928
+ if (!enabled) return null;
929
+ const payload = menuCapture?.snapshot.payload ?? (source && "kind" in source ? source : void 0);
930
+ const quickVariants = (section) => variants.filter((v) => v.copyLabel && (v.section ?? "ai") === section).map((v) => /* @__PURE__ */ jsx10(DropdownMenuItem, { "aria-label": v.ariaLabel, onSelect: () => void transfer("markdown", false, v), children: /* @__PURE__ */ jsx10(MenuPresentation, { ...v, label: v.copyLabel }) }, `copy:${v.id}`));
931
+ const visibleCopies = COPIES.filter((f) => Boolean(formatSources?.[f]) || !["csv", "tsv"].includes(f) || payload?.kind === "rows");
932
+ const visibleDownloads = DOWNLOADS.filter((f) => f !== "xlsx" && Boolean(formatSources?.[f]) || !["csv", "tsv", "xlsx"].includes(f) || payload?.kind === "rows");
933
+ const visibleAdapters = menuCapture ? adapters.filter((a) => a.supports(menuCapture.snapshot.payload)) : [];
934
+ const visibleActions = menuCapture ? registeredActions.filter((a) => a.supports(menuCapture.snapshot)) : [];
935
+ return /* @__PURE__ */ jsxs6("span", { className: cn("matrx-alchemy", className), onClick: stopPropagation ? (event) => event.stopPropagation() : void 0, children: [
936
+ /* @__PURE__ */ jsxs6(DropdownMenu, { onOpenChange: (open) => {
937
+ if (open) void preflight();
938
+ }, children: [
939
+ /* @__PURE__ */ jsx10(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx10("button", { ref: triggerRef, type: "button", className: "matrx-alchemy-trigger", disabled, "aria-label": `Copy, transform or export ${sourceLabel}`, title: "Alchemy \u2014 copy, transform, export", children: /* @__PURE__ */ jsx10(AlchemyGlyph, {}) }) }),
940
+ /* @__PURE__ */ jsxs6(DropdownMenuContent, { align: "end", className: "matrx-alchemy-menu", children: [
941
+ menuStatus === "loading" ? /* @__PURE__ */ jsx10(DropdownMenuItem, { disabled: true, children: "Inspecting content\u2026" }) : menuStatus !== "idle" ? /* @__PURE__ */ jsx10(DropdownMenuItem, { disabled: true, children: menuStatus }) : null,
942
+ !hide.includes("copy") ? /* @__PURE__ */ jsxs6(Fragment, { children: [
943
+ /* @__PURE__ */ jsx10(DropdownMenuLabel, { children: "Copy" }),
944
+ quickVariants("copy"),
945
+ visibleCopies.map((format) => /* @__PURE__ */ jsx10(DropdownMenuItem, { onSelect: () => void transfer(format, false), children: format === "plain" ? "Copy" : `Copy ${LABEL[format]}` }, format)),
946
+ visibleAdapters.map((adapter) => /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: () => void transfer(adapter, false), children: [
947
+ "Copy ",
948
+ adapter.label
949
+ ] }, adapter.id))
950
+ ] }) : null,
951
+ !hide.includes("prepare") ? /* @__PURE__ */ jsxs6(Fragment, { children: [
952
+ /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}),
953
+ quickVariants("ai"),
954
+ /* @__PURE__ */ jsx10(DropdownMenuItem, { onSelect: () => void openWorkspace(), children: "Prepare for AI\u2026" }),
955
+ nearestWorkspaceKey && nearestSurface ? /* @__PURE__ */ jsx10(DropdownMenuItem, { onSelect: () => void openWorkspace(nearestWorkspaceKey, { source: nearestSurface, label: nearestSurface.surfaceName }), children: "Prepare this surface\u2026" }) : null,
956
+ variants.map((variant) => /* @__PURE__ */ jsx10(DropdownMenuItem, { onSelect: () => void openWorkspace(variantWorkspaceKeys.get(variant.id), variant), children: /* @__PURE__ */ jsx10(MenuPresentation, { ...variant, label: `Prepare ${variant.label}\u2026` }) }, variant.id))
957
+ ] }) : null,
958
+ !hide.includes("download") ? /* @__PURE__ */ jsxs6(Fragment, { children: [
959
+ /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}),
960
+ /* @__PURE__ */ jsx10(DropdownMenuLabel, { children: "Download" }),
961
+ visibleDownloads.map((format) => /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: () => void transfer(format, true), children: [
962
+ "Download ",
963
+ LABEL[format]
964
+ ] }, format)),
965
+ visibleAdapters.map((adapter) => /* @__PURE__ */ jsxs6(DropdownMenuItem, { onSelect: () => void transfer(adapter, true), children: [
966
+ "Download ",
967
+ adapter.label
968
+ ] }, `download:${adapter.id}`))
969
+ ] }) : null,
970
+ visibleActions.length ? /* @__PURE__ */ jsx10(DropdownMenuSeparator, {}) : null,
971
+ visibleActions.map((action) => /* @__PURE__ */ jsx10(DropdownMenuItem, { "aria-label": action.ariaLabel, onSelect: () => void runAction(action), children: /* @__PURE__ */ jsx10(MenuPresentation, { ...action, label: action.label }) }, action.id))
972
+ ] })
973
+ ] }),
974
+ notice ? /* @__PURE__ */ jsx10("span", { className: "matrx-alchemy-notice", role: "status", children: notice }) : null,
975
+ Object.values(workspaces).map((saved) => /* @__PURE__ */ jsx10(PreparationWorkspace, { restoreFocus: () => triggerRef.current?.focus(), open: workspaceOpen && workspaceKey === saved.key, state: saved, scopes, onClose: () => setWorkspaceOpen(false), onChange: (next) => setWorkspaces((current) => ({ ...current, [saved.key]: next })), transport, adapters, captureScope: workspaceCaptures.current.get(saved.key) ?? capturePrimary, ...ai ? { ai } : {}, onOutcome: report }, saved.key))
976
+ ] });
515
977
  }
516
-
517
- // src/data-table/copy-payload.ts
518
- function tableAgentText(value) {
519
- if (typeof value === "string") return value;
520
- const attributes = Object.entries(value.attributes ?? {}).filter(([, entry]) => entry !== null && entry !== void 0 && entry !== "").map(([key, entry]) => ` ${key}="${String(entry)}"`).join("");
521
- const context = Object.entries(value.context ?? {}).filter(([, entry]) => entry !== null && entry !== void 0 && entry !== "").map(([key, entry]) => `<${key}>${String(entry)}</${key}>`).join("\n");
522
- const url = typeof window === "undefined" ? "" : window.location.href;
523
- const route = typeof window === "undefined" ? "" : window.location.pathname;
524
- return `<${value.kind}${attributes}>
525
- <context>
526
- <location>${value.location}</location>
527
- ${url ? `<url>${url}</url>` : ""}
528
- ${route ? `<route>${route}</route>` : ""}
529
- <copied>${value.description}</copied>
530
- <copied-at>${(/* @__PURE__ */ new Date()).toISOString()}</copied-at>
531
- ${context}
532
- </context>
533
- ${value.summary ? `<summary>
534
- ${value.summary}
535
- </summary>
536
- ` : ""}<data format="json">
537
- \`\`\`json
538
- ${JSON.stringify(value.data, null, 2)}
539
- \`\`\`
540
- </data>
541
- </${value.kind}>`;
978
+ function initialControls(state) {
979
+ const rows = state.original.payload.kind === "rows" ? state.original.payload : null;
980
+ return {
981
+ preset: state.preferences.initialPreset,
982
+ sections: state.snapshot.sections.filter((s) => s.includedByDefault).map((s) => s.id),
983
+ search: "",
984
+ filters: {},
985
+ sort: "",
986
+ direction: "asc",
987
+ top: state.preferences.limits.maxRows,
988
+ visibleColumns: rows?.columns.filter((c) => c.visible).map((c) => c.id) ?? [],
989
+ selectedOnly: false,
990
+ selectedIndices: [],
991
+ maxDepth: state.preferences.limits.maxDepth,
992
+ maxStringChars: state.preferences.limits.maxStringChars,
993
+ maxArrayItems: state.preferences.limits.maxArrayItems ?? null
994
+ };
542
995
  }
543
-
544
- // src/data-table/copy.tsx
545
- import { jsx as jsx11, jsxs as jsxs6 } from "react/jsx-runtime";
546
- function resolve(value) {
547
- return typeof value === "function" ? value() : value;
996
+ function presetLimits(preset, controls, preferences) {
997
+ const min = (choice, cap) => choice == null ? cap : cap == null ? choice : Math.min(choice, cap);
998
+ if (preset === "full") return preferences.limits;
999
+ const request = preset === "brief" ? { maxDepth: 4, maxStringChars: 800, maxArrayItems: 25 } : preset === "compact" ? { maxDepth: 8, maxStringChars: 4e3, maxArrayItems: 100 } : { maxDepth: controls.maxDepth, maxStringChars: controls.maxStringChars, maxArrayItems: controls.maxArrayItems };
1000
+ return { ...preferences.limits, maxDepth: min(request.maxDepth, preferences.limits.maxDepth), maxStringChars: min(request.maxStringChars, preferences.limits.maxStringChars), maxArrayItems: min(request.maxArrayItems, preferences.limits.maxArrayItems ?? null) };
548
1001
  }
549
- function download(item) {
550
- if (!item.build) throw new Error(`No export action is available for ${item.label}.`);
551
- const file = item.build();
552
- const url = URL.createObjectURL(new Blob([file.content], { type: file.mime }));
553
- try {
554
- const link = document.createElement("a");
555
- link.href = url;
556
- link.download = `${item.label.replace(/[^a-z0-9]+/gi, "-")}.${file.extension}`;
557
- document.body.append(link);
558
- link.click();
559
- link.remove();
560
- } finally {
561
- URL.revokeObjectURL(url);
1002
+ function rebuild(state, controls) {
1003
+ let draft = resetDraft(state.snapshot);
1004
+ if (state.snapshot.sections.length) draft = applySectionSelection(draft, state.snapshot, controls.sections);
1005
+ if (draft.payload.kind === "rows") draft = transformRows(draft, {
1006
+ search: controls.search,
1007
+ filters: controls.filters,
1008
+ ...controls.sort ? { sort: { column: controls.sort, direction: controls.direction } } : {},
1009
+ ...controls.selectedOnly ? { selectedIndices: controls.selectedIndices } : {},
1010
+ visibleColumns: controls.visibleColumns,
1011
+ structuredProjection: "visible",
1012
+ top: controls.top
1013
+ });
1014
+ const limits = presetLimits(controls.preset, controls, state.preferences);
1015
+ if (draft.payload.kind === "json" || draft.payload.kind === "registered") {
1016
+ draft = reduceJson(draft, { maxDepth: limits.maxDepth, maxStringChars: limits.maxStringChars, maxArrayItems: limits.maxArrayItems ?? null });
562
1017
  }
1018
+ return applyTransferLimits(draft, limits);
563
1019
  }
564
- function CopyButtons(props) {
565
- const { CopyControls } = useMatrxDataTableHost();
566
- if (CopyControls) return /* @__PURE__ */ jsx11(CopyControls, { ...props });
567
- return /* @__PURE__ */ jsx11(DefaultCopyButtons, { ...props });
1020
+ function isBuiltInFormat(format) {
1021
+ return format in LABEL;
568
1022
  }
569
- function DefaultCopyButtons({ human, agent, json, label, size = "icon", appearance = "segmented", disabled = false, stopPropagation = true, className, aiVariants = [], agentVariant, aiCustom, export: exportConfig, hide }) {
570
- const { notify, notice } = useTableNotice();
571
- const clipboard = useClipboard({ notify: (message, kind) => notify[kind](message) });
572
- const [busy, setBusy] = useState4(false);
573
- const [modal, setModal] = useState4(null);
574
- const run = async (operation) => {
575
- if (busy) return;
576
- setBusy(true);
1023
+ function preview(draft, format) {
1024
+ const resolved = isBuiltInFormat(format) ? format : "plain";
1025
+ return serialize(draft, resolved === "xlsx" ? "tsv" : resolved, { spreadsheetSafe: resolved === "csv" || resolved === "tsv" || resolved === "xlsx" }).plainText;
1026
+ }
1027
+ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onChange, transport, ai, adapters, onOutcome, captureScope }) {
1028
+ const fence = useFence();
1029
+ const latest = React7.useRef(state);
1030
+ latest.current = state;
1031
+ const aiSession = React7.useRef(void 0);
1032
+ if (!aiSession.current && ai) aiSession.current = ai.fork?.() ?? ai;
1033
+ const [controls, setControls] = React7.useState(() => initialControls(state));
1034
+ const [controlsHistory, setControlsHistory] = React7.useState([]);
1035
+ const [pendingControls, setPendingControls] = React7.useState(null);
1036
+ const requestedFormat = state.preferences.initialFormat;
1037
+ const [format, setFormat] = React7.useState(() => isBuiltInFormat(requestedFormat) ? supportsBuiltIn(requestedFormat, state.draft.payload) ? requestedFormat : "plain" : adapters.some((adapter) => adapter.id === requestedFormat && adapter.supports(state.draft.payload)) ? requestedFormat : "plain");
1038
+ const [buffer, setBuffer] = React7.useState(() => isBuiltInFormat(format) ? preview(state.draft, format) : "Preparing format preview\u2026");
1039
+ const [bufferError, setBufferError] = React7.useState(null);
1040
+ const [formatError, setFormatError] = React7.useState(null);
1041
+ const [preparedArtifact, setPreparedArtifact] = React7.useState(null);
1042
+ const [instruction, setInstruction] = React7.useState("");
1043
+ const [busy, setBusy] = React7.useState(null);
1044
+ const [aiFailed, setAiFailed] = React7.useState(false);
1045
+ const activeAi = React7.useRef(null);
1046
+ const activeCapture = React7.useRef(null);
1047
+ React7.useEffect(() => {
1048
+ if (bufferError) return;
1049
+ const adapter = adapters.find((candidate) => candidate.id === format);
1050
+ const controller = new AbortController();
1051
+ const snapshotId = state.snapshot.id;
1052
+ const revision = state.draft.revision;
1053
+ setPreparedArtifact(null);
1054
+ setFormatError(null);
1055
+ if (adapter) setBuffer("Preparing format preview\u2026");
1056
+ const artifactPromise = adapter ? adapter.build(state.draft, controller.signal) : buildArtifact(state.draft, isBuiltInFormat(format) ? format : "plain", state.snapshot.label);
1057
+ void artifactPromise.then((artifact) => {
1058
+ if (!controller.signal.aborted && artifact.snapshotId === snapshotId && artifact.draftRevision === revision) {
1059
+ setPreparedArtifact(artifact);
1060
+ setBuffer(artifact.plainText);
1061
+ }
1062
+ }).catch((error) => {
1063
+ if (!controller.signal.aborted) setFormatError(errorMessage(error, "Format preview failed"));
1064
+ });
1065
+ return () => controller.abort();
1066
+ }, [adapters, bufferError, format, state.draft, state.snapshot.id]);
1067
+ const close = React7.useCallback(() => {
1068
+ activeAi.current?.abort();
1069
+ activeCapture.current?.abort();
1070
+ activeAi.current = null;
1071
+ activeCapture.current = null;
1072
+ fence.abortAll();
1073
+ setBusy(null);
1074
+ onClose();
1075
+ }, [fence, onClose]);
1076
+ const changeControls = (next) => {
1077
+ if (state.draft.manuallyEdited) {
1078
+ setPendingControls(next);
1079
+ return;
1080
+ }
1081
+ setControlsHistory((history) => [...history, controls]);
1082
+ setControls(next);
1083
+ onChange({ ...state, draft: rebuild(state, next), error: void 0 });
1084
+ };
1085
+ const applyPendingControls = () => {
1086
+ if (!pendingControls) return;
1087
+ setControlsHistory((history) => [...history, controls]);
1088
+ setControls(pendingControls);
1089
+ setBufferError(null);
1090
+ onChange({ ...state, draft: rebuild(state, pendingControls), error: void 0 });
1091
+ setPendingControls(null);
1092
+ };
1093
+ const editBuffer = (text) => {
1094
+ setBuffer(text);
1095
+ setPreparedArtifact(null);
1096
+ setFormatError(null);
1097
+ const payload = state.draft.payload;
1098
+ if (payload.kind === "json" || payload.kind === "registered") {
1099
+ try {
1100
+ const value = JSON.parse(text);
1101
+ const next = payload.kind === "json" ? { kind: "json", value } : { kind: "registered", format: payload.format, value };
1102
+ setBufferError(null);
1103
+ onChange({ ...state, draft: editDraft(state.draft, next), error: void 0 });
1104
+ } catch {
1105
+ setBufferError("JSON is incomplete or invalid. Fix it before copying or downloading.");
1106
+ }
1107
+ return;
1108
+ }
1109
+ setBufferError(null);
1110
+ const kind = payload.kind === "markdown" || format === "markdown" ? "markdown" : "text";
1111
+ if (payload.kind === "rows" || !isBuiltInFormat(format)) setFormat("plain");
1112
+ onChange({ ...state, draft: editDraft(state.draft, { kind, text }), error: void 0 });
1113
+ };
1114
+ const requestCapture = async (scope) => {
1115
+ activeCapture.current?.abort();
1116
+ const { controller, id } = fence.begin();
1117
+ activeCapture.current = controller;
1118
+ const revision = state.draft.revision;
1119
+ setBusy("capture");
577
1120
  try {
578
- await operation();
1121
+ const result = await captureScope(scope, controller.signal);
1122
+ if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, error: result.collectionError });
579
1123
  } catch (error) {
580
- notify.error(error instanceof Error ? error.message : String(error));
1124
+ if (fence.valid(id, controller)) onChange({ ...latest.current, error: errorMessage(error, "Refresh failed") });
581
1125
  } finally {
582
- setBusy(false);
1126
+ fence.finish(controller);
1127
+ if (activeCapture.current === controller) activeCapture.current = null;
1128
+ if (fence.valid(id, controller)) setBusy(null);
1129
+ }
1130
+ };
1131
+ const resolveCapture = (choice) => {
1132
+ if (!state.pending) return;
1133
+ if (choice === "keep") {
1134
+ const { pending: _pending, ...kept } = state;
1135
+ onChange({ ...kept, error: void 0 });
1136
+ return;
583
1137
  }
1138
+ const pending = state.pending;
1139
+ const preferences = resolveTransferPreferences({}, state.preferences, pending.snapshot.limits);
1140
+ const base = { ...pending, key: state.key, original: createDraft(pending.snapshot), draft: createDraft(pending.snapshot), scope: pending.scope, preferences, ...pending.collectionError ? { error: pending.collectionError } : {} };
1141
+ const nextControls = initialControls(base);
1142
+ setControls(nextControls);
1143
+ setControlsHistory([]);
1144
+ setPendingControls(null);
1145
+ setBufferError(null);
1146
+ onChange({ ...base, draft: rebuild(base, nextControls) });
584
1147
  };
585
- const faithfulAgent = agent === void 0 ? [] : [{
586
- id: agentVariant?.id ?? "copy-for-ai",
587
- label: agentVariant?.label ?? "Copy for AI",
588
- ...agentVariant?.hint === void 0 ? {} : { hint: agentVariant.hint },
589
- section: "ai",
590
- build: () => resolve(agent)
591
- }];
592
- const orderedAgent = agentVariant?.position === "last" ? [...aiVariants, ...faithfulAgent] : [...faithfulAgent, ...aiVariants];
593
- const variants = [
594
- ...json === void 0 ? [] : [{ id: "json", label: "Copy JSON", section: "copy", build: () => JSON.stringify(resolve(json), null, 2) }],
595
- ...orderedAgent
596
- ].filter((variant) => !hide?.includes(variant.section ?? "ai"));
597
- const exports = hide?.includes("export") ? void 0 : exportConfig;
598
- const showCustom = aiCustom && !hide?.includes("ai");
599
- const buttonClass = `${size === "xs" ? "h-8 w-8 lg:h-5 lg:w-8" : ""} ${appearance === "bare" ? "border-0 bg-transparent shadow-none" : ""}`;
600
- return /* @__PURE__ */ jsxs6("span", { className: `inline-flex flex-wrap items-center gap-0.5 ${className ?? ""}`, onClick: stopPropagation ? (event) => event.stopPropagation() : void 0, children: [
601
- human !== void 0 && !hide?.includes("copy") ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "icon", className: buttonClass, disabled: disabled || busy, "aria-label": `Copy ${label}`, onClick: () => void run(() => clipboard.copyText(resolve(human), `${label} copied`)), children: /* @__PURE__ */ jsx11(Copy, { className: "h-3.5 w-3.5" }) }) : null,
602
- variants.length > 0 || showCustom || exports ? /* @__PURE__ */ jsxs6(Popover, { children: [
603
- /* @__PURE__ */ jsx11(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "icon", className: buttonClass, disabled: disabled || busy, "aria-label": `Copy ${label} for AI or export`, children: /* @__PURE__ */ jsx11(SlidersHorizontal, { className: "h-3.5 w-3.5" }) }) }),
604
- /* @__PURE__ */ jsxs6(PopoverContent, { align: "end", className: "w-64 p-1", children: [
605
- variants.map((variant) => /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", disabled: busy, onClick: () => void run(async () => {
606
- if (variant.onSelect) variant.onSelect();
607
- else if (variant.modal) setModal(variant.id);
608
- else if (variant.build) await clipboard.copyText(tableAgentText(await variant.build()), variant.successMessage ?? `${label} copied`);
609
- else throw new Error(`No copy action is available for ${variant.label}.`);
610
- }), children: variant.label }, variant.id)),
611
- showCustom ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", onClick: () => setModal("custom"), children: aiCustom.label ?? "Custom copy\u2026" }) : null,
612
- exports?.items.map((item) => /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", disabled: busy, onClick: () => void run(async () => {
613
- if (item.onSelect) await item.onSelect();
614
- else download(item);
615
- }), children: item.label }, item.id)),
616
- exports?.sheetRows ? /* @__PURE__ */ jsx11(Button, { type: "button", variant: "ghost", size: "sm", className: "w-full justify-start", onClick: () => void run(async () => {
617
- const rows = exports.sheetRows?.();
618
- if (!rows) return;
619
- const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
620
- const value = (input) => String(input ?? "").replace(/[\t\r\n]+/g, " ");
621
- await clipboard.copyText([keys.map(value).join(" "), ...rows.map((row) => keys.map((key) => value(row[key])).join(" "))].join("\n"), "Copied for pasting into Google Sheets");
622
- }), children: "Copy for Google Sheets" }) : null
1148
+ const runAi = async (recover = false) => {
1149
+ const adapter = aiSession.current;
1150
+ if (!adapter || !adapter.supports(state.snapshot) || !recover && !instruction.trim() || recover && !adapter.recover) return;
1151
+ activeAi.current?.abort();
1152
+ const { controller, id } = fence.begin();
1153
+ activeAi.current = controller;
1154
+ const snapshotId = state.snapshot.id;
1155
+ const revision = state.draft.revision;
1156
+ setBusy("ai");
1157
+ setAiFailed(false);
1158
+ try {
1159
+ const next = recover ? await adapter.recover({ snapshot: state.snapshot, draft: state.draft, signal: controller.signal }) : await adapter.run({ snapshot: state.snapshot, draft: state.draft, instruction, signal: controller.signal });
1160
+ const current = latest.current;
1161
+ if (!fence.valid(id, controller) || current.snapshot.id !== snapshotId || current.draft.revision !== revision || next.snapshotId !== snapshotId || next.sourceId !== state.draft.sourceId || next.sourceRevision !== state.draft.sourceRevision || next.revision <= revision) return;
1162
+ try {
1163
+ serialize(next, "plain");
1164
+ } catch {
1165
+ onChange({ ...current, error: "AI returned an invalid transfer draft." });
1166
+ setAiFailed(true);
1167
+ return;
1168
+ }
1169
+ onChange({ ...current, draft: next, error: void 0 });
1170
+ setBufferError(null);
1171
+ } catch (error) {
1172
+ if (!isAbort(error) && fence.valid(id, controller)) {
1173
+ setAiFailed(true);
1174
+ onChange({ ...latest.current, error: errorMessage(error, "AI preparation failed") });
1175
+ }
1176
+ } finally {
1177
+ fence.finish(controller);
1178
+ if (activeAi.current === controller) activeAi.current = null;
1179
+ if (fence.valid(id, controller)) setBusy(null);
1180
+ }
1181
+ };
1182
+ const cancelAi = async () => {
1183
+ const aiController = activeAi.current;
1184
+ const { controller, id } = fence.begin();
1185
+ try {
1186
+ await aiSession.current?.cancel?.();
1187
+ } catch (error) {
1188
+ if (fence.valid(id, controller)) onChange({ ...latest.current, error: errorMessage(error, "AI cancellation failed") });
1189
+ } finally {
1190
+ aiController?.abort();
1191
+ fence.finish(controller);
1192
+ if (activeAi.current === aiController) activeAi.current = null;
1193
+ if (fence.valid(id, controller)) setBusy(null);
1194
+ }
1195
+ };
1196
+ const [deliveryNotice, setDeliveryNotice] = React7.useState(null);
1197
+ React7.useEffect(() => {
1198
+ setDeliveryNotice(null);
1199
+ }, [state.draft.revision]);
1200
+ const reportDelivery = (outcome) => {
1201
+ setDeliveryNotice(outcomeMessage(outcome));
1202
+ onOutcome(outcome);
1203
+ };
1204
+ const deliver = async (download) => {
1205
+ if (bufferError) return;
1206
+ const { controller, id } = fence.begin();
1207
+ const snapshotId = state.snapshot.id;
1208
+ const revision = state.draft.revision;
1209
+ setBusy(download ? "download" : "copy");
1210
+ try {
1211
+ if (!preparedArtifact) throw new Error("Wait for the selected format preview before delivery.");
1212
+ const artifact = preparedArtifact;
1213
+ const current = latest.current;
1214
+ if (!fence.valid(id, controller) || current.snapshot.id !== snapshotId || current.draft.revision !== revision || artifact.snapshotId !== snapshotId || artifact.draftRevision !== revision) return;
1215
+ const result = await transport[download ? "download" : "copy"](artifact, controller.signal);
1216
+ if (fence.valid(id, controller)) reportDelivery(result);
1217
+ } catch (error) {
1218
+ if (fence.valid(id, controller)) reportDelivery({ status: "error", code: "prepare-failed", message: errorMessage(error, "Preparation failed"), retryable: true });
1219
+ } finally {
1220
+ fence.finish(controller);
1221
+ if (fence.valid(id, controller)) setBusy(null);
1222
+ }
1223
+ };
1224
+ const rows = state.original.payload.kind === "rows" ? state.original.payload : null;
1225
+ const formats = COPIES.filter((f) => supportsBuiltIn(f, state.draft.payload));
1226
+ const workspaceAdapters = adapters.filter((adapter) => adapter.supports(state.draft.payload));
1227
+ const displayedOmissions = preparedArtifact?.omissions ?? state.draft.omissions;
1228
+ const limitText = Object.entries(state.preferences.limits).filter(([, value]) => value != null).map(([key, value]) => `${key.replace(/^max/, "max ")} ${Number(value).toLocaleString()}`).join(" \xB7 ");
1229
+ return /* @__PURE__ */ jsx10(Dialog, { open, onOpenChange: (next) => {
1230
+ if (!next) close();
1231
+ }, children: /* @__PURE__ */ jsxs6(DialogContent, { onCloseAutoFocus: (event) => {
1232
+ event.preventDefault();
1233
+ restoreFocus();
1234
+ }, className: "matrx-alchemy-workspace max-w-5xl", "aria-busy": busy !== null, children: [
1235
+ /* @__PURE__ */ jsxs6(DialogHeader, { children: [
1236
+ /* @__PURE__ */ jsxs6(DialogTitle, { children: [
1237
+ "Prepare ",
1238
+ state.snapshot.label
1239
+ ] }),
1240
+ /* @__PURE__ */ jsxs6(DialogDescription, { children: [
1241
+ "Captured ",
1242
+ state.snapshot.unsavedChanges ? "from your unsaved draft" : "as an immutable snapshot",
1243
+ ". Your source is never edited."
623
1244
  ] })
1245
+ ] }),
1246
+ /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-workspace-grid", children: [
1247
+ /* @__PURE__ */ jsxs6("section", { className: "matrx-alchemy-main", children: [
1248
+ /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-toolbar", children: [
1249
+ scopes.length > 1 ? /* @__PURE__ */ jsxs6("label", { children: [
1250
+ "Scope",
1251
+ /* @__PURE__ */ jsx10("select", { "aria-label": "Transfer scope", value: state.scope, disabled: busy !== null, onChange: (e) => void requestCapture(e.target.value), children: scopes.map((scope) => /* @__PURE__ */ jsx10("option", { value: scope, children: SCOPE_LABEL[scope] }, scope)) })
1252
+ ] }) : null,
1253
+ /* @__PURE__ */ jsxs6("label", { children: [
1254
+ "Detail",
1255
+ /* @__PURE__ */ jsxs6("select", { "aria-label": "Detail preset", value: controls.preset, onChange: (e) => changeControls({ ...controls, preset: e.target.value }), children: [
1256
+ /* @__PURE__ */ jsx10("option", { value: "full", children: "Full" }),
1257
+ /* @__PURE__ */ jsx10("option", { value: "compact", children: "Compact" }),
1258
+ /* @__PURE__ */ jsx10("option", { value: "brief", children: "Brief" }),
1259
+ /* @__PURE__ */ jsx10("option", { value: "custom", children: "Custom" })
1260
+ ] })
1261
+ ] }),
1262
+ /* @__PURE__ */ jsx10(Button, { type: "button", size: "sm", variant: "outline", disabled: !state.draft.history?.length && !controlsHistory.length || busy !== null, onClick: () => {
1263
+ setBufferError(null);
1264
+ if (controlsHistory.length) {
1265
+ const next = controlsHistory[controlsHistory.length - 1];
1266
+ setControls(next);
1267
+ setControlsHistory((history) => history.slice(0, -1));
1268
+ onChange({ ...state, draft: rebuild(state, next), error: void 0 });
1269
+ } else onChange({ ...state, draft: undoDraft(state.draft), error: void 0 });
1270
+ }, children: "Undo" }),
1271
+ /* @__PURE__ */ jsx10(Button, { type: "button", size: "sm", variant: "outline", disabled: busy !== null, onClick: () => {
1272
+ const next = initialControls(state);
1273
+ setControls(next);
1274
+ setControlsHistory([]);
1275
+ setPendingControls(null);
1276
+ setBufferError(null);
1277
+ onChange({ ...state, draft: rebuild(state, next), error: void 0 });
1278
+ }, children: "Reset" }),
1279
+ /* @__PURE__ */ jsx10(Button, { type: "button", size: "sm", variant: "outline", disabled: busy !== null, onClick: () => void requestCapture(state.scope), children: "Refresh source" })
1280
+ ] }),
1281
+ controls.preset === "custom" && (state.draft.payload.kind === "json" || state.draft.payload.kind === "registered") ? /* @__PURE__ */ jsxs6("fieldset", { className: "matrx-alchemy-options", children: [
1282
+ /* @__PURE__ */ jsx10("legend", { children: "JSON limits" }),
1283
+ [["maxDepth", "Maximum depth"], ["maxStringChars", "Maximum string characters"], ["maxArrayItems", "Maximum array items"]].map(([key, text]) => /* @__PURE__ */ jsxs6("label", { children: [
1284
+ text,
1285
+ /* @__PURE__ */ jsx10("input", { type: "number", min: "0", value: controls[key] ?? "", onChange: (e) => changeControls({ ...controls, [key]: Number.isNaN(e.target.valueAsNumber) ? null : e.target.valueAsNumber }) })
1286
+ ] }, key))
1287
+ ] }) : null,
1288
+ rows ? /* @__PURE__ */ jsx10(RowControls, { rows, controls, change: changeControls }) : null,
1289
+ state.snapshot.sections.length ? /* @__PURE__ */ jsxs6("fieldset", { className: "matrx-alchemy-options", children: [
1290
+ /* @__PURE__ */ jsx10("legend", { children: "Sections" }),
1291
+ state.snapshot.sections.map((section) => /* @__PURE__ */ jsxs6("label", { children: [
1292
+ /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: controls.sections.includes(section.id), onChange: (e) => changeControls({ ...controls, sections: e.target.checked ? [...controls.sections, section.id] : controls.sections.filter((id) => id !== section.id) }) }),
1293
+ " ",
1294
+ section.label
1295
+ ] }, section.id))
1296
+ ] }) : null,
1297
+ /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-coverage", children: [
1298
+ /* @__PURE__ */ jsxs6("span", { children: [
1299
+ state.snapshot.coverage.included.toLocaleString(),
1300
+ " included",
1301
+ state.snapshot.coverage.total == null ? "" : ` of ${state.snapshot.coverage.total.toLocaleString()}`,
1302
+ " \xB7 ",
1303
+ state.snapshot.coverage.status
1304
+ ] }),
1305
+ state.snapshot.coverage.reason ? /* @__PURE__ */ jsx10("span", { children: state.snapshot.coverage.reason }) : null,
1306
+ limitText ? /* @__PURE__ */ jsxs6("span", { children: [
1307
+ "Limits: ",
1308
+ limitText
1309
+ ] }) : null,
1310
+ displayedOmissions.length ? /* @__PURE__ */ jsxs6("details", { children: [
1311
+ /* @__PURE__ */ jsxs6("summary", { children: [
1312
+ displayedOmissions.length,
1313
+ " omissions or adjustments"
1314
+ ] }),
1315
+ /* @__PURE__ */ jsx10("ul", { children: displayedOmissions.map((o, i) => /* @__PURE__ */ jsxs6("li", { children: [
1316
+ o.path || "/",
1317
+ ": ",
1318
+ o.reason,
1319
+ o.count == null ? "" : ` (${o.count})`
1320
+ ] }, `${o.path}-${o.reason}-${i}`)) })
1321
+ ] }) : null
1322
+ ] }),
1323
+ /* @__PURE__ */ jsxs6("label", { className: "matrx-alchemy-preview-label", children: [
1324
+ "Output format",
1325
+ /* @__PURE__ */ jsxs6("select", { "aria-label": "Output format", value: format, onChange: (e) => setFormat(e.target.value), children: [
1326
+ formats.map((f) => /* @__PURE__ */ jsx10("option", { value: f, children: LABEL[f] }, f)),
1327
+ state.draft.payload.kind === "rows" ? /* @__PURE__ */ jsx10("option", { value: "xlsx", children: "Excel workbook" }) : null,
1328
+ workspaceAdapters.map((adapter) => /* @__PURE__ */ jsx10("option", { value: adapter.id, children: adapter.label }, adapter.id))
1329
+ ] })
1330
+ ] }),
1331
+ /* @__PURE__ */ jsx10("textarea", { className: "matrx-alchemy-preview", "aria-label": "Prepared output", "aria-invalid": bufferError ? true : void 0, "aria-describedby": bufferError ? "alchemy-buffer-error" : void 0, value: buffer, onChange: (e) => editBuffer(e.target.value) }),
1332
+ bufferError ? /* @__PURE__ */ jsx10("p", { id: "alchemy-buffer-error", role: "alert", className: "matrx-alchemy-error", children: bufferError }) : null,
1333
+ formatError ? /* @__PURE__ */ jsx10("p", { role: "alert", className: "matrx-alchemy-error", children: formatError }) : null,
1334
+ bufferError ? /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", disabled: busy !== null, onClick: () => {
1335
+ const fallback = editDraft(state.draft, { kind: "text", text: buffer });
1336
+ setBufferError(null);
1337
+ onChange({ ...state, draft: fallback, error: void 0 });
1338
+ setFormat("plain");
1339
+ }, children: "Use invalid JSON as plain text" }) : null
1340
+ ] }),
1341
+ ai || busy === "capture" || state.error ? /* @__PURE__ */ jsxs6("aside", { children: [
1342
+ ai ? /* @__PURE__ */ jsxs6(Fragment, { children: [
1343
+ /* @__PURE__ */ jsxs6("label", { children: [
1344
+ "AI instruction",
1345
+ /* @__PURE__ */ jsx10("textarea", { value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "Tell AI how to prepare this content" })
1346
+ ] }),
1347
+ /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", disabled: !aiSession.current || !instruction.trim() || busy !== null || !aiSession.current.supports(state.snapshot), onClick: () => void runAi(false), children: "Generate preview" }),
1348
+ busy === "ai" ? /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: () => void cancelAi(), children: aiSession.current?.cancel ? "Cancel AI" : "Stop waiting" }) : null
1349
+ ] }) : null,
1350
+ busy === "capture" ? /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: () => {
1351
+ activeCapture.current?.abort();
1352
+ activeCapture.current = null;
1353
+ setBusy(null);
1354
+ }, children: "Stop loading rows" }) : null,
1355
+ ai && aiSession.current?.recover && busy !== "ai" ? /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: () => void runAi(true), children: aiFailed ? "Retry or rejoin AI run" : "Rejoin AI run" }) : null,
1356
+ ai ? /* @__PURE__ */ jsx10("p", { className: "matrx-alchemy-meta", children: "AI creates a preview. It never copies automatically. Closing detaches this view without cancelling server work." }) : null,
1357
+ state.error ? /* @__PURE__ */ jsx10("p", { role: "alert", className: "matrx-alchemy-error", children: state.error }) : null
1358
+ ] }) : null
1359
+ ] }),
1360
+ pendingControls ? /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-decision", role: "alert", children: [
1361
+ /* @__PURE__ */ jsx10("p", { children: "This control would replace your manual edit." }),
1362
+ /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: () => setPendingControls(null), children: "Keep edited draft" }),
1363
+ /* @__PURE__ */ jsx10(Button, { type: "button", onClick: applyPendingControls, children: "Rebuild from source" })
1364
+ ] }) : null,
1365
+ state.pending ? /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-decision", role: "alert", children: [
1366
+ /* @__PURE__ */ jsx10("p", { children: "A fresh source snapshot is ready." }),
1367
+ /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: () => resolveCapture("keep"), children: "Keep current snapshot and draft" }),
1368
+ /* @__PURE__ */ jsx10(Button, { type: "button", onClick: () => resolveCapture("regenerate"), children: "Regenerate from fresh source" })
624
1369
  ] }) : null,
625
- variants.map((variant) => variant.modal && modal === variant.id ? /* @__PURE__ */ jsx11("span", { children: variant.modal({ open: true, onOpenChange: (open) => setModal(open ? variant.id : null) }) }, variant.id) : null),
626
- modal === "custom" && aiCustom ? /* @__PURE__ */ jsx11(CustomCopy, { source: aiCustom, onClose: () => setModal(null) }) : null,
627
- notice
1370
+ deliveryNotice ? /* @__PURE__ */ jsx10("p", { role: "status", className: "matrx-alchemy-meta", children: deliveryNotice }) : null,
1371
+ /* @__PURE__ */ jsxs6(DialogFooter, { children: [
1372
+ /* @__PURE__ */ jsxs6("span", { className: "matrx-alchemy-meta", children: [
1373
+ buffer.length.toLocaleString(),
1374
+ " characters \xB7 ~",
1375
+ estimateTokens(buffer).toLocaleString(),
1376
+ " tokens"
1377
+ ] }),
1378
+ /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", onClick: close, children: "Close" }),
1379
+ /* @__PURE__ */ jsx10(Button, { type: "button", variant: "outline", disabled: busy !== null || Boolean(bufferError || formatError) || !preparedArtifact, onClick: () => void deliver(true), children: "Download prepared output" }),
1380
+ /* @__PURE__ */ jsx10(Button, { type: "button", disabled: busy !== null || Boolean(bufferError || formatError) || !preparedArtifact, onClick: () => void deliver(false), children: "Copy prepared output" })
1381
+ ] })
1382
+ ] }) });
1383
+ }
1384
+ function RowControls({ rows, controls, change }) {
1385
+ return /* @__PURE__ */ jsxs6(Fragment, { children: [
1386
+ /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-toolbar", children: [
1387
+ /* @__PURE__ */ jsxs6("label", { children: [
1388
+ "Search",
1389
+ /* @__PURE__ */ jsx10("input", { value: controls.search, onChange: (e) => change({ ...controls, search: e.target.value }) })
1390
+ ] }),
1391
+ /* @__PURE__ */ jsxs6("label", { children: [
1392
+ "Sort",
1393
+ /* @__PURE__ */ jsxs6("select", { value: controls.sort, onChange: (e) => change({ ...controls, sort: e.target.value }), children: [
1394
+ /* @__PURE__ */ jsx10("option", { value: "", children: "None" }),
1395
+ rows.columns.map((column) => /* @__PURE__ */ jsx10("option", { value: column.id, children: column.label }, column.id))
1396
+ ] })
1397
+ ] }),
1398
+ /* @__PURE__ */ jsxs6("label", { children: [
1399
+ "Direction",
1400
+ /* @__PURE__ */ jsxs6("select", { value: controls.direction, disabled: !controls.sort, onChange: (e) => change({ ...controls, direction: e.target.value }), children: [
1401
+ /* @__PURE__ */ jsx10("option", { value: "asc", children: "Ascending" }),
1402
+ /* @__PURE__ */ jsx10("option", { value: "desc", children: "Descending" })
1403
+ ] })
1404
+ ] }),
1405
+ /* @__PURE__ */ jsxs6("label", { children: [
1406
+ "Top rows",
1407
+ /* @__PURE__ */ jsx10("input", { type: "number", min: "0", value: controls.top ?? "", onChange: (e) => change({ ...controls, top: Number.isNaN(e.target.valueAsNumber) ? null : e.target.valueAsNumber }) })
1408
+ ] })
1409
+ ] }),
1410
+ /* @__PURE__ */ jsxs6("fieldset", { className: "matrx-alchemy-options", children: [
1411
+ /* @__PURE__ */ jsx10("legend", { children: "Columns and filters" }),
1412
+ rows.columns.map((column) => /* @__PURE__ */ jsxs6("div", { className: "matrx-alchemy-column", children: [
1413
+ /* @__PURE__ */ jsxs6("label", { children: [
1414
+ /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: controls.visibleColumns.includes(column.id), onChange: (e) => change({ ...controls, visibleColumns: e.target.checked ? [...controls.visibleColumns, column.id] : controls.visibleColumns.filter((id) => id !== column.id) }) }),
1415
+ " ",
1416
+ column.label
1417
+ ] }),
1418
+ /* @__PURE__ */ jsx10("input", { "aria-label": `Filter ${column.label}`, placeholder: "Filter", value: controls.filters[column.id] ?? "", onChange: (e) => change({ ...controls, filters: { ...controls.filters, [column.id]: e.target.value } }) })
1419
+ ] }, column.id))
1420
+ ] }),
1421
+ /* @__PURE__ */ jsxs6("fieldset", { className: "matrx-alchemy-options", children: [
1422
+ /* @__PURE__ */ jsx10("legend", { children: "Rows" }),
1423
+ /* @__PURE__ */ jsxs6("label", { children: [
1424
+ /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: controls.selectedOnly, onChange: (e) => change({ ...controls, selectedOnly: e.target.checked }) }),
1425
+ " Only selected rows"
1426
+ ] }),
1427
+ /* @__PURE__ */ jsx10("div", { className: "matrx-alchemy-row-list", children: rows.rows.map((row, index) => {
1428
+ const summary = rows.columns.filter((column) => column.visible).slice(0, 2).map((column) => String(readTransferCell(row, column) ?? "")).filter(Boolean).join(" \xB7 ");
1429
+ return /* @__PURE__ */ jsxs6("label", { children: [
1430
+ /* @__PURE__ */ jsx10("input", { type: "checkbox", checked: controls.selectedIndices.includes(index), onChange: (e) => change({ ...controls, selectedIndices: e.target.checked ? [...controls.selectedIndices, index] : controls.selectedIndices.filter((value) => value !== index) }) }),
1431
+ " Row ",
1432
+ index + 1,
1433
+ summary ? ` \u2014 ${summary}` : ""
1434
+ ] }, index);
1435
+ }) })
1436
+ ] })
628
1437
  ] });
629
1438
  }
630
- function CustomCopy({ source, onClose }) {
631
- const [values, setValues] = useState4(() => Object.fromEntries(source.options.map((option) => [option.key, option.default])));
632
- const { notify, notice } = useTableNotice();
633
- const clipboard = useClipboard({ notify: (message, kind) => notify[kind](message) });
1439
+
1440
+ // src/content-transfer/matrx-copy.tsx
1441
+ import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
1442
+ var resolve = (value) => typeof value === "function" ? value() : value;
1443
+ var asPayload = (value) => ({
1444
+ kind: "markdown",
1445
+ text: typeof value === "string" ? value : buildAgentPayload(value)
1446
+ });
1447
+ var structuredPayload = (value) => {
1448
+ if (typeof value === "string") return { kind: "markdown", text: value };
1449
+ const clean = (record) => Object.fromEntries(
1450
+ Object.entries(record ?? {}).filter(([, entry]) => entry !== void 0)
1451
+ );
1452
+ return {
1453
+ kind: "json",
1454
+ value: normalizeTransferJson({
1455
+ kind: value.kind,
1456
+ location: value.location,
1457
+ description: value.description,
1458
+ data: value.data,
1459
+ ...value.summary !== void 0 ? { summary: value.summary } : {},
1460
+ attributes: clean(value.attributes),
1461
+ context: clean(value.context)
1462
+ })
1463
+ };
1464
+ };
1465
+ var actionDone = (message) => ({
1466
+ status: "success",
1467
+ delivered: "action",
1468
+ mimeTypes: [],
1469
+ message
1470
+ });
1471
+ var sourceFor = (id, label, build) => ({
1472
+ id,
1473
+ label,
1474
+ capture: async ({ signal }) => {
1475
+ signal.throwIfAborted();
1476
+ const payload = await build();
1477
+ signal.throwIfAborted();
1478
+ return directSource2(payload, { sourceId: id, label });
1479
+ }
1480
+ });
1481
+ function MatrxCopyMenu(props) {
1482
+ return /* @__PURE__ */ jsx11(MatrxCopyMenuSession, { ...props }, props.sourceId ?? props.label);
1483
+ }
1484
+ function MatrxCopyMenuSession(props) {
1485
+ const instance = React8.useId();
1486
+ const controller = React8.useRef(null);
1487
+ const capabilities = useContentTransferCapabilities();
1488
+ const transport = capabilities.transport ?? createBrowserTransport2();
1489
+ const [modal, setModal] = React8.useState(null);
1490
+ const [configuration, setConfiguration] = React8.useState(null);
1491
+ const copyVisible = !props.hide?.includes("copy");
1492
+ const aiVisible = !props.hide?.includes("ai");
1493
+ const exportVisible = !props.hide?.includes("export");
1494
+ const make = (suffix, build, label = props.label) => sourceFor(
1495
+ `${props.sourceId ?? `${instance}:${props.label}`}:${suffix}`,
1496
+ label,
1497
+ build
1498
+ );
1499
+ const human = copyVisible && props.human !== void 0 ? make("human", () => ({ kind: "text", text: resolve(props.human) })) : void 0;
1500
+ const humanMarkdown = copyVisible && props.human !== void 0 ? make("human-markdown", () => ({
1501
+ kind: "markdown",
1502
+ text: resolve(props.human)
1503
+ })) : void 0;
1504
+ const json = aiVisible && props.json !== void 0 ? make("json", () => ({
1505
+ kind: "json",
1506
+ value: normalizeTransferJson(resolve(props.json))
1507
+ })) : void 0;
1508
+ const agent = aiVisible && props.agent !== void 0 ? make("agent", () => asPayload(resolve(props.agent))) : void 0;
1509
+ const structuredAgent = aiVisible && props.agent !== void 0 ? make("structured-agent", () => structuredPayload(resolve(props.agent))) : void 0;
1510
+ const table = exportVisible && props.export?.sheetRows ? make("rows", () => {
1511
+ const value = normalizeTransferJson(props.export.sheetRows());
1512
+ if (!Array.isArray(value) || value.some(
1513
+ (row) => row === null || typeof row !== "object" || Array.isArray(row)
1514
+ ))
1515
+ throw new Error("Sheet rows must be JSON objects.");
1516
+ const rows = value;
1517
+ const keys = [...new Set(rows.flatMap((row) => Object.keys(row)))];
1518
+ return {
1519
+ kind: "rows",
1520
+ rows,
1521
+ columns: keys.map((key) => ({
1522
+ id: key,
1523
+ label: key,
1524
+ path: `/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`,
1525
+ visible: true
1526
+ }))
1527
+ };
1528
+ }) : void 0;
1529
+ const builderVariants = aiVisible ? (props.aiVariants ?? []).filter(
1530
+ (variant) => variant.build && !variant.onSelect && !variant.modal
1531
+ ).map((variant) => ({
1532
+ ...variant,
1533
+ copyLabel: variant.label,
1534
+ source: make(
1535
+ `variant:${variant.id}`,
1536
+ async () => asPayload(await variant.build()),
1537
+ variant.label
1538
+ )
1539
+ })) : [];
1540
+ const faithful = agent ? [
1541
+ {
1542
+ id: props.agentVariant?.id ?? "faithful",
1543
+ label: props.agentVariant?.label ?? "Full AI context",
1544
+ source: agent,
1545
+ copyLabel: props.agentVariant?.label ?? "Copy for AI",
1546
+ ...props.agentVariant?.hint ? { hint: props.agentVariant.hint } : {}
1547
+ }
1548
+ ] : [];
1549
+ const variants = props.agentVariant?.position === "last" ? [...builderVariants, ...faithful] : [...faithful, ...builderVariants];
1550
+ const actions = [];
1551
+ if (aiVisible)
1552
+ for (const variant of props.aiVariants ?? []) {
1553
+ if (!variant.onSelect && !variant.modal) continue;
1554
+ actions.push({
1555
+ ...variant,
1556
+ id: `variant-action:${variant.id}`,
1557
+ label: variant.label,
1558
+ supports: () => true,
1559
+ run: async () => {
1560
+ if (variant.onSelect) await variant.onSelect();
1561
+ else setModal(variant);
1562
+ return actionDone(`Opened ${variant.label}`);
1563
+ }
1564
+ });
1565
+ }
1566
+ if (aiVisible && props.aiCustom)
1567
+ actions.push({
1568
+ id: "configure-source",
1569
+ label: props.aiCustom.label ?? "Customize source\u2026",
1570
+ ...props.aiCustom.hint ? { hint: props.aiCustom.hint } : {},
1571
+ supports: () => true,
1572
+ run: async () => {
1573
+ setConfiguration({ custom: props.aiCustom });
1574
+ return actionDone("Source options opened");
1575
+ }
1576
+ });
1577
+ if (aiVisible && props.groomer)
1578
+ actions.push({
1579
+ id: "groom-source",
1580
+ label: "Customize sections\u2026",
1581
+ supports: () => true,
1582
+ run: async () => {
1583
+ setConfiguration({ groomer: props.groomer() });
1584
+ return actionDone("Section options opened");
1585
+ }
1586
+ });
1587
+ if (exportVisible)
1588
+ for (const item of props.export?.items ?? [])
1589
+ actions.push({
1590
+ id: `export:${item.id}`,
1591
+ label: item.label,
1592
+ supports: () => true,
1593
+ ...item.build && !item.onSelect ? {
1594
+ source: make(
1595
+ `export:${item.id}`,
1596
+ async () => {
1597
+ const file = item.build();
1598
+ const bytes = new Uint8Array(
1599
+ await new Blob([file.content], {
1600
+ type: file.mime
1601
+ }).arrayBuffer()
1602
+ );
1603
+ return {
1604
+ kind: "registered",
1605
+ format: "matrx-file",
1606
+ value: {
1607
+ bytes: Array.from(bytes),
1608
+ mime: file.mime,
1609
+ extension: file.extension
1610
+ }
1611
+ };
1612
+ },
1613
+ item.label
1614
+ )
1615
+ } : {},
1616
+ run: async ({ snapshot, draft, signal }) => {
1617
+ if (item.onSelect) {
1618
+ await item.onSelect();
1619
+ return actionDone(`Opened ${item.label}`);
1620
+ }
1621
+ const value = draft.payload.kind === "registered" ? draft.payload.value : null;
1622
+ if (!value || Array.isArray(value) || typeof value !== "object" || !Array.isArray(value.bytes) || typeof value.mime !== "string" || typeof value.extension !== "string" || value.bytes.some(
1623
+ (byte) => typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255
1624
+ ))
1625
+ throw new Error(`${item.label} did not capture a valid file.`);
1626
+ if (draft.omissions.some((omission) => omission.reason !== "duplicate"))
1627
+ throw new Error(
1628
+ "This custom file exceeds the configured export limits or contains excluded content. Use a standard format or revise the source options."
1629
+ );
1630
+ const bytes = new Uint8Array(value.bytes);
1631
+ const filename = `${props.label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "export"}.${value.extension.replace(/[^a-zA-Z0-9.-]/g, "") || "bin"}`;
1632
+ return transport.download(
1633
+ sealTransferArtifact2({
1634
+ snapshotId: snapshot.id,
1635
+ draftRevision: draft.revision,
1636
+ format: `export:${item.id}`,
1637
+ plainText: "",
1638
+ file: { filename, mime: value.mime, bytes },
1639
+ omissions: [...draft.omissions]
1640
+ }),
1641
+ signal
1642
+ );
1643
+ }
1644
+ });
1645
+ if (exportVisible && table && props.sendToSheet)
1646
+ actions.push({
1647
+ id: "send-google-sheet",
1648
+ label: "Send to Google Sheet",
1649
+ source: table,
1650
+ supports: () => true,
1651
+ run: async ({ draft }) => {
1652
+ if (draft.payload.kind !== "rows")
1653
+ throw new Error("Google Sheets requires captured rows.");
1654
+ return props.sendToSheet(draft.payload.rows, props.label);
1655
+ }
1656
+ });
1657
+ const source = json ?? table ?? structuredAgent ?? human ?? builderVariants[0]?.source;
1658
+ if (!source && !actions.length) return null;
1659
+ const target = source ?? make("actions", () => ({ kind: "text", text: props.label }));
1660
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
1661
+ /* @__PURE__ */ jsx11(
1662
+ ContentTransferMenu,
1663
+ {
1664
+ source: target,
1665
+ controllerRef: controller,
1666
+ label: props.label,
1667
+ variants,
1668
+ actions,
1669
+ hide: [
1670
+ ...!copyVisible || !source ? ["copy"] : [],
1671
+ ...!aiVisible || !source ? ["prepare"] : [],
1672
+ ...!exportVisible || !source ? ["download"] : []
1673
+ ],
1674
+ formatSources: {
1675
+ ...human ? { plain: human, markdown: humanMarkdown, html: humanMarkdown } : {},
1676
+ ...json ? { json, "compact-json": json } : {},
1677
+ ...table ? { csv: table, tsv: table } : {}
1678
+ },
1679
+ ...props.disabled !== void 0 ? { disabled: props.disabled } : {},
1680
+ className: cn(
1681
+ props.className,
1682
+ props.size === "xs" && "matrx-alchemy-xs",
1683
+ props.size === "sm" && "matrx-alchemy-sm"
1684
+ ),
1685
+ ...props.stopPropagation !== void 0 ? { stopPropagation: props.stopPropagation } : {}
1686
+ }
1687
+ ),
1688
+ modal?.modal?.({
1689
+ open: true,
1690
+ onOpenChange: (open) => {
1691
+ if (!open) setModal(null);
1692
+ }
1693
+ }),
1694
+ configuration ? /* @__PURE__ */ jsx11(
1695
+ SourceConfiguration,
1696
+ {
1697
+ restoreFocus: () => controller.current?.focusTrigger(),
1698
+ configuration,
1699
+ onClose: () => setConfiguration(null),
1700
+ onPrepare: async (payload, label) => {
1701
+ if (!controller.current)
1702
+ throw new Error("Alchemy is no longer mounted.");
1703
+ await controller.current.prepare(payload, {
1704
+ label,
1705
+ id: `${instance}:configured:${crypto.randomUUID()}`
1706
+ });
1707
+ setConfiguration(null);
1708
+ }
1709
+ },
1710
+ `${instance}:configuration`
1711
+ ) : null
1712
+ ] });
1713
+ }
1714
+ function SourceConfiguration({
1715
+ restoreFocus,
1716
+ configuration,
1717
+ onClose,
1718
+ onPrepare
1719
+ }) {
1720
+ const custom = configuration.custom;
1721
+ const handingOff = React8.useRef(false);
1722
+ const groomer = configuration.groomer;
1723
+ const [values, setValues] = React8.useState(
1724
+ () => Object.fromEntries(
1725
+ custom?.options.map((option) => [option.key, option.default]) ?? []
1726
+ )
1727
+ );
1728
+ const [sections, setSections] = React8.useState(
1729
+ () => Object.fromEntries(
1730
+ groomer?.sections.map((section) => [
1731
+ section.id,
1732
+ section.defaultSelection ?? "full"
1733
+ ]) ?? []
1734
+ )
1735
+ );
1736
+ const [error, setError] = React8.useState(null);
1737
+ const [busy, setBusy] = React8.useState(false);
1738
+ const title = custom?.dialogTitle ?? groomer?.label ?? "Customize source";
1739
+ let prepared = null;
1740
+ let buildError = null;
634
1741
  let text = "";
635
- let error = null;
636
1742
  try {
637
- const built = source.build(values);
638
- text = tableAgentText(source.wrap(built.text, values, built.meta));
639
- } catch (raw) {
640
- error = raw instanceof Error ? raw.message : String(raw);
1743
+ if (custom) {
1744
+ const result = custom.build(values);
1745
+ prepared = custom.wrap(result.text, values, result.meta);
1746
+ } else if (groomer) {
1747
+ const data = {};
1748
+ const omitted = [];
1749
+ for (const section of groomer.sections) {
1750
+ const level = sections[section.id] ?? "full";
1751
+ if (level === "off") omitted.push(section.id);
1752
+ else {
1753
+ const value = section.build(level);
1754
+ if (value !== void 0 && value !== null) data[section.id] = value;
1755
+ }
1756
+ }
1757
+ prepared = {
1758
+ kind: groomer.kind,
1759
+ location: groomer.location,
1760
+ description: groomer.description,
1761
+ data,
1762
+ ...groomer.summary ? { summary: groomer.summary } : {},
1763
+ attributes: {
1764
+ ...groomer.attributes,
1765
+ dropped_sections: omitted.join(",")
1766
+ },
1767
+ ...groomer.context ? { context: groomer.context } : {}
1768
+ };
1769
+ }
1770
+ text = prepared ? buildAgentPayload(prepared) : "";
1771
+ } catch (cause) {
1772
+ buildError = cause instanceof Error ? cause.message : "Could not prepare this source.";
641
1773
  }
642
- return /* @__PURE__ */ jsx11(Dialog, { open: true, onOpenChange: (open) => {
643
- if (!open) onClose();
644
- }, children: /* @__PURE__ */ jsxs6(DialogContent, { children: [
645
- /* @__PURE__ */ jsx11(DialogHeader, { children: /* @__PURE__ */ jsx11(DialogTitle, { children: source.dialogTitle ?? "Custom copy" }) }),
646
- source.dialogDescription ? /* @__PURE__ */ jsx11("p", { className: "text-sm", children: source.dialogDescription }) : null,
647
- source.options.map((option) => /* @__PURE__ */ jsxs6("label", { className: "flex items-center justify-between gap-3 text-sm", children: [
648
- option.label,
649
- option.kind === "toggle" ? /* @__PURE__ */ jsx11("input", { type: "checkbox", checked: values[option.key] === true, onChange: (e) => setValues({ ...values, [option.key]: e.target.checked }) }) : option.kind === "preset" ? /* @__PURE__ */ jsx11("select", { "aria-label": option.label, value: Number(values[option.key]), onChange: (e) => setValues({ ...values, [option.key]: Number(e.target.value) }), children: option.options.map((choice) => /* @__PURE__ */ jsx11("option", { value: choice.value, children: choice.label }, choice.value)) }) : option.kind === "slider" ? /* @__PURE__ */ jsx11("input", { "aria-label": option.label, type: "range", min: 0, max: option.stops.length - 1, step: 1, value: Math.max(0, option.stops.indexOf(Number(values[option.key]))), onChange: (e) => setValues({ ...values, [option.key]: option.stops[Number(e.target.value)] ?? option.default }) }) : /* @__PURE__ */ jsxs6("span", { className: "flex gap-1", children: [
650
- option.presets?.map((choice) => /* @__PURE__ */ jsx11(Button, { type: "button", size: "sm", variant: "outline", onClick: () => setValues({ ...values, [option.key]: choice.value }), children: choice.label }, choice.value)),
651
- /* @__PURE__ */ jsx11("input", { "aria-label": option.label, type: "number", value: Number(values[option.key]), min: option.min, step: option.step, onChange: (e) => {
652
- if (Number.isFinite(e.target.valueAsNumber)) setValues({ ...values, [option.key]: e.target.valueAsNumber });
653
- } })
654
- ] })
655
- ] }, option.key)),
656
- error ? /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-destructive", children: error }) : /* @__PURE__ */ jsx11("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap text-xs", children: text }),
657
- notice,
658
- /* @__PURE__ */ jsxs6(DialogFooter, { children: [
659
- /* @__PURE__ */ jsx11(Button, { type: "button", variant: "outline", onClick: onClose, children: "Close" }),
660
- /* @__PURE__ */ jsx11(Button, { type: "button", disabled: error !== null, onClick: () => void clipboard.copyText(text, "Custom copy copied"), children: "Copy" })
661
- ] })
662
- ] }) });
1774
+ return /* @__PURE__ */ jsx11(
1775
+ Dialog,
1776
+ {
1777
+ open: true,
1778
+ onOpenChange: (open) => {
1779
+ if (!open && !busy) onClose();
1780
+ },
1781
+ children: /* @__PURE__ */ jsxs7(
1782
+ DialogContent,
1783
+ {
1784
+ onCloseAutoFocus: (event) => {
1785
+ event.preventDefault();
1786
+ if (!handingOff.current) restoreFocus();
1787
+ },
1788
+ className: "matrx-alchemy-workspace",
1789
+ children: [
1790
+ /* @__PURE__ */ jsxs7(DialogHeader, { children: [
1791
+ /* @__PURE__ */ jsx11(DialogTitle, { children: title }),
1792
+ /* @__PURE__ */ jsx11(DialogDescription, { children: custom?.dialogDescription ?? "Choose the source detail, then prepare, edit, summarize, copy or export it in Alchemy." })
1793
+ ] }),
1794
+ /* @__PURE__ */ jsxs7("div", { className: "matrx-alchemy-main matrx-alchemy-toolbar", children: [
1795
+ custom?.options.map((option) => /* @__PURE__ */ jsxs7("label", { children: [
1796
+ option.label,
1797
+ "hint" in option && option.hint ? /* @__PURE__ */ jsx11("small", { children: option.hint }) : null,
1798
+ option.kind === "toggle" ? /* @__PURE__ */ jsx11(
1799
+ "input",
1800
+ {
1801
+ type: "checkbox",
1802
+ checked: Boolean(values[option.key]),
1803
+ onChange: (event) => setValues({ ...values, [option.key]: event.target.checked })
1804
+ }
1805
+ ) : option.kind === "preset" ? /* @__PURE__ */ jsx11(
1806
+ "select",
1807
+ {
1808
+ value: Number(values[option.key]),
1809
+ onChange: (event) => setValues({
1810
+ ...values,
1811
+ [option.key]: Number(event.target.value)
1812
+ }),
1813
+ children: option.options.map((choice) => /* @__PURE__ */ jsx11("option", { value: choice.value, children: choice.label }, choice.value))
1814
+ }
1815
+ ) : option.kind === "slider" ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1816
+ /* @__PURE__ */ jsx11(
1817
+ "input",
1818
+ {
1819
+ type: "range",
1820
+ min: 0,
1821
+ max: Math.max(0, option.stops.length - 1),
1822
+ step: 1,
1823
+ value: option.stops.indexOf(Number(values[option.key])),
1824
+ onChange: (event) => setValues({
1825
+ ...values,
1826
+ [option.key]: option.stops[Number(event.target.value)]
1827
+ })
1828
+ }
1829
+ ),
1830
+ /* @__PURE__ */ jsx11("span", { children: option.formatValue(Number(values[option.key])) })
1831
+ ] }) : /* @__PURE__ */ jsxs7(Fragment2, { children: [
1832
+ /* @__PURE__ */ jsx11(
1833
+ "input",
1834
+ {
1835
+ type: "number",
1836
+ min: option.min,
1837
+ step: option.step,
1838
+ value: Number(values[option.key]),
1839
+ onChange: (event) => setValues({
1840
+ ...values,
1841
+ [option.key]: Number(event.target.value)
1842
+ })
1843
+ }
1844
+ ),
1845
+ option.presets?.map((preset) => /* @__PURE__ */ jsx11(
1846
+ Button,
1847
+ {
1848
+ type: "button",
1849
+ variant: "ghost",
1850
+ onClick: () => setValues({ ...values, [option.key]: preset.value }),
1851
+ children: preset.label
1852
+ },
1853
+ preset.value
1854
+ ))
1855
+ ] })
1856
+ ] }, option.key)),
1857
+ groomer ? /* @__PURE__ */ jsxs7(Fragment2, { children: [
1858
+ /* @__PURE__ */ jsx11("div", { children: ["full", "compact", "brief"].map((level) => /* @__PURE__ */ jsx11(
1859
+ Button,
1860
+ {
1861
+ type: "button",
1862
+ variant: "ghost",
1863
+ onClick: () => setSections(
1864
+ Object.fromEntries(
1865
+ groomer.sections.map((section) => [
1866
+ section.id,
1867
+ level === "brief" && section.cuttable ? "off" : level
1868
+ ])
1869
+ )
1870
+ ),
1871
+ children: level
1872
+ },
1873
+ level
1874
+ )) }),
1875
+ groomer.sections.map((section) => /* @__PURE__ */ jsxs7("label", { children: [
1876
+ section.title,
1877
+ /* @__PURE__ */ jsxs7(
1878
+ "select",
1879
+ {
1880
+ value: sections[section.id],
1881
+ onChange: (event) => setSections({
1882
+ ...sections,
1883
+ [section.id]: event.target.value
1884
+ }),
1885
+ children: [
1886
+ ["full", "compact", "brief"].map((level) => /* @__PURE__ */ jsx11("option", { value: level, children: section.levelLabels?.[level] ?? level }, level)),
1887
+ section.cuttable ? /* @__PURE__ */ jsx11("option", { value: "off", children: "Exclude" }) : null
1888
+ ]
1889
+ }
1890
+ ),
1891
+ section.description ? /* @__PURE__ */ jsx11("span", { children: section.description }) : null
1892
+ ] }, section.id))
1893
+ ] }) : null
1894
+ ] }),
1895
+ /* @__PURE__ */ jsxs7("p", { children: [
1896
+ text.length.toLocaleString(),
1897
+ " characters \xB7 approximately",
1898
+ " ",
1899
+ Math.ceil(text.length / 4).toLocaleString(),
1900
+ " tokens"
1901
+ ] }),
1902
+ error || buildError ? /* @__PURE__ */ jsx11("p", { role: "alert", children: error ?? buildError }) : null,
1903
+ /* @__PURE__ */ jsxs7(DialogFooter, { children: [
1904
+ /* @__PURE__ */ jsx11(
1905
+ Button,
1906
+ {
1907
+ type: "button",
1908
+ variant: "ghost",
1909
+ disabled: busy,
1910
+ onClick: onClose,
1911
+ children: "Cancel"
1912
+ }
1913
+ ),
1914
+ /* @__PURE__ */ jsx11(
1915
+ Button,
1916
+ {
1917
+ type: "button",
1918
+ disabled: busy || !prepared || Boolean(buildError),
1919
+ onClick: async () => {
1920
+ if (!prepared) return;
1921
+ setBusy(true);
1922
+ setError(null);
1923
+ try {
1924
+ handingOff.current = true;
1925
+ await onPrepare(structuredPayload(prepared), title);
1926
+ } catch (cause) {
1927
+ handingOff.current = false;
1928
+ setError(
1929
+ cause instanceof Error ? cause.message : "Could not open Alchemy."
1930
+ );
1931
+ } finally {
1932
+ setBusy(false);
1933
+ }
1934
+ },
1935
+ children: busy ? "Preparing\u2026" : "Prepare in Alchemy"
1936
+ }
1937
+ )
1938
+ ] })
1939
+ ]
1940
+ }
1941
+ )
1942
+ }
1943
+ );
1944
+ }
1945
+
1946
+ // src/data-table/copy.tsx
1947
+ import { jsx as jsx12 } from "react/jsx-runtime";
1948
+ function CopyButtons(props) {
1949
+ const { CopyControls } = useMatrxDataTableHost();
1950
+ if (CopyControls) return /* @__PURE__ */ jsx12(CopyControls, { ...props });
1951
+ return /* @__PURE__ */ jsx12(MatrxCopyMenu, { ...props });
663
1952
  }
664
1953
 
665
1954
  // src/data-table/MatrxUuidCell.tsx
666
1955
  import {
667
- useEffect as useEffect2,
668
- useRef,
669
- useState as useState5
1956
+ useEffect as useEffect3,
1957
+ useRef as useRef3,
1958
+ useState as useState6
670
1959
  } from "react";
671
1960
 
1961
+ // src/data-table/useTableNotice.tsx
1962
+ import { useState as useState5 } from "react";
1963
+ import { jsx as jsx13, jsxs as jsxs8 } from "react/jsx-runtime";
1964
+ function useTableNotice() {
1965
+ const host = useMatrxDataTableHost();
1966
+ const [message, setMessage] = useState5(null);
1967
+ const notify = {
1968
+ success(text) {
1969
+ if (host.notify) host.notify.success(text);
1970
+ else setMessage({ text, error: false });
1971
+ },
1972
+ error(text) {
1973
+ if (host.notify) host.notify.error(text);
1974
+ else setMessage({ text, error: true });
1975
+ }
1976
+ };
1977
+ const notice = message ? /* @__PURE__ */ jsxs8("span", { role: message.error ? "alert" : "status", className: message.error ? "text-xs text-destructive" : "text-xs text-muted-foreground", children: [
1978
+ message.text,
1979
+ /* @__PURE__ */ jsx13("button", { type: "button", "aria-label": "Dismiss notification", className: "ml-2 underline", onClick: () => setMessage(null), children: "Dismiss" })
1980
+ ] }) : null;
1981
+ return { notify, notice };
1982
+ }
1983
+
672
1984
  // src/tooltip.tsx
673
1985
  import * as TooltipPrimitive from "@radix-ui/react-tooltip";
674
- import * as React7 from "react";
675
- import { jsx as jsx12 } from "react/jsx-runtime";
1986
+ import * as React9 from "react";
1987
+ import { jsx as jsx14 } from "react/jsx-runtime";
676
1988
  var TooltipProvider = TooltipPrimitive.Provider;
677
1989
  var Tooltip = TooltipPrimitive.Root;
678
1990
  var TooltipTrigger = TooltipPrimitive.Trigger;
679
- var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {
1991
+ var TooltipContent = React9.forwardRef(({ className, sideOffset = 4, container, animated = true, ...props }, ref) => {
680
1992
  const resolved = usePortalContainer(container);
681
- return /* @__PURE__ */ jsx12(TooltipPrimitive.Portal, { container: resolved ?? null, children: /* @__PURE__ */ jsx12(
1993
+ return /* @__PURE__ */ jsx14(TooltipPrimitive.Portal, { container: resolved ?? null, children: /* @__PURE__ */ jsx14(
682
1994
  TooltipPrimitive.Content,
683
1995
  {
684
1996
  ref,
@@ -696,7 +2008,7 @@ var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, container,
696
2008
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
697
2009
 
698
2010
  // src/data-table/MatrxUuidCell.tsx
699
- import { jsx as jsx13, jsxs as jsxs7 } from "react/jsx-runtime";
2011
+ import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
700
2012
  var SHORT_LEN = 8;
701
2013
  function shortUuid(id, len = SHORT_LEN) {
702
2014
  return id.slice(0, len);
@@ -735,18 +2047,18 @@ function MatrxUuidCell({
735
2047
  }) {
736
2048
  const { resolveEntityDoors, ResourcePeek } = useMatrxDataTableHost();
737
2049
  const { notify, notice } = useTableNotice();
738
- const [copied, setCopied] = useState5(false);
739
- const [opening, setOpening] = useState5(false);
740
- const [peekOpen, setPeekOpen] = useState5(false);
741
- const timer = useRef(null);
742
- useEffect2(
2050
+ const [copied, setCopied] = useState6(false);
2051
+ const [opening, setOpening] = useState6(false);
2052
+ const [peekOpen, setPeekOpen] = useState6(false);
2053
+ const timer = useRef3(null);
2054
+ useEffect3(
743
2055
  () => () => {
744
2056
  if (timer.current) clearTimeout(timer.current);
745
2057
  },
746
2058
  []
747
2059
  );
748
2060
  if (!value) {
749
- return /* @__PURE__ */ jsx13("span", { className: "text-muted-foreground/40", children: "\u2014" });
2061
+ return /* @__PURE__ */ jsx15("span", { className: "text-muted-foreground/40", children: "\u2014" });
750
2062
  }
751
2063
  const doors = token ? resolveEntityDoors?.(token, value, href) ?? null : null;
752
2064
  const resolvedHref = doors?.href ?? href ?? null;
@@ -789,13 +2101,13 @@ function MatrxUuidCell({
789
2101
  };
790
2102
  const idNode = (() => {
791
2103
  if (forbidden) {
792
- return /* @__PURE__ */ jsxs7("span", { className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-muted-foreground/70 tabular-nums", children: [
793
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate", children: display }),
794
- /* @__PURE__ */ jsx13(ShieldOff, { className: "h-3 w-3 shrink-0 opacity-60", "aria-hidden": true })
2104
+ return /* @__PURE__ */ jsxs9("span", { className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-muted-foreground/70 tabular-nums", children: [
2105
+ /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate", children: display }),
2106
+ /* @__PURE__ */ jsx15(ShieldOff, { className: "h-3 w-3 shrink-0 opacity-60", "aria-hidden": true })
795
2107
  ] });
796
2108
  }
797
2109
  if (onOpen) {
798
- return /* @__PURE__ */ jsxs7(
2110
+ return /* @__PURE__ */ jsxs9(
799
2111
  "button",
800
2112
  {
801
2113
  type: "button",
@@ -804,14 +2116,14 @@ function MatrxUuidCell({
804
2116
  disabled: opening,
805
2117
  className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400 disabled:opacity-60",
806
2118
  children: [
807
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate tabular-nums", children: display }),
808
- /* @__PURE__ */ jsx13(PanelRight, { className: "h-3 w-3 shrink-0 opacity-50" })
2119
+ /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate tabular-nums", children: display }),
2120
+ /* @__PURE__ */ jsx15(PanelRight, { className: "h-3 w-3 shrink-0 opacity-50" })
809
2121
  ]
810
2122
  }
811
2123
  );
812
2124
  }
813
2125
  if (resolvedHref) {
814
- return /* @__PURE__ */ jsxs7(
2126
+ return /* @__PURE__ */ jsxs9(
815
2127
  "a",
816
2128
  {
817
2129
  href: resolvedHref,
@@ -821,15 +2133,15 @@ function MatrxUuidCell({
821
2133
  onClick: stop,
822
2134
  className: "inline-flex min-w-0 items-center gap-1 font-mono text-xs text-sky-600 hover:underline dark:text-sky-400",
823
2135
  children: [
824
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate tabular-nums", children: display }),
825
- /* @__PURE__ */ jsx13(ExternalLink, { className: "h-3 w-3 shrink-0 opacity-50" })
2136
+ /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate tabular-nums", children: display }),
2137
+ /* @__PURE__ */ jsx15(ExternalLink, { className: "h-3 w-3 shrink-0 opacity-50" })
826
2138
  ]
827
2139
  }
828
2140
  );
829
2141
  }
830
- return /* @__PURE__ */ jsx13("span", { className: "min-w-0 truncate font-mono text-xs text-muted-foreground tabular-nums select-all", children: display });
2142
+ return /* @__PURE__ */ jsx15("span", { className: "min-w-0 truncate font-mono text-xs text-muted-foreground tabular-nums select-all", children: display });
831
2143
  })();
832
- return /* @__PURE__ */ jsxs7(
2144
+ return /* @__PURE__ */ jsxs9(
833
2145
  "span",
834
2146
  {
835
2147
  "data-matrx-cell-control": true,
@@ -839,11 +2151,11 @@ function MatrxUuidCell({
839
2151
  ),
840
2152
  onClick: stop,
841
2153
  children: [
842
- /* @__PURE__ */ jsx13(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs7(Tooltip, { children: [
843
- /* @__PURE__ */ jsx13(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx13("span", { className: "inline-flex min-w-0 max-w-full items-center", children: idNode }) }),
844
- /* @__PURE__ */ jsx13(TooltipContent, { side: "top", className: "max-w-sm break-all font-mono", children: forbidden ? `No access \xB7 ${tip}` : tip })
2154
+ /* @__PURE__ */ jsx15(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs9(Tooltip, { children: [
2155
+ /* @__PURE__ */ jsx15(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx15("span", { className: "inline-flex min-w-0 max-w-full items-center", children: idNode }) }),
2156
+ /* @__PURE__ */ jsx15(TooltipContent, { side: "top", className: "max-w-sm break-all font-mono", children: forbidden ? `No access \xB7 ${tip}` : tip })
845
2157
  ] }) }),
846
- /* @__PURE__ */ jsx13(
2158
+ /* @__PURE__ */ jsx15(
847
2159
  "button",
848
2160
  {
849
2161
  type: "button",
@@ -854,10 +2166,10 @@ function MatrxUuidCell({
854
2166
  "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
855
2167
  copied && "text-primary"
856
2168
  ),
857
- children: copied ? /* @__PURE__ */ jsx13(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx13(Copy, { className: "h-3 w-3" })
2169
+ children: copied ? /* @__PURE__ */ jsx15(Check, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx15(Copy, { className: "h-3 w-3" })
858
2170
  }
859
2171
  ),
860
- canPeek ? /* @__PURE__ */ jsx13(
2172
+ canPeek ? /* @__PURE__ */ jsx15(
861
2173
  "button",
862
2174
  {
863
2175
  type: "button",
@@ -868,10 +2180,10 @@ function MatrxUuidCell({
868
2180
  "aria-label": `Quick look at ${label ?? "record"}`,
869
2181
  title: `Quick look at ${label ?? "record"}`,
870
2182
  className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
871
- children: /* @__PURE__ */ jsx13(Lightbulb, { className: "h-3 w-3" })
2183
+ children: /* @__PURE__ */ jsx15(Lightbulb, { className: "h-3 w-3" })
872
2184
  }
873
2185
  ) : null,
874
- onOpen && resolvedHref && !forbidden ? /* @__PURE__ */ jsx13(
2186
+ onOpen && resolvedHref && !forbidden ? /* @__PURE__ */ jsx15(
875
2187
  "a",
876
2188
  {
877
2189
  href: resolvedHref,
@@ -881,12 +2193,12 @@ function MatrxUuidCell({
881
2193
  "aria-label": `Open ${label ?? "record"} in a new tab`,
882
2194
  title: `Open ${label ?? "record"} in a new tab`,
883
2195
  className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded text-muted-foreground transition-colors hover:text-foreground focus:outline-none",
884
- children: /* @__PURE__ */ jsx13(ExternalLink, { className: "h-3 w-3" })
2196
+ children: /* @__PURE__ */ jsx15(ExternalLink, { className: "h-3 w-3" })
885
2197
  }
886
2198
  ) : null,
887
2199
  trailing,
888
2200
  notice,
889
- canPeek && peekOpen && ResourcePeek ? /* @__PURE__ */ jsx13(
2201
+ canPeek && peekOpen && ResourcePeek ? /* @__PURE__ */ jsx15(
890
2202
  ResourcePeek,
891
2203
  {
892
2204
  kind: doors?.peekKind ?? token ?? "record",
@@ -906,7 +2218,7 @@ function isUuidValue(value) {
906
2218
  }
907
2219
 
908
2220
  // src/data-table/DataRowInspector.tsx
909
- import { jsx as jsx14, jsxs as jsxs8 } from "react/jsx-runtime";
2221
+ import { jsx as jsx16, jsxs as jsxs10 } from "react/jsx-runtime";
910
2222
  var noFieldToken = () => null;
911
2223
  function formatValue(value) {
912
2224
  if (value == null) return "\u2014";
@@ -935,7 +2247,7 @@ function DataRowInspector({
935
2247
  tokenForField = noFieldToken
936
2248
  }) {
937
2249
  const entries = isPlainObject(row) ? Object.entries(row) : [["value", row]];
938
- const fieldButtons = (key, value) => fieldCopy ? /* @__PURE__ */ jsx14("div", { className: "absolute right-1 top-1 opacity-0 transition-opacity focus-within:opacity-100 group-hover/field:opacity-100", children: /* @__PURE__ */ jsx14(
2250
+ const fieldButtons = (key, value) => fieldCopy ? /* @__PURE__ */ jsx16("div", { className: "absolute right-1 top-1 opacity-0 transition-opacity focus-within:opacity-100 group-hover/field:opacity-100", children: /* @__PURE__ */ jsx16(
939
2251
  CopyButtons,
940
2252
  {
941
2253
  size: "xs",
@@ -950,21 +2262,21 @@ function DataRowInspector({
950
2262
  })
951
2263
  }
952
2264
  ) }) : null;
953
- return /* @__PURE__ */ jsxs8(
2265
+ return /* @__PURE__ */ jsxs10(
954
2266
  "div",
955
2267
  {
956
2268
  className: cn("flex h-full min-h-0 flex-col overflow-hidden", className),
957
2269
  children: [
958
- title ? /* @__PURE__ */ jsx14("div", { className: "shrink-0 border-b border-border px-3 py-2 text-sm font-medium", children: title }) : null,
959
- /* @__PURE__ */ jsx14("div", { className: "min-h-0 flex-1 overflow-auto p-3", children: /* @__PURE__ */ jsx14("dl", { className: "space-y-2", children: entries.map(([key, value]) => {
2270
+ title ? /* @__PURE__ */ jsx16("div", { className: "shrink-0 border-b border-border px-3 py-2 text-sm font-medium", children: title }) : null,
2271
+ /* @__PURE__ */ jsx16("div", { className: "min-h-0 flex-1 overflow-auto p-3", children: /* @__PURE__ */ jsx16("dl", { className: "space-y-2", children: entries.map(([key, value]) => {
960
2272
  if (isUuidValue(value)) {
961
- return /* @__PURE__ */ jsxs8(
2273
+ return /* @__PURE__ */ jsxs10(
962
2274
  "div",
963
2275
  {
964
2276
  className: "group/field relative grid grid-cols-[minmax(7rem,9rem)_1fr] gap-2 rounded-md border border-border/60 bg-muted/20 px-2.5 py-2",
965
2277
  children: [
966
- /* @__PURE__ */ jsx14("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
967
- /* @__PURE__ */ jsx14("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx14(
2278
+ /* @__PURE__ */ jsx16("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
2279
+ /* @__PURE__ */ jsx16("dd", { className: "min-w-0", children: /* @__PURE__ */ jsx16(
968
2280
  MatrxUuidCell,
969
2281
  {
970
2282
  value,
@@ -979,13 +2291,13 @@ function DataRowInspector({
979
2291
  }
980
2292
  const formatted = formatValue(value);
981
2293
  const multiline = formatted.includes("\n") || formatted.length > 80;
982
- return /* @__PURE__ */ jsxs8(
2294
+ return /* @__PURE__ */ jsxs10(
983
2295
  "div",
984
2296
  {
985
2297
  className: "group/field relative grid grid-cols-[minmax(7rem,9rem)_1fr] gap-2 rounded-md border border-border/60 bg-muted/20 py-2 pl-2.5 pr-12",
986
2298
  children: [
987
- /* @__PURE__ */ jsx14("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
988
- /* @__PURE__ */ jsx14(
2299
+ /* @__PURE__ */ jsx16("dt", { className: "truncate text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: key }),
2300
+ /* @__PURE__ */ jsx16(
989
2301
  "dd",
990
2302
  {
991
2303
  className: cn(
@@ -1007,7 +2319,7 @@ function DataRowInspector({
1007
2319
  }
1008
2320
 
1009
2321
  // src/data-table/DataRowWindow.tsx
1010
- import { jsx as jsx15, jsxs as jsxs9 } from "react/jsx-runtime";
2322
+ import { jsx as jsx17, jsxs as jsxs11 } from "react/jsx-runtime";
1011
2323
  function DataRowWindow({
1012
2324
  isOpen,
1013
2325
  onClose,
@@ -1027,15 +2339,15 @@ function DataRowWindow({
1027
2339
  const hasEdit = editContent != null;
1028
2340
  const hasTabs = hasEdit && children == null;
1029
2341
  const initialTab = defaultTab ?? (hasEdit ? "edit" : "view");
1030
- const [tab, setTab] = useState6(initialTab);
1031
- useEffect3(() => {
2342
+ const [tab, setTab] = useState7(initialTab);
2343
+ useEffect4(() => {
1032
2344
  if (!isOpen) return;
1033
2345
  setTab(defaultTab ?? (hasEdit ? "edit" : "view"));
1034
2346
  }, [isOpen, windowId, hasEdit, defaultTab]);
1035
2347
  if (!isOpen) return null;
1036
- const viewBody = viewContent ?? /* @__PURE__ */ jsx15(DataRowInspector, { row });
1037
- const body = children ?? /* @__PURE__ */ jsx15("div", { className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden", children: hasTabs ? tab === "edit" ? /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: editContent }) : /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) : /* @__PURE__ */ jsx15("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) });
1038
- return /* @__PURE__ */ jsx15(
2348
+ const viewBody = viewContent ?? /* @__PURE__ */ jsx17(DataRowInspector, { row });
2349
+ const body = children ?? /* @__PURE__ */ jsx17("div", { className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden", children: hasTabs ? tab === "edit" ? /* @__PURE__ */ jsx17("div", { className: "min-h-0 flex-1 overflow-y-auto", children: editContent }) : /* @__PURE__ */ jsx17("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) : /* @__PURE__ */ jsx17("div", { className: "min-h-0 flex-1 overflow-y-auto", children: viewBody }) });
2350
+ return /* @__PURE__ */ jsx17(
1039
2351
  WindowPanel,
1040
2352
  {
1041
2353
  id: windowId,
@@ -1049,26 +2361,26 @@ function DataRowWindow({
1049
2361
  bodyClassName: "flex min-h-0 flex-1 flex-col overflow-hidden p-0",
1050
2362
  sidebarDefaultSize: 140,
1051
2363
  sidebarMinSize: 110,
1052
- sidebar: hasTabs ? /* @__PURE__ */ jsxs9(
2364
+ sidebar: hasTabs ? /* @__PURE__ */ jsxs11(
1053
2365
  "nav",
1054
2366
  {
1055
2367
  "aria-label": "Row window tabs",
1056
2368
  className: "flex h-full flex-col gap-0.5 overflow-y-auto p-1.5",
1057
2369
  children: [
1058
- /* @__PURE__ */ jsx15(
2370
+ /* @__PURE__ */ jsx17(
1059
2371
  TabButton,
1060
2372
  {
1061
2373
  active: tab === "view",
1062
- icon: /* @__PURE__ */ jsx15(Eye, { className: "h-4 w-4 shrink-0" }),
2374
+ icon: /* @__PURE__ */ jsx17(Eye, { className: "h-4 w-4 shrink-0" }),
1063
2375
  label: "View",
1064
2376
  onClick: () => setTab("view")
1065
2377
  }
1066
2378
  ),
1067
- /* @__PURE__ */ jsx15(
2379
+ /* @__PURE__ */ jsx17(
1068
2380
  TabButton,
1069
2381
  {
1070
2382
  active: tab === "edit",
1071
- icon: /* @__PURE__ */ jsx15(Pencil, { className: "h-4 w-4 shrink-0" }),
2383
+ icon: /* @__PURE__ */ jsx17(Pencil, { className: "h-4 w-4 shrink-0" }),
1072
2384
  label: "Edit",
1073
2385
  onClick: () => setTab("edit")
1074
2386
  }
@@ -1086,7 +2398,7 @@ function TabButton({
1086
2398
  label,
1087
2399
  onClick
1088
2400
  }) {
1089
- return /* @__PURE__ */ jsxs9(
2401
+ return /* @__PURE__ */ jsxs11(
1090
2402
  "button",
1091
2403
  {
1092
2404
  type: "button",
@@ -1099,7 +2411,7 @@ function TabButton({
1099
2411
  ),
1100
2412
  children: [
1101
2413
  icon,
1102
- /* @__PURE__ */ jsx15("span", { className: "min-w-0 flex-1 truncate", children: label })
2414
+ /* @__PURE__ */ jsx17("span", { className: "min-w-0 flex-1 truncate", children: label })
1103
2415
  ]
1104
2416
  }
1105
2417
  );