@ai-matrx/design-system 0.18.8 → 0.18.10

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 (58) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/content-transfer.cjs +163 -202
  3. package/dist/content-transfer.cjs.map +1 -1
  4. package/dist/content-transfer.css +43 -30
  5. package/dist/content-transfer.d.cts +4 -0
  6. package/dist/content-transfer.d.ts +4 -0
  7. package/dist/content-transfer.js +225 -264
  8. package/dist/content-transfer.js.map +1 -1
  9. package/dist/data-table/copy-helpers.d.cts +1 -1
  10. package/dist/data-table/copy-helpers.d.ts +1 -1
  11. package/dist/data-table/data-row-inspector.cjs +163 -202
  12. package/dist/data-table/data-row-inspector.cjs.map +1 -1
  13. package/dist/data-table/data-row-inspector.js +245 -284
  14. package/dist/data-table/data-row-inspector.js.map +1 -1
  15. package/dist/data-table/data-row-window.cjs +163 -202
  16. package/dist/data-table/data-row-window.cjs.map +1 -1
  17. package/dist/data-table/data-row-window.js +245 -284
  18. package/dist/data-table/data-row-window.js.map +1 -1
  19. package/dist/data-table/filter-engine.d.cts +1 -1
  20. package/dist/data-table/filter-engine.d.ts +1 -1
  21. package/dist/data-table/host.cjs.map +1 -1
  22. package/dist/data-table/host.d.cts +2 -0
  23. package/dist/data-table/host.d.ts +2 -0
  24. package/dist/data-table/host.js.map +1 -1
  25. package/dist/data-table/index.cjs +1192 -751
  26. package/dist/data-table/index.cjs.map +1 -1
  27. package/dist/data-table/index.d.cts +24 -3
  28. package/dist/data-table/index.d.ts +24 -3
  29. package/dist/data-table/index.js +1264 -823
  30. package/dist/data-table/index.js.map +1 -1
  31. package/dist/data-table/infer-filter.d.cts +1 -1
  32. package/dist/data-table/infer-filter.d.ts +1 -1
  33. package/dist/data-table/layered-filters.d.cts +1 -1
  34. package/dist/data-table/layered-filters.d.ts +1 -1
  35. package/dist/data-table/pagination.cjs +5 -5
  36. package/dist/data-table/pagination.cjs.map +1 -1
  37. package/dist/data-table/pagination.js +5 -5
  38. package/dist/data-table/pagination.js.map +1 -1
  39. package/dist/data-table/query-control.d.cts +1 -1
  40. package/dist/data-table/query-control.d.ts +1 -1
  41. package/dist/data-table/types.cjs.map +1 -1
  42. package/dist/data-table/types.d.cts +1 -1
  43. package/dist/data-table/types.d.ts +1 -1
  44. package/dist/data-table/url-state.d.cts +1 -1
  45. package/dist/data-table/url-state.d.ts +1 -1
  46. package/dist/data-table/uuid-cell.cjs.map +1 -1
  47. package/dist/data-table/uuid-cell.js.map +1 -1
  48. package/dist/data-table/xlsx.d.cts +1 -1
  49. package/dist/data-table/xlsx.d.ts +1 -1
  50. package/dist/index.cjs +5 -5
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.js +5 -5
  53. package/dist/index.js.map +1 -1
  54. package/dist/styles.css +8 -1
  55. package/dist/tokens.css +2 -0
  56. package/dist/{types-cH8oLqUx.d.ts → types-C2xN6W_q.d.ts} +46 -3
  57. package/dist/{types-0CNCWvdK.d.cts → types-D_tpuk6m.d.cts} +46 -3
  58. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.18.10 — 2026-09-12
4
+
5
+ Adds the compact Alchemy copy/download format palette and hardens the shared
6
+ data-table toolbar, saved views, mobile editing, drag projection, async error
7
+ handling, and duplicate view validation. Select popups now contain and wrap
8
+ long option labels on narrow viewports instead of clipping them off-screen.
9
+
10
+ ### Consumer action
11
+
12
+ Update to 0.18.10. No host configuration changes are required.
13
+
14
+ ## 0.18.9 — 2026-09-12
15
+
16
+ Data tables and mobile-card scrollers now contain horizontal overscroll while
17
+ leaving vertical wheel and touch chaining to the browser. This removes the
18
+ manual wheel reroute that could strand nested page scrolling at a table edge.
19
+
20
+ ### Consumer action
21
+
22
+ Update to 0.18.9. No host configuration changes are required.
23
+
3
24
  ## 0.18.8 — 2026-09-12
4
25
 
5
26
  Table and selected-row export menus now use one canonical typed source built
@@ -181,48 +181,6 @@ function XIcon(props) {
181
181
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "m6 6 12 12" })
182
182
  ] });
183
183
  }
184
- function radixProps(props) {
185
- return {
186
- xmlns: "http://www.w3.org/2000/svg",
187
- viewBox: "0 0 15 15",
188
- width: 15,
189
- height: 15,
190
- fill: "none",
191
- "aria-hidden": true,
192
- ...props
193
- };
194
- }
195
- function CheckIcon(props) {
196
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
197
- "path",
198
- {
199
- 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",
200
- fill: "currentColor",
201
- fillRule: "evenodd",
202
- clipRule: "evenodd"
203
- }
204
- ) });
205
- }
206
- function DotFilledIcon(props) {
207
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
208
- "path",
209
- {
210
- 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",
211
- fill: "currentColor"
212
- }
213
- ) });
214
- }
215
- function RadixChevronRightIcon(props) {
216
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { ...radixProps(props), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
217
- "path",
218
- {
219
- 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",
220
- fill: "currentColor",
221
- fillRule: "evenodd",
222
- clipRule: "evenodd"
223
- }
224
- ) });
225
- }
226
184
 
227
185
  // src/portal-container.tsx
228
186
  var React2 = __toESM(require("react"), 1);
@@ -486,132 +444,45 @@ var DialogDescription = React5.forwardRef(({ className, ...props }, ref) => /* @
486
444
  ));
487
445
  DialogDescription.displayName = DialogPrimitive.Description.displayName;
488
446
 
489
- // src/dropdown-menu.tsx
490
- var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"), 1);
447
+ // src/popover.tsx
448
+ var PopoverPrimitive = __toESM(require("@radix-ui/react-popover"), 1);
491
449
  var React6 = __toESM(require("react"), 1);
492
450
  var import_jsx_runtime6 = require("react/jsx-runtime");
493
- var DropdownMenu = DropdownMenuPrimitive.Root;
494
- var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
495
- 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";
496
- 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";
497
- 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";
498
- var DropdownMenuSubTrigger = React6.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
499
- DropdownMenuPrimitive.SubTrigger,
500
- {
501
- ref,
502
- "data-slot": "dropdown-menu-sub-trigger",
503
- className: cn(
504
- "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",
505
- inset && "pl-8",
506
- className
507
- ),
508
- ...props,
509
- children: [
510
- children,
511
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RadixChevronRightIcon, { className: "ml-auto h-4 w-4" })
512
- ]
513
- }
514
- ));
515
- DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
516
- var DropdownMenuSubContent = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
517
- DropdownMenuPrimitive.SubContent,
518
- {
519
- ref,
520
- "data-slot": "dropdown-menu-sub-content",
521
- className: cn(MENU_SURFACE_CLASS, "shadow-lg", MOTION_POPPER, className),
522
- ...props
523
- }
524
- ));
525
- DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
526
- var DropdownMenuContent = React6.forwardRef(({ className, sideOffset = 4, container, ...props }, ref) => {
527
- const portalContainer = usePortalContainer(container);
528
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DropdownMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
529
- DropdownMenuPrimitive.Content,
530
- {
531
- ref,
532
- "data-slot": "dropdown-menu-content",
533
- sideOffset,
534
- className: cn(
535
- MENU_SURFACE_CLASS,
536
- "shadow-md",
537
- MOTION_POPPER,
538
- className
539
- ),
540
- ...props
541
- }
542
- ) });
543
- });
544
- DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
545
- var DropdownMenuItem = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
546
- DropdownMenuPrimitive.Item,
547
- {
548
- ref,
549
- "data-slot": "dropdown-menu-item",
550
- className: cn(MENU_ITEM_CLASS, inset && "pl-8", className),
551
- ...props
552
- }
553
- ));
554
- DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
555
- var DropdownMenuCheckboxItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
556
- DropdownMenuPrimitive.CheckboxItem,
557
- {
558
- ref,
559
- "data-slot": "dropdown-menu-checkbox-item",
560
- className: cn(MENU_INDICATOR_ITEM_CLASS, className),
561
- ...props,
562
- children: [
563
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(CheckIcon, { className: "h-4 w-4" }) }) }),
564
- children
565
- ]
566
- }
567
- ));
568
- DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
569
- var DropdownMenuRadioItem = React6.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
570
- DropdownMenuPrimitive.RadioItem,
571
- {
572
- ref,
573
- "data-slot": "dropdown-menu-radio-item",
574
- className: cn(MENU_INDICATOR_ITEM_CLASS, className),
575
- ...props,
576
- children: [
577
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DotFilledIcon, { className: "h-4 w-4 fill-current" }) }) }),
578
- children
579
- ]
580
- }
581
- ));
582
- DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
583
- var DropdownMenuLabel = React6.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
584
- DropdownMenuPrimitive.Label,
585
- {
586
- ref,
587
- "data-slot": "dropdown-menu-label",
588
- className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
589
- ...props
590
- }
591
- ));
592
- DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
593
- var DropdownMenuSeparator = React6.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
594
- DropdownMenuPrimitive.Separator,
595
- {
596
- ref,
597
- "data-slot": "dropdown-menu-separator",
598
- className: cn("-mx-1 my-1 h-px bg-muted", className),
599
- ...props
600
- }
601
- ));
602
- DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
603
- var DropdownMenuShortcut = ({
604
- className,
605
- ...props
606
- }) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
607
- "span",
608
- {
609
- "data-slot": "dropdown-menu-shortcut",
610
- className: cn("ml-auto text-xs tracking-widest opacity-60", className),
451
+ var Popover = PopoverPrimitive.Root;
452
+ var PopoverTrigger = PopoverPrimitive.Trigger;
453
+ var PopoverContent = React6.forwardRef(
454
+ ({
455
+ className,
456
+ align = "center",
457
+ sideOffset = 4,
458
+ container,
459
+ animated = true,
611
460
  ...props
461
+ }, ref) => {
462
+ const portalContainer = usePortalContainer(container);
463
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(PopoverPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
464
+ PopoverPrimitive.Content,
465
+ {
466
+ ref,
467
+ align,
468
+ sideOffset,
469
+ className: cn(
470
+ // Cap to the viewport space Radix measured and scroll — a popover taller
471
+ // than the screen must never trap the user with unreachable content.
472
+ // z must EQUAL the dialog layer (z-[10000]), never exceed it: with equal
473
+ // z, DOM portal order decides, so a dialog opened FROM a popover stacks
474
+ // above it, and a popover opened from a dialog still stacks above the
475
+ // dialog. z-[10001] buried dialogs behind fullscreen popovers.
476
+ "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",
477
+ animated && MOTION_POPPER,
478
+ className
479
+ ),
480
+ ...props
481
+ }
482
+ ) });
612
483
  }
613
484
  );
614
- DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
485
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
615
486
 
616
487
  // src/content-transfer/icon.ts
617
488
  var ALCHEMY_GLYPH_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 5.5h8v11h-8z"/><path d="M8.5 9.5h8v9h-8z"/><path d="m18.5 3 .65 1.85L21 5.5l-1.85.65L18.5 8l-.65-1.85L16 5.5l1.85-.65z"/></svg>';
@@ -790,6 +661,7 @@ function MatrxCopyMenuSession(props) {
790
661
  label: item.label,
791
662
  supports: () => true,
792
663
  ...item.build && !item.onSelect ? {
664
+ placement: "download",
793
665
  source: make(
794
666
  `export:${item.id}`,
795
667
  async () => {
@@ -870,10 +742,11 @@ function MatrxCopyMenuSession(props) {
870
742
  label: props.label,
871
743
  variants,
872
744
  actions,
745
+ sourceFormats: Boolean(source),
873
746
  hide: [
874
747
  ...!copyVisible || !source ? ["copy"] : [],
875
748
  ...!aiVisible || !source ? ["prepare"] : [],
876
- ...!exportVisible || !source ? ["download"] : []
749
+ ...!exportVisible ? ["download"] : []
877
750
  ],
878
751
  formatSources: {
879
752
  ...human ? { plain: human, markdown: humanMarkdown, html: humanMarkdown } : {},
@@ -1232,6 +1105,53 @@ function MenuPresentation({ label, hint, icon: Icon }) {
1232
1105
  ] })
1233
1106
  ] });
1234
1107
  }
1108
+ function PaletteGlyph({ format, ai = false, file = false }) {
1109
+ if (ai) return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1110
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 5a3 3 0 1 0-5.997.125A4 4 0 0 0 3.5 10.9a4 4 0 0 0 .556 6.588A4 4 0 0 0 12 19" }),
1111
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 5a3 3 0 1 1 5.997.125 4 4 0 0 1 2.503 5.775 4 4 0 0 1-.556 6.588A4 4 0 0 1 12 19" }),
1112
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M12 5v14M8 9h1M15 9h1M8 15h1M15 15h1" })
1113
+ ] });
1114
+ if (file) return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
1115
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M6 3h8l4 4v14H6z" }),
1116
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 3v5h5M9 13h6M9 17h6" })
1117
+ ] });
1118
+ const path = format === "json" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1119
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M9 4C7 4 7 7 5 7v3c2 0 2 1 2 2s0 2-2 2v3c2 0 2 3 4 3" }),
1120
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M15 4c2 0 2 3 4 3v3c-2 0-2 1-2 2s0 2 2 2v3c-2 0-2 3-4 3" })
1121
+ ] }) : format === "compact-json" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1122
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "m14 10 7-7" }),
1123
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M20 10h-6V4" }),
1124
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "m3 21 7-7" }),
1125
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M4 14h6v6" })
1126
+ ] }) : format === "csv" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1127
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("rect", { x: "4", y: "4", width: "16", height: "16", rx: "1" }),
1128
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M4 10h16M10 4v16" })
1129
+ ] }) : format === "tsv" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1130
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
1131
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2v6h6M8 13h2M14 13h2M8 17h2M14 17h2" })
1132
+ ] }) : format === "xlsx" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1133
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
1134
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2v6h6M8 13h8M8 17h8M12 11v8" })
1135
+ ] }) : format === "markdown" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1136
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5z" }),
1137
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M14 2v6h6M10 13l-2 2 2 2M14 17l2-2-2-2" })
1138
+ ] }) : format === "html" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M15 6h6M3 6h7M3 12h18M3 18h12" }) }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("path", { d: "M4 7V4h16v3M9 20h6M12 4v16" }) });
1139
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("svg", { className: "matrx-alchemy-palette-glyph", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: path });
1140
+ }
1141
+ function PaletteRows({ title, items, onRun, extra, ai = false }) {
1142
+ if (!items.length && !extra) return null;
1143
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("section", { className: "matrx-alchemy-palette-section", children: [
1144
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h3", { children: title }),
1145
+ items.map((item) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", className: cn("matrx-alchemy-palette-row", ai && "matrx-alchemy-palette-row-ai"), "aria-label": item.ariaLabel, onClick: () => onRun?.(item), children: [
1146
+ item.icon ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(item.icon, { className: "matrx-alchemy-menu-icon" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteGlyph, { ai }),
1147
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
1148
+ item.copyLabel ?? item.label,
1149
+ item.hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("small", { className: "matrx-alchemy-menu-hint", children: item.hint }) : null
1150
+ ] })
1151
+ ] }, item.id)),
1152
+ extra
1153
+ ] });
1154
+ }
1235
1155
  var LABEL = { plain: "Plain text", markdown: "Markdown", html: "Formatted", json: "JSON", "compact-json": "Compact JSON", csv: "CSV", tsv: "Google Sheets", xlsx: "Excel workbook" };
1236
1156
  var BUILT_INS = Object.freeze(Object.keys(LABEL).map((id) => ({ id })));
1237
1157
  var COPIES = ["plain", "markdown", "html", "json", "compact-json", "csv", "tsv"];
@@ -1320,7 +1240,7 @@ function ContentTransferMenu(props) {
1320
1240
  const identityKey = props.capabilities?.identityKey ?? inherited.identityKey;
1321
1241
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ContentTransferMenuSession, { ...props }, identityKey ?? "anonymous");
1322
1242
  }
1323
- function ContentTransferMenuSession({ source, surface, label, projection, sections, preferences, table, formatSources, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef }) {
1243
+ function ContentTransferMenuSession({ source, surface, label, projection, sections, preferences, table, formatSources, sourceFormats = true, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef }) {
1324
1244
  const triggerRef = React8.useRef(null);
1325
1245
  const inherited = React8.useContext(CapabilityContext);
1326
1246
  const nearestSurface = React8.useContext(SurfaceContext);
@@ -1333,6 +1253,8 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
1333
1253
  const workspaceCaptures = React8.useRef(/* @__PURE__ */ new Map());
1334
1254
  const externalWorkspaceKeys = React8.useRef(/* @__PURE__ */ new Set());
1335
1255
  const [notice, setNotice] = React8.useState(null);
1256
+ const [paletteOpen, setPaletteOpen] = React8.useState(false);
1257
+ const [mode, setMode] = React8.useState("copy");
1336
1258
  const scopes = React8.useMemo(() => table?.availableScopes ?? ["target"], [table]);
1337
1259
  const initialScope = table?.initialScope ?? "target";
1338
1260
  if (!scopes.includes(initialScope)) throw new Error("Alchemy initialScope must be included in availableScopes.");
@@ -1584,48 +1506,87 @@ function ContentTransferMenuSession({ source, surface, label, projection, sectio
1584
1506
  }, [captureOne, capturePrimary, fence, initialScope, preferences, providerPreferences, report]);
1585
1507
  if (!enabled) return null;
1586
1508
  const payload = menuCapture?.snapshot.payload ?? (source && "kind" in source ? source : void 0);
1587
- const quickVariants = (section) => variants.filter((v) => v.copyLabel && (v.section ?? "ai") === section).map((v) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { "aria-label": v.ariaLabel, onSelect: () => void transfer("markdown", false, v), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MenuPresentation, { ...v, label: v.copyLabel }) }, `copy:${v.id}`));
1588
- const visibleCopies = COPIES.filter((f) => Boolean(formatSources?.[f]) || !["csv", "tsv"].includes(f) || payload?.kind === "rows");
1589
- const visibleDownloads = DOWNLOADS.filter((f) => f !== "xlsx" && Boolean(formatSources?.[f]) || !["csv", "tsv", "xlsx"].includes(f) || payload?.kind === "rows");
1590
- const visibleAdapters = menuCapture ? adapters.filter((a) => a.supports(menuCapture.snapshot.payload)) : [];
1509
+ const quickVariants = (section) => variants.filter((v) => v.copyLabel && (v.section ?? "ai") === section);
1510
+ const visibleCopies = sourceFormats ? COPIES.filter((f) => Boolean(formatSources?.[f]) || !["csv", "tsv"].includes(f) || payload?.kind === "rows") : [];
1511
+ const visibleDownloads = sourceFormats ? DOWNLOADS.filter((f) => f !== "xlsx" && Boolean(formatSources?.[f]) || !["csv", "tsv", "xlsx"].includes(f) || payload?.kind === "rows") : [];
1512
+ const visibleAdapters = sourceFormats && menuCapture ? adapters.filter((a) => a.supports(menuCapture.snapshot.payload)) : [];
1591
1513
  const visibleActions = menuCapture ? registeredActions.filter((a) => a.supports(menuCapture.snapshot)) : [];
1514
+ const downloadActions = visibleActions.filter((action) => action.placement === "download");
1515
+ const ordinaryActions = visibleActions.filter((action) => action.placement !== "download");
1516
+ const copyAvailable = !hide.includes("copy") && visibleCopies.length + visibleAdapters.length + quickVariants("copy").length > 0;
1517
+ const downloadAvailable = !hide.includes("download") && visibleDownloads.length + visibleAdapters.length + downloadActions.length > 0;
1518
+ const prepareAvailable = !hide.includes("prepare") && (quickVariants("ai").length > 0 || Boolean(primary) || Boolean(nearestWorkspaceKey) || variants.length > 0);
1519
+ const resolvedMode = mode === "copy" && !copyAvailable ? "download" : mode === "download" && !downloadAvailable ? "copy" : mode;
1520
+ const closePalette = () => setPaletteOpen(false);
1521
+ const tile = (id, label2, onClick, options = {}) => {
1522
+ const presentation = options.action;
1523
+ const Icon = presentation?.icon;
1524
+ const fallbackLabel = options.file && !/^download\b/i.test(label2) ? `Download ${label2}` : label2;
1525
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-tile", "aria-label": presentation?.ariaLabel ?? (presentation ? fallbackLabel : options.ariaLabel ?? `${resolvedMode === "copy" ? "Copy" : "Download"} ${label2}`), onClick: () => {
1526
+ closePalette();
1527
+ onClick();
1528
+ }, children: [
1529
+ Icon ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Icon, { className: "matrx-alchemy-menu-icon" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteGlyph, { ...!options.custom ? { format: id } : {}, ...options.ai ? { ai: true } : {}, ...options.file ? { file: true } : {} }),
1530
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { children: [
1531
+ label2,
1532
+ presentation?.hint ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("small", { className: "matrx-alchemy-menu-hint", children: presentation.hint }) : null
1533
+ ] })
1534
+ ] }, id);
1535
+ };
1592
1536
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: cn("matrx-alchemy", className), onClick: stopPropagation ? (event) => event.stopPropagation() : void 0, children: [
1593
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropdownMenu, { onOpenChange: (open) => {
1594
- if (open) void preflight();
1537
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Popover, { open: paletteOpen, onOpenChange: (open) => {
1538
+ setPaletteOpen(open);
1539
+ if (open) {
1540
+ setMode("copy");
1541
+ void preflight();
1542
+ }
1595
1543
  }, children: [
1596
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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__ */ (0, import_jsx_runtime8.jsx)(AlchemyGlyph, {}) }) }),
1597
- /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropdownMenuContent, { align: "end", className: "matrx-alchemy-menu", children: [
1598
- menuStatus === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { disabled: true, children: "Inspecting content\u2026" }) : menuStatus !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { disabled: true, children: menuStatus }) : null,
1599
- !hide.includes("copy") ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1600
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuLabel, { children: "Copy" }),
1601
- quickVariants("copy"),
1602
- visibleCopies.map((format) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { onSelect: () => void transfer(format, false), children: format === "plain" ? "Copy" : `Copy ${LABEL[format]}` }, format)),
1603
- visibleAdapters.map((adapter) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropdownMenuItem, { onSelect: () => void transfer(adapter, false), children: [
1604
- "Copy ",
1605
- adapter.label
1606
- ] }, adapter.id))
1544
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("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__ */ (0, import_jsx_runtime8.jsx)(AlchemyGlyph, {}) }) }),
1545
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(PopoverContent, { align: "end", className: "matrx-alchemy-menu matrx-alchemy-palette", "aria-label": "Alchemy output options", onCloseAutoFocus: () => triggerRef.current?.focus(), children: [
1546
+ copyAvailable && downloadAvailable ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "matrx-alchemy-mode", "aria-label": "Output action", children: [
1547
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-label": "Copy mode", "aria-pressed": resolvedMode === "copy", onClick: () => setMode("copy"), children: "Copy" }),
1548
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", "aria-label": "Download mode", "aria-pressed": resolvedMode === "download", onClick: () => setMode("download"), children: "Download" })
1607
1549
  ] }) : null,
1608
- !hide.includes("prepare") ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1609
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuSeparator, {}),
1610
- quickVariants("ai"),
1611
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { onSelect: () => void openWorkspace(), children: "Prepare for AI\u2026" }),
1612
- nearestWorkspaceKey && nearestSurface ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { onSelect: () => void openWorkspace(nearestWorkspaceKey, { source: nearestSurface, label: nearestSurface.surfaceName }), children: "Prepare this surface\u2026" }) : null,
1613
- variants.map((variant) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { onSelect: () => void openWorkspace(variantWorkspaceKeys.get(variant.id), variant), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MenuPresentation, { ...variant, label: `Prepare ${variant.label}\u2026` }) }, variant.id))
1614
- ] }) : null,
1615
- !hide.includes("download") ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1616
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuSeparator, {}),
1617
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuLabel, { children: "Download" }),
1618
- visibleDownloads.map((format) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropdownMenuItem, { onSelect: () => void transfer(format, true), children: [
1619
- "Download ",
1620
- LABEL[format]
1621
- ] }, format)),
1622
- visibleAdapters.map((adapter) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(DropdownMenuItem, { onSelect: () => void transfer(adapter, true), children: [
1623
- "Download ",
1624
- adapter.label
1625
- ] }, `download:${adapter.id}`))
1550
+ menuStatus === "loading" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "matrx-alchemy-palette-status", children: "Inspecting content\u2026" }) : menuStatus !== "idle" ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "matrx-alchemy-error", children: menuStatus }) : null,
1551
+ resolvedMode === "copy" && copyAvailable ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1552
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "matrx-alchemy-palette-grid", children: [
1553
+ visibleCopies.map((format) => tile(format, { plain: "Text", markdown: "Markdown", html: "Formatted", json: "JSON", "compact-json": "Compact", csv: "CSV", tsv: "Sheets" }[format], () => void transfer(format, false), { ariaLabel: `Copy ${LABEL[format]}` })),
1554
+ visibleAdapters.map((adapter) => tile(adapter.id, adapter.label, () => void transfer(adapter, false), { custom: true }))
1555
+ ] }),
1556
+ quickVariants("copy").length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteRows, { title: "Quick copy", items: quickVariants("copy"), onRun: (variant) => {
1557
+ closePalette();
1558
+ void transfer("markdown", false, variant);
1559
+ } }) : null
1626
1560
  ] }) : null,
1627
- visibleActions.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuSeparator, {}) : null,
1628
- visibleActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DropdownMenuItem, { "aria-label": action.ariaLabel, onSelect: () => void runAction(action), children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MenuPresentation, { ...action, label: action.label }) }, action.id))
1561
+ resolvedMode === "download" && downloadAvailable ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_jsx_runtime8.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "matrx-alchemy-palette-grid", children: [
1562
+ visibleDownloads.map((format) => tile(format, { plain: "Text", markdown: "Markdown", html: "Formatted", json: "JSON", "compact-json": "Compact", csv: "CSV", tsv: "Sheets", xlsx: "Excel" }[format], () => void transfer(format, true), { ariaLabel: `Download ${LABEL[format]}` })),
1563
+ visibleAdapters.map((adapter) => tile(adapter.id, adapter.label, () => void transfer(adapter, true), { custom: true })),
1564
+ downloadActions.map((action) => tile(action.id, action.label, () => void runAction(action), { custom: true, action, file: true }))
1565
+ ] }) }) : null,
1566
+ prepareAvailable ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteRows, { title: "Prepare", ai: true, items: quickVariants("ai"), onRun: (variant) => {
1567
+ closePalette();
1568
+ void transfer("markdown", false, variant);
1569
+ }, extra: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => {
1570
+ closePalette();
1571
+ void openWorkspace();
1572
+ }, children: [
1573
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteGlyph, { ai: true }),
1574
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Prepare for AI\u2026" })
1575
+ ] }) }) : null,
1576
+ prepareAvailable && (nearestWorkspaceKey || variants.length) ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteRows, { title: "Context", items: [], extra: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
1577
+ nearestWorkspaceKey && nearestSurface ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", onClick: () => {
1578
+ closePalette();
1579
+ void openWorkspace(nearestWorkspaceKey, { source: nearestSurface, label: nearestSurface.surfaceName });
1580
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { children: "Prepare this surface\u2026" }) }) : null,
1581
+ variants.map((variant) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", "aria-label": variant.ariaLabel, onClick: () => {
1582
+ closePalette();
1583
+ void openWorkspace(variantWorkspaceKeys.get(variant.id), variant);
1584
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MenuPresentation, { ...variant, label: `Prepare ${variant.label}\u2026` }) }, variant.id))
1585
+ ] }) }) : null,
1586
+ ordinaryActions.length ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(PaletteRows, { title: "Actions", items: [], extra: ordinaryActions.map((action) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("button", { type: "button", className: "matrx-alchemy-palette-row", "aria-label": action.ariaLabel, onClick: () => {
1587
+ closePalette();
1588
+ void runAction(action);
1589
+ }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MenuPresentation, { ...action, label: action.label }) }, action.id)) }) : null
1629
1590
  ] })
1630
1591
  ] }),
1631
1592
  notice ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "matrx-alchemy-notice", role: "status", children: notice }) : null,