@ai-matrx/design-system 0.18.1 → 0.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/data-table/copy-types.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\n\nexport type CopyResolvable<T> = T | (() => T);\nexport type CopyScalar = string | number | boolean | null | undefined;\nexport interface AgentPayloadInput { kind: string; location: string; description: string; data: unknown; summary?: string; context?: Record<string, CopyScalar>; attributes?: Record<string, CopyScalar>; }\nexport type AiIcon = ComponentType<{ className?: string }>;\nexport interface AiVariant { id: string; label: string; hint?: string; icon?: AiIcon; section?: \"copy\" | \"ai\"; ariaLabel?: string; successMessage?: string; build?: () => AgentPayloadInput | string | Promise<AgentPayloadInput | string>; onSelect?: () => void; modal?: (props: { open: boolean; onOpenChange: (open: boolean) => void }) => ReactNode; }\nexport type AiCustomOption =\n | { kind: \"toggle\"; key: string; label: string; hint?: string; default: boolean }\n | { kind: \"preset\"; key: string; label: string; options: { label: string; value: number }[]; default: number }\n | { kind: \"slider\"; key: string; label: string; hint?: string; stops: number[]; default: number; formatValue: (value: number) => string }\n | { kind: \"number\"; key: string; label: string; hint?: string; min?: number; step?: number; presets?: { label: string; value: number }[]; default: number };\nexport type AiOptionValues = Record<string, boolean | number>;\nexport interface AiCustomSource { label?: string; hint?: string; options: AiCustomOption[]; build: (options: AiOptionValues) => { text: string; meta?: Record<string, string | number | boolean | undefined> }; wrap: (text: string, options: AiOptionValues, meta?: Record<string, string | number | boolean | undefined>) => AgentPayloadInput; dialogTitle?: string; dialogDescription?: string; }\nexport interface ExportItem { id: string; label: string; build?: () => { content: BlobPart; extension: string; mime: string }; onSelect?: () => void | Promise<void>; }\nexport interface CopyExportConfig { items: ExportItem[]; sheetRows?: () => Array<Record<string, unknown>>; }\n/** Exact portable table-copy API. Host adapters import this, never recreate it. */\nexport interface CopyControlProps {\n /** Changes reset the preparation session when a mounted control changes record. */\n sourceId?: string;\n human?: CopyResolvable<string>;\n agent?: CopyResolvable<AgentPayloadInput | string>;\n json?: CopyResolvable<unknown>;\n label: string;\n size?: \"xs\" | \"icon\" | \"sm\";\n /** Accepted for direct compatibility; package chrome has no application groomer engine. */\n appearance?: \"segmented\" | \"bare\";\n disabled?: boolean;\n stopPropagation?: boolean;\n className?: string;\n /** Legacy source compatibility; controls are already grouped by default. */\n grouped?: boolean;\n aiVariants?: AiVariant[];\n agentVariant?: { id?: string; label?: string; hint?: string; position?: \"first\" | \"last\" };\n aiCustom?: AiCustomSource;\n export?: CopyExportConfig;\n hide?: Array<\"copy\" | \"ai\" | \"export\">;\n}\nexport interface CopySubsetColumn<T> { id: string; header: string; accessorKey?: keyof T & string; accessorFn?: (row: T) => unknown; hidden?: boolean; }\nexport interface CopySubsetSource<T> { label: string; location: string; kind: string; rows: T[]; columns: CopySubsetColumn<T>[]; getRowId: (row: T) => string; serializer?: (rows: T[]) => AgentPayloadInput; }\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../src/data-table/copy-types.ts"],"sourcesContent":["import type { ComponentType, ReactNode } from \"react\";\n\nexport type CopyResolvable<T> = T | (() => T);\nexport type CopyScalar = string | number | boolean | null | undefined;\nexport interface AgentPayloadInput { kind: string; location: string; description: string; data: unknown; summary?: string; context?: Record<string, CopyScalar>; attributes?: Record<string, CopyScalar>; }\nexport type AiIcon = ComponentType<{ className?: string }>;\nexport interface AiVariant { id: string; label: string; hint?: string; icon?: AiIcon; section?: \"copy\" | \"ai\"; ariaLabel?: string; successMessage?: string; build?: () => AgentPayloadInput | string | Promise<AgentPayloadInput | string>; onSelect?: () => void; modal?: (props: { open: boolean; onOpenChange: (open: boolean) => void }) => ReactNode; }\nexport type AiCustomOption =\n | { kind: \"toggle\"; key: string; label: string; hint?: string; default: boolean }\n | { kind: \"preset\"; key: string; label: string; options: { label: string; value: number }[]; default: number }\n | { kind: \"slider\"; key: string; label: string; hint?: string; stops: number[]; default: number; formatValue: (value: number) => string }\n | { kind: \"number\"; key: string; label: string; hint?: string; min?: number; step?: number; presets?: { label: string; value: number }[]; default: number };\nexport type AiOptionValues = Record<string, boolean | number>;\nexport interface AiCustomSource { label?: string; hint?: string; options: AiCustomOption[]; build: (options: AiOptionValues) => { text: string; meta?: Record<string, string | number | boolean | undefined> }; wrap: (text: string, options: AiOptionValues, meta?: Record<string, string | number | boolean | undefined>) => AgentPayloadInput; dialogTitle?: string; dialogDescription?: string; }\nexport interface ExportItem { id: string; label: string; build?: () => { content: BlobPart; extension: string; mime: string }; onSelect?: () => void | Promise<void>; }\nexport interface CopyExportConfig { items: ExportItem[]; sheetRows?: () => Array<Record<string, unknown>>; }\n/** Exact portable table-copy API. Host adapters import this, never recreate it. */\nexport interface CopyControlProps {\n /** Changes reset the preparation session when a mounted control changes record. */\n sourceId?: string;\n /** Canonical table controls opt into row preparation when sheet rows exist. */\n primarySource?: \"table\";\n human?: CopyResolvable<string>;\n agent?: CopyResolvable<AgentPayloadInput | string>;\n json?: CopyResolvable<unknown>;\n label: string;\n size?: \"xs\" | \"icon\" | \"sm\";\n /** Accepted for direct compatibility; package chrome has no application groomer engine. */\n appearance?: \"segmented\" | \"bare\";\n disabled?: boolean;\n stopPropagation?: boolean;\n className?: string;\n /** Legacy source compatibility; controls are already grouped by default. */\n grouped?: boolean;\n aiVariants?: AiVariant[];\n agentVariant?: { id?: string; label?: string; hint?: string; position?: \"first\" | \"last\" };\n aiCustom?: AiCustomSource;\n export?: CopyExportConfig;\n hide?: Array<\"copy\" | \"ai\" | \"export\">;\n}\nexport interface CopySubsetColumn<T> { id: string; header: string; accessorKey?: keyof T & string; accessorFn?: (row: T) => unknown; hidden?: boolean; }\nexport interface CopySubsetSource<T> { label: string; location: string; kind: string; rows: T[]; columns: CopySubsetColumn<T>[]; getRowId: (row: T) => string; serializer?: (rows: T[]) => AgentPayloadInput; }\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -96,6 +96,8 @@ interface CopyExportConfig {
96
96
  interface CopyControlProps {
97
97
  /** Changes reset the preparation session when a mounted control changes record. */
98
98
  sourceId?: string;
99
+ /** Canonical table controls opt into row preparation when sheet rows exist. */
100
+ primarySource?: "table";
99
101
  human?: CopyResolvable<string>;
100
102
  agent?: CopyResolvable<AgentPayloadInput | string>;
101
103
  json?: CopyResolvable<unknown>;
@@ -96,6 +96,8 @@ interface CopyExportConfig {
96
96
  interface CopyControlProps {
97
97
  /** Changes reset the preparation session when a mounted control changes record. */
98
98
  sourceId?: string;
99
+ /** Canonical table controls opt into row preparation when sheet rows exist. */
100
+ primarySource?: "table";
99
101
  human?: CopyResolvable<string>;
100
102
  agent?: CopyResolvable<AgentPayloadInput | string>;
101
103
  json?: CopyResolvable<unknown>;
@@ -657,6 +657,10 @@ function materialIdentity(material) {
657
657
  }
658
658
  return `payload:${id}`;
659
659
  }
660
+ function snapshotContent(snapshot) {
661
+ const { sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits } = snapshot;
662
+ return JSON.stringify({ sourceId, label, payload, coverage, unsavedChanges, omissions, sections, limits });
663
+ }
660
664
  function useFence() {
661
665
  const mounted = React7.useRef(true);
662
666
  const nextId = React7.useRef(0);
@@ -705,7 +709,12 @@ async function buildArtifact(draft, format, label) {
705
709
  const extension = format === "plain" ? "txt" : format === "compact-json" ? "json" : format;
706
710
  return (0, import_content_transfer.serialize)(draft, format, { spreadsheetSafe: format === "csv" || format === "tsv", filename: `${filenameFor(label)}.${extension}` });
707
711
  }
708
- function ContentTransferMenu({ source, surface, label, projection, sections, preferences, table, formatSources, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef }) {
712
+ function ContentTransferMenu(props) {
713
+ const inherited = React7.useContext(CapabilityContext);
714
+ const identityKey = props.capabilities?.identityKey ?? inherited.identityKey;
715
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(ContentTransferMenuSession, { ...props }, identityKey ?? "anonymous");
716
+ }
717
+ function ContentTransferMenuSession({ source, surface, label, projection, sections, preferences, table, formatSources, variants = EMPTY_VARIANTS, actions = [], capabilities: local, disabled, enabled = true, className, stopPropagation = true, hide = [], controllerRef }) {
709
718
  const triggerRef = React7.useRef(null);
710
719
  const inherited = React7.useContext(CapabilityContext);
711
720
  const nearestSurface = React7.useContext(SurfaceContext);
@@ -754,7 +763,7 @@ function ContentTransferMenu({ source, surface, label, projection, sections, pre
754
763
  const captured = await material.capture(signal);
755
764
  sourceMaterial = { id: captured.snapshot.sourceId, label: captured.snapshot.label, capture: async () => captured };
756
765
  } else if ("kind" in material) {
757
- sourceMaterial = applyMenuProjection ? { id: "direct", label: sourceLabel, capture: async () => (0, import_content_transfer.directSource)(material, { label: sourceLabel, ...projection ? { projection } : {}, ...sections ? { sections } : {} }) } : material;
766
+ sourceMaterial = { id: materialIdentity(material), label: sourceLabel, capture: async () => (0, import_content_transfer.directSource)(material, { sourceId: materialIdentity(material), label: sourceLabel, ...applyMenuProjection && projection ? { projection } : {}, ...applyMenuProjection && sections ? { sections } : {} }) };
758
767
  } else if (applyMenuProjection && (projection || sections)) {
759
768
  sourceMaterial = {
760
769
  id: material.id,
@@ -771,7 +780,7 @@ function ContentTransferMenu({ source, surface, label, projection, sections, pre
771
780
  } else {
772
781
  sourceMaterial = material;
773
782
  }
774
- const raw = "kind" in sourceMaterial ? (0, import_content_transfer.directSource)(sourceMaterial) : await sourceMaterial.capture({ scope, signal });
783
+ const raw = await sourceMaterial.capture({ scope, signal });
775
784
  const constrained = (0, import_content_transfer.resolveTransferPreferences)(providerPreferences, preferences, raw.snapshot.limits);
776
785
  const result = await (0, import_content_transfer.capture)({ id: raw.snapshot.sourceId, label: raw.snapshot.label, capture: async () => ({ ...raw, snapshot: { ...raw.snapshot, limits: constrained.limits } }) }, signal, scope);
777
786
  if (scope !== "all-matching" || !collectPages) return result;
@@ -831,31 +840,117 @@ function ContentTransferMenu({ source, surface, label, projection, sections, pre
831
840
  fence.finish(controller);
832
841
  }
833
842
  }, [captureOne, capturePrimary, fence, formatSources, initialScope, preferences, providerPreferences, report, transport]);
834
- const openWorkspace = React7.useCallback(async (key = primaryWorkspaceKey, variant) => {
835
- const variantMaterial = variant && "kind" in variant.source ? { id: `variant:${key}`, label: variant.label, capture: async () => (0, import_content_transfer.directSource)(variant.source, { label: variant.label }) } : variant?.source;
836
- const captureWorkspace = variantMaterial ? (scope, signal) => captureOne(variantMaterial, scope, signal, true) : capturePrimary;
837
- workspaceCaptures.current.set(key, captureWorkspace);
838
- if (workspaces[key]) {
839
- setWorkspaceKey(key);
840
- setWorkspaceOpen(true);
841
- return;
842
- }
843
- const { controller, id } = fence.begin();
844
- try {
845
- const result = await captureWorkspace(initialScope, controller.signal);
846
- if (!fence.valid(id, controller)) return;
847
- const resolved = (0, import_content_transfer.resolveTransferPreferences)(providerPreferences, preferences, result.snapshot.limits);
848
- const original = (0, import_content_transfer.createDraft)(result.snapshot);
849
- const base = { ...result, key, original, draft: original, scope: initialScope, preferences: resolved, ...result.collectionError ? { error: result.collectionError } : {} };
850
- setWorkspaces((current) => ({ ...current, [key]: { ...base, draft: rebuild(base, initialControls(base)) } }));
851
- setWorkspaceKey(key);
852
- setWorkspaceOpen(true);
853
- } catch (error) {
854
- if (fence.valid(id, controller)) setNotice(errorMessage(error, "Could not capture content"));
855
- } finally {
856
- fence.finish(controller);
857
- }
858
- }, [captureOne, capturePrimary, fence, initialScope, preferences, primaryWorkspaceKey, providerPreferences, workspaces]);
843
+ const openWorkspace = React7.useCallback(
844
+ async (key = primaryWorkspaceKey, variant) => {
845
+ const variantMaterial = variant && "kind" in variant.source ? {
846
+ id: `variant:${key}`,
847
+ label: variant.label,
848
+ capture: async () => (0, import_content_transfer.directSource)(variant.source, {
849
+ sourceId: `variant:${key}`,
850
+ label: variant.label
851
+ })
852
+ } : variant?.source;
853
+ const captureWorkspace = (scope, signal, collectPages = true) => captureOne(variantMaterial ?? primary, scope, signal, true, collectPages);
854
+ workspaceCaptures.current.set(key, captureWorkspace);
855
+ const existing = workspaces[key];
856
+ const { controller, id } = fence.begin();
857
+ if (existing) {
858
+ try {
859
+ const allMatching = existing.scope === "all-matching";
860
+ const fresh = await captureWorkspace(
861
+ existing.scope,
862
+ controller.signal,
863
+ !allMatching
864
+ );
865
+ if (!fence.valid(id, controller) || !workspaceCaptures.current.has(key))
866
+ return;
867
+ if (allMatching && (!existing.allMatching || !fresh.allMatching))
868
+ throw new Error(
869
+ "This source can no longer provide an all-matching snapshot."
870
+ );
871
+ const collectionIdentity = (value) => value && {
872
+ query: value.source.query,
873
+ projection: value.projection,
874
+ columns: value.columns,
875
+ limits: value.limits
876
+ };
877
+ const changed = allMatching ? JSON.stringify(collectionIdentity(existing.allMatching)) !== JSON.stringify(collectionIdentity(fresh.allMatching)) : snapshotContent(existing.snapshot) !== snapshotContent(fresh.snapshot);
878
+ setWorkspaces((current) => {
879
+ const retained = current[key];
880
+ if (!retained) return current;
881
+ return {
882
+ ...current,
883
+ [key]: {
884
+ ...retained,
885
+ error: fresh.collectionError,
886
+ ...allMatching ? { sourceStale: changed } : changed ? { pending: { ...fresh, scope: retained.scope } } : { pending: void 0 }
887
+ }
888
+ };
889
+ });
890
+ setWorkspaceKey(key);
891
+ setWorkspaceOpen(true);
892
+ } catch (error) {
893
+ if (fence.valid(id, controller) && workspaceCaptures.current.has(key)) {
894
+ setWorkspaces(
895
+ (current) => current[key] ? {
896
+ ...current,
897
+ [key]: {
898
+ ...current[key],
899
+ error: errorMessage(error, "Could not refresh source")
900
+ }
901
+ } : current
902
+ );
903
+ setWorkspaceKey(key);
904
+ setWorkspaceOpen(true);
905
+ }
906
+ } finally {
907
+ fence.finish(controller);
908
+ }
909
+ return;
910
+ }
911
+ try {
912
+ const result = await captureWorkspace(initialScope, controller.signal);
913
+ if (!fence.valid(id, controller)) return;
914
+ const resolved = (0, import_content_transfer.resolveTransferPreferences)(
915
+ providerPreferences,
916
+ preferences,
917
+ result.snapshot.limits
918
+ );
919
+ const original = (0, import_content_transfer.createDraft)(result.snapshot);
920
+ const base = {
921
+ ...result,
922
+ key,
923
+ original,
924
+ draft: original,
925
+ scope: initialScope,
926
+ preferences: resolved,
927
+ ...result.collectionError ? { error: result.collectionError } : {}
928
+ };
929
+ setWorkspaces((current) => ({
930
+ ...current,
931
+ [key]: { ...base, draft: rebuild(base, initialControls(base)) }
932
+ }));
933
+ setWorkspaceKey(key);
934
+ setWorkspaceOpen(true);
935
+ } catch (error) {
936
+ if (fence.valid(id, controller))
937
+ setNotice(errorMessage(error, "Could not capture content"));
938
+ } finally {
939
+ fence.finish(controller);
940
+ }
941
+ },
942
+ [
943
+ captureOne,
944
+ capturePrimary,
945
+ fence,
946
+ initialScope,
947
+ preferences,
948
+ primaryWorkspaceKey,
949
+ primary,
950
+ providerPreferences,
951
+ workspaces
952
+ ]
953
+ );
859
954
  React7.useImperativeHandle(controllerRef, () => ({
860
955
  focusTrigger: () => triggerRef.current?.focus(),
861
956
  prepare: async (requestedSource, options) => {
@@ -998,6 +1093,9 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
998
1093
  const [instruction, setInstruction] = React7.useState("");
999
1094
  const [busy, setBusy] = React7.useState(null);
1000
1095
  const [aiFailed, setAiFailed] = React7.useState(false);
1096
+ React7.useEffect(() => {
1097
+ setAiFailed(false);
1098
+ }, [state.snapshot.id, state.draft.revision]);
1001
1099
  const activeAi = React7.useRef(null);
1002
1100
  const activeCapture = React7.useRef(null);
1003
1101
  React7.useEffect(() => {
@@ -1075,7 +1173,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
1075
1173
  setBusy("capture");
1076
1174
  try {
1077
1175
  const result = await captureScope(scope, controller.signal);
1078
- if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, error: result.collectionError });
1176
+ if (fence.valid(id, controller) && latest.current.draft.revision === revision) onChange({ ...latest.current, pending: { ...result, scope }, sourceStale: false, error: result.collectionError });
1079
1177
  } catch (error) {
1080
1178
  if (fence.valid(id, controller)) onChange({ ...latest.current, error: errorMessage(error, "Refresh failed") });
1081
1179
  } finally {
@@ -1308,7 +1406,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
1308
1406
  activeCapture.current = null;
1309
1407
  setBusy(null);
1310
1408
  }, children: "Stop loading rows" }) : null,
1311
- ai && aiSession.current?.recover && busy !== "ai" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "button", variant: "outline", onClick: () => void runAi(true), children: aiFailed ? "Retry or rejoin AI run" : "Rejoin AI run" }) : null,
1409
+ ai && aiFailed && aiSession.current?.recover && busy !== "ai" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "button", variant: "outline", onClick: () => void runAi(true), children: aiFailed ? "Retry or rejoin AI run" : "Rejoin AI run" }) : null,
1312
1410
  ai ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "matrx-alchemy-meta", children: "AI creates a preview. It never copies automatically. Closing detaches this view without cancelling server work." }) : null,
1313
1411
  state.error ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "alert", className: "matrx-alchemy-error", children: state.error }) : null
1314
1412
  ] }) : null
@@ -1318,6 +1416,7 @@ function PreparationWorkspace({ restoreFocus, open, state, scopes, onClose, onCh
1318
1416
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "button", variant: "outline", onClick: () => setPendingControls(null), children: "Keep edited draft" }),
1319
1417
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "button", onClick: applyPendingControls, children: "Rebuild from source" })
1320
1418
  ] }) : null,
1419
+ state.sourceStale ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { role: "alert", className: "matrx-alchemy-decision", children: "The source changed since this draft. Use Refresh source to review the latest data." }) : null,
1321
1420
  state.pending ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "matrx-alchemy-decision", role: "alert", children: [
1322
1421
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "A fresh source snapshot is ready." }),
1323
1422
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Button, { type: "button", variant: "outline", onClick: () => resolveCapture("keep"), children: "Keep current snapshot and draft" }),
@@ -1435,7 +1534,17 @@ var sourceFor = (id, label, build) => ({
1435
1534
  }
1436
1535
  });
1437
1536
  function MatrxCopyMenu(props) {
1438
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MatrxCopyMenuSession, { ...props }, props.sourceId ?? props.label);
1537
+ const inherited = useContentTransferCapabilities();
1538
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
1539
+ MatrxCopyMenuSession,
1540
+ {
1541
+ ...props
1542
+ },
1543
+ JSON.stringify([
1544
+ inherited.identityKey ?? null,
1545
+ props.sourceId ?? props.label
1546
+ ])
1547
+ );
1439
1548
  }
1440
1549
  function MatrxCopyMenuSession(props) {
1441
1550
  const instance = React8.useId();
@@ -1463,7 +1572,7 @@ function MatrxCopyMenuSession(props) {
1463
1572
  })) : void 0;
1464
1573
  const agent = aiVisible && props.agent !== void 0 ? make("agent", () => asPayload(resolve(props.agent))) : void 0;
1465
1574
  const structuredAgent = aiVisible && props.agent !== void 0 ? make("structured-agent", () => structuredPayload(resolve(props.agent))) : void 0;
1466
- const table = exportVisible && props.export?.sheetRows ? make("rows", () => {
1575
+ const table = (exportVisible || props.primarySource === "table") && props.export?.sheetRows ? make("rows", () => {
1467
1576
  const value = (0, import_content_transfer2.normalizeTransferJson)(props.export.sheetRows());
1468
1577
  if (!Array.isArray(value) || value.some(
1469
1578
  (row) => row === null || typeof row !== "object" || Array.isArray(row)
@@ -1610,7 +1719,7 @@ function MatrxCopyMenuSession(props) {
1610
1719
  return props.sendToSheet(draft.payload.rows, props.label);
1611
1720
  }
1612
1721
  });
1613
- const source = json ?? table ?? structuredAgent ?? human ?? builderVariants[0]?.source;
1722
+ const source = (props.primarySource === "table" ? table : void 0) ?? json ?? table ?? structuredAgent ?? human ?? builderVariants[0]?.source;
1614
1723
  if (!source && !actions.length) return null;
1615
1724
  const target = source ?? make("actions", () => ({ kind: "text", text: props.label }));
1616
1725
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_jsx_runtime8.Fragment, { children: [
@@ -1630,7 +1739,7 @@ function MatrxCopyMenuSession(props) {
1630
1739
  formatSources: {
1631
1740
  ...human ? { plain: human, markdown: humanMarkdown, html: humanMarkdown } : {},
1632
1741
  ...json ? { json, "compact-json": json } : {},
1633
- ...table ? { csv: table, tsv: table } : {}
1742
+ ...table && exportVisible ? { csv: table, tsv: table } : {}
1634
1743
  },
1635
1744
  ...props.disabled !== void 0 ? { disabled: props.disabled } : {},
1636
1745
  className: cn(
@@ -1913,8 +2022,9 @@ function useMatrxDataTableHost() {
1913
2022
  var import_jsx_runtime10 = require("react/jsx-runtime");
1914
2023
  function CopyButtons(props) {
1915
2024
  const { CopyControls } = useMatrxDataTableHost();
1916
- if (CopyControls) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CopyControls, { ...props });
1917
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MatrxCopyMenu, { ...props });
2025
+ const copyProps = props.export?.sheetRows ? { ...props, primarySource: "table" } : props;
2026
+ if (CopyControls) return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(CopyControls, { ...copyProps });
2027
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MatrxCopyMenu, { ...copyProps });
1918
2028
  }
1919
2029
 
1920
2030
  // src/data-table/MatrxUuidCell.tsx