@proveanything/smartlinks-utils-ui 0.3.8 → 0.3.9

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,7 +1,7 @@
1
- import { styleInject } from '../../chunk-NIAIQRFC.js';
1
+ import { styleInject } from '../../chunk-IGFYMNKL.js';
2
2
  import { cn } from '../../chunk-L7FQ52F5.js';
3
3
  import { createContext, useMemo, useState, useEffect, useCallback, useRef, useContext, createElement } from 'react';
4
- import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, Globe, Tag, Boxes, Layers, Package, Rows3, Image, List, ChevronRight, Eraser, Box, X, AlertTriangle, Info, HelpCircle, Search, CornerDownLeft, Circle, AlertCircle, Undo2, Save } from 'lucide-react';
4
+ import { ChevronDown, Database, Lightbulb, SearchX, Inbox, LayoutGrid, Eye, MoreHorizontal, Download, Upload, Trash2, Copy, Pencil, Plus, CircleDashed, ArrowDownLeft, CheckCircle2, Globe, Tag, Boxes, Layers, Package, Rows3, Image, List, ChevronRight, Eraser, ClipboardPaste, Box, X, AlertTriangle, Info, HelpCircle, Search, CornerDownLeft, Circle, AlertCircle, Undo2, Save } from 'lucide-react';
5
5
  import { useQueryClient, useInfiniteQuery, useQuery } from '@tanstack/react-query';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -93,7 +93,20 @@ var DEFAULT_I18N = {
93
93
  noItemsTitle: "No items yet",
94
94
  noItemsBody: "Create your first item to get started.",
95
95
  railEmptyTitle: "No records yet",
96
- railEmptyBody: "Records you create will appear here."
96
+ railEmptyBody: "Records you create will appear here.",
97
+ copy: "Copy",
98
+ paste: "Paste",
99
+ pasteFrom: "Paste from {name}",
100
+ pasteReplace: "Paste (replace)",
101
+ clipboardEmpty: "Nothing to paste",
102
+ copyToast: "Copied {name}. Pick a destination to paste.",
103
+ pasteToast: "Pasted from {name}. Review and Save.",
104
+ pasteConfirmTitle: "Replace existing values?",
105
+ pasteConfirmBody: "{destination} already has saved values. Replace them with the copied values?",
106
+ pasteConfirmReplace: "Replace",
107
+ pasteConfirmCancel: "Cancel",
108
+ pasteWarnTitle: "Cross-scope paste",
109
+ pasteWarnContinue: "Continue"
97
110
  };
98
111
 
99
112
  // src/components/RecordsAdmin/types/presentation.ts
@@ -1187,7 +1200,7 @@ var ConfirmDialog = ({
1187
1200
  children: discardLabel
1188
1201
  }
1189
1202
  ),
1190
- /* @__PURE__ */ jsx(
1203
+ saveLabel ? /* @__PURE__ */ jsx(
1191
1204
  "button",
1192
1205
  {
1193
1206
  type: "button",
@@ -1196,7 +1209,7 @@ var ConfirmDialog = ({
1196
1209
  onClick: () => onChoice("save"),
1197
1210
  children: saveLabel
1198
1211
  }
1199
- )
1212
+ ) : null
1200
1213
  ] })
1201
1214
  ]
1202
1215
  }
@@ -1253,6 +1266,122 @@ var useConfirmDialog = () => {
1253
1266
  )
1254
1267
  };
1255
1268
  };
1269
+ var DEFAULTS2 = {
1270
+ title: "Replace existing values?",
1271
+ body: "This destination already has saved values. Replace them?",
1272
+ confirmLabel: "Replace",
1273
+ cancelLabel: "Cancel"
1274
+ };
1275
+ var usePasteConfirm = () => {
1276
+ const [open, setOpen] = useState(false);
1277
+ const [state, setState] = useState(DEFAULTS2);
1278
+ const resolverRef = useRef(null);
1279
+ const confirm = useCallback((i18n) => {
1280
+ setState({ ...DEFAULTS2, ...i18n ?? {} });
1281
+ setOpen(true);
1282
+ return new Promise((resolve) => {
1283
+ resolverRef.current = resolve;
1284
+ });
1285
+ }, []);
1286
+ const handleChoice = useCallback((choice) => {
1287
+ setOpen(false);
1288
+ const r = resolverRef.current;
1289
+ resolverRef.current = null;
1290
+ r?.(choice === "discard");
1291
+ }, []);
1292
+ return {
1293
+ confirm,
1294
+ dialog: /* @__PURE__ */ jsx(
1295
+ ConfirmDialog,
1296
+ {
1297
+ open,
1298
+ title: state.title,
1299
+ body: state.body,
1300
+ saveLabel: "",
1301
+ discardLabel: state.confirmLabel,
1302
+ cancelLabel: state.cancelLabel,
1303
+ onChoice: handleChoice
1304
+ }
1305
+ )
1306
+ };
1307
+ };
1308
+ var stores = /* @__PURE__ */ new Map();
1309
+ var storageKey = (key) => `ra:clipboard:${key}`;
1310
+ var getStore = (key) => {
1311
+ let s = stores.get(key);
1312
+ if (!s) {
1313
+ s = { entry: null, listeners: /* @__PURE__ */ new Set() };
1314
+ if (typeof window !== "undefined") {
1315
+ try {
1316
+ const raw = window.sessionStorage.getItem(storageKey(key));
1317
+ if (raw) s.entry = JSON.parse(raw);
1318
+ } catch {
1319
+ }
1320
+ }
1321
+ stores.set(key, s);
1322
+ }
1323
+ return s;
1324
+ };
1325
+ var persist = (key, entry) => {
1326
+ if (typeof window === "undefined") return;
1327
+ try {
1328
+ if (entry) window.sessionStorage.setItem(storageKey(key), JSON.stringify(entry));
1329
+ else window.sessionStorage.removeItem(storageKey(key));
1330
+ } catch {
1331
+ }
1332
+ };
1333
+ var notify = (store) => {
1334
+ store.listeners.forEach((l) => l());
1335
+ };
1336
+ var composeKey = (appId, recordType) => `${appId}::${recordType}`;
1337
+ function useRecordClipboard(args) {
1338
+ const key = composeKey(args.appId, args.recordType);
1339
+ const store = getStore(key);
1340
+ const [entry, setEntry] = useState(
1341
+ store.entry
1342
+ );
1343
+ useEffect(() => {
1344
+ const listener = () => setEntry(store.entry);
1345
+ store.listeners.add(listener);
1346
+ listener();
1347
+ return () => {
1348
+ store.listeners.delete(listener);
1349
+ };
1350
+ }, [store]);
1351
+ const set = useCallback((next) => {
1352
+ const full = { ...next, copiedAt: (/* @__PURE__ */ new Date()).toISOString() };
1353
+ store.entry = full;
1354
+ persist(key, full);
1355
+ notify(store);
1356
+ }, [store, key]);
1357
+ const clear = useCallback(() => {
1358
+ store.entry = null;
1359
+ persist(key, null);
1360
+ notify(store);
1361
+ }, [store, key]);
1362
+ return { entry, hasEntry: entry != null, set, clear };
1363
+ }
1364
+ function checkPasteCompatibility(source, destination) {
1365
+ const from = source.kind;
1366
+ const to = destination.kind;
1367
+ if (from === to) return { status: "allowed" };
1368
+ if ((from === "variant" || from === "batch") && to === "product") {
1369
+ return { status: "warn", reason: "This will overwrite product-level values." };
1370
+ }
1371
+ return { status: "allowed" };
1372
+ }
1373
+ function cloneValue(value) {
1374
+ if (value == null) return value;
1375
+ try {
1376
+ return structuredClone(value);
1377
+ } catch {
1378
+ try {
1379
+ return JSON.parse(JSON.stringify(value));
1380
+ } catch {
1381
+ return value;
1382
+ }
1383
+ }
1384
+ }
1256
1385
  var LABELS = {
1257
1386
  product: "Products",
1258
1387
  facet: "Shared",
@@ -1332,8 +1461,90 @@ var StatusDot = ({ source, status, className }) => {
1332
1461
  else if (source === "inherited" || status === "partial") cls = "ra-status-shared";
1333
1462
  return /* @__PURE__ */ jsx("span", { className: cn("ra-status-dot", cls, className), "aria-hidden": "true" });
1334
1463
  };
1464
+ var RowContextMenu = ({
1465
+ onCopy,
1466
+ onPaste,
1467
+ canPaste,
1468
+ pasteWillReplace,
1469
+ pasteSourceLabel,
1470
+ i18n
1471
+ }) => {
1472
+ const [open, setOpen] = useState(false);
1473
+ const wrapperRef = useRef(null);
1474
+ useEffect(() => {
1475
+ if (!open) return;
1476
+ const onDoc = (e) => {
1477
+ if (!wrapperRef.current?.contains(e.target)) setOpen(false);
1478
+ };
1479
+ const onKey = (e) => {
1480
+ if (e.key === "Escape") setOpen(false);
1481
+ };
1482
+ document.addEventListener("mousedown", onDoc);
1483
+ document.addEventListener("keydown", onKey);
1484
+ return () => {
1485
+ document.removeEventListener("mousedown", onDoc);
1486
+ document.removeEventListener("keydown", onKey);
1487
+ };
1488
+ }, [open]);
1489
+ if (!onCopy && !onPaste) return null;
1490
+ const pasteLabel = !canPaste ? i18n.clipboardEmpty : pasteSourceLabel ? i18n.pasteFrom.replace("{name}", pasteSourceLabel) : pasteWillReplace ? i18n.pasteReplace : i18n.paste;
1491
+ return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className: "ra-row-menu-wrap relative", children: [
1492
+ /* @__PURE__ */ jsx(
1493
+ "button",
1494
+ {
1495
+ type: "button",
1496
+ className: "ra-row-menu-trigger",
1497
+ "aria-label": "Row actions",
1498
+ "aria-haspopup": "menu",
1499
+ "aria-expanded": open,
1500
+ onClick: (e) => {
1501
+ e.stopPropagation();
1502
+ setOpen((v) => !v);
1503
+ },
1504
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "w-3.5 h-3.5", "aria-hidden": "true" })
1505
+ }
1506
+ ),
1507
+ open && /* @__PURE__ */ jsxs("div", { role: "menu", className: "ra-row-menu", onClick: (e) => e.stopPropagation(), children: [
1508
+ onCopy && /* @__PURE__ */ jsxs(
1509
+ "button",
1510
+ {
1511
+ type: "button",
1512
+ role: "menuitem",
1513
+ className: "ra-row-menu-item",
1514
+ onClick: (e) => {
1515
+ e.stopPropagation();
1516
+ setOpen(false);
1517
+ onCopy();
1518
+ },
1519
+ children: [
1520
+ /* @__PURE__ */ jsx(Copy, { className: "w-3 h-3", "aria-hidden": "true" }),
1521
+ /* @__PURE__ */ jsx("span", { children: i18n.copy })
1522
+ ]
1523
+ }
1524
+ ),
1525
+ onPaste && /* @__PURE__ */ jsxs(
1526
+ "button",
1527
+ {
1528
+ type: "button",
1529
+ role: "menuitem",
1530
+ className: "ra-row-menu-item",
1531
+ disabled: !canPaste,
1532
+ onClick: (e) => {
1533
+ e.stopPropagation();
1534
+ setOpen(false);
1535
+ if (canPaste) onPaste();
1536
+ },
1537
+ children: [
1538
+ /* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3", "aria-hidden": "true" }),
1539
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: pasteLabel })
1540
+ ]
1541
+ }
1542
+ )
1543
+ ] })
1544
+ ] });
1545
+ };
1335
1546
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1336
- const { selected, onSelect, isDirty } = ctx;
1547
+ const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
1337
1548
  const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
1338
1549
  return /* @__PURE__ */ jsxs(
1339
1550
  "button",
@@ -1358,6 +1569,23 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1358
1569
  "aria-label": "Unsaved changes",
1359
1570
  className: "ra-status-dot ra-status-shared shrink-0"
1360
1571
  }
1572
+ ),
1573
+ (onCopy || onPaste) && /* @__PURE__ */ jsx(
1574
+ RowContextMenu,
1575
+ {
1576
+ onCopy,
1577
+ onPaste,
1578
+ canPaste,
1579
+ pasteWillReplace,
1580
+ pasteSourceLabel: clipboardSourceLabel,
1581
+ i18n: {
1582
+ copy: DEFAULT_I18N.copy,
1583
+ paste: DEFAULT_I18N.paste,
1584
+ pasteFrom: DEFAULT_I18N.pasteFrom,
1585
+ pasteReplace: DEFAULT_I18N.pasteReplace,
1586
+ clipboardEmpty: DEFAULT_I18N.clipboardEmpty
1587
+ }
1588
+ }
1361
1589
  )
1362
1590
  ]
1363
1591
  }
@@ -1365,7 +1593,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1365
1593
  };
1366
1594
  var initials = (s) => s.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
1367
1595
  var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1368
- const { selected, onSelect, isDirty } = ctx;
1596
+ const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
1369
1597
  const aspect = variant === "gallery" ? "aspect-video" : "aspect-square";
1370
1598
  return /* @__PURE__ */ jsxs(
1371
1599
  "button",
@@ -1417,7 +1645,26 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1417
1645
  }
1418
1646
  ),
1419
1647
  /* @__PURE__ */ jsxs("div", { className: "p-2.5 min-w-0", children: [
1420
- /* @__PURE__ */ jsx("div", { className: "ra-row-title", children: record.label }),
1648
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-1.5 min-w-0", children: [
1649
+ /* @__PURE__ */ jsx("div", { className: "ra-row-title flex-1 min-w-0", children: record.label }),
1650
+ (onCopy || onPaste) && /* @__PURE__ */ jsx(
1651
+ RowContextMenu,
1652
+ {
1653
+ onCopy,
1654
+ onPaste,
1655
+ canPaste,
1656
+ pasteWillReplace,
1657
+ pasteSourceLabel: clipboardSourceLabel,
1658
+ i18n: {
1659
+ copy: DEFAULT_I18N.copy,
1660
+ paste: DEFAULT_I18N.paste,
1661
+ pasteFrom: DEFAULT_I18N.pasteFrom,
1662
+ pasteReplace: DEFAULT_I18N.pasteReplace,
1663
+ clipboardEmpty: DEFAULT_I18N.clipboardEmpty
1664
+ }
1665
+ }
1666
+ )
1667
+ ] }),
1421
1668
  variant === "gallery" && record.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: record.subtitle }),
1422
1669
  record.badges && record.badges.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-1 mt-1.5 flex-wrap", children: record.badges.slice(0, 3).map((b, i) => /* @__PURE__ */ jsx("span", { className: "ra-chip", "data-tone": "muted", children: b.label }, `${b.label}-${i}`)) })
1423
1670
  ] })
@@ -1433,13 +1680,18 @@ var RecordList = ({
1433
1680
  presentation = "list",
1434
1681
  renderListRow,
1435
1682
  renderCard,
1436
- groupBy
1683
+ groupBy,
1684
+ rowClipboard
1437
1685
  }) => {
1438
- const buildCtx = (item) => ({
1439
- selected: item.ref === selectedRef,
1440
- onSelect: () => onSelect(item),
1441
- isDirty: !!dirtyRef && item.ref === dirtyRef
1442
- });
1686
+ const buildCtx = (item) => {
1687
+ const cb = rowClipboard ? rowClipboard(item) : null;
1688
+ return {
1689
+ selected: item.ref === selectedRef,
1690
+ onSelect: () => onSelect(item),
1691
+ isDirty: !!dirtyRef && item.ref === dirtyRef,
1692
+ ...cb ?? {}
1693
+ };
1694
+ };
1443
1695
  const groups = useMemo(() => {
1444
1696
  if (!groupBy) return null;
1445
1697
  const buckets = /* @__PURE__ */ new Map();
@@ -1721,8 +1973,10 @@ var DeleteButton = ({
1721
1973
  label = "Delete record",
1722
1974
  confirmLabel = "Confirm delete",
1723
1975
  revertMs = 3e3,
1724
- disabled
1976
+ disabled,
1977
+ icon
1725
1978
  }) => {
1979
+ const Icon = icon ?? Trash2;
1726
1980
  const [armed, setArmed] = useState(false);
1727
1981
  const [busy, setBusy] = useState(false);
1728
1982
  const timerRef = useRef(null);
@@ -1783,7 +2037,7 @@ var DeleteButton = ({
1783
2037
  color: "hsl(var(--ra-danger, var(--ra-text)))"
1784
2038
  },
1785
2039
  children: [
1786
- /* @__PURE__ */ jsx(Trash2, { className: "w-3.5 h-3.5" }),
2040
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
1787
2041
  label
1788
2042
  ]
1789
2043
  }
@@ -1799,8 +2053,17 @@ function RecordEditor({
1799
2053
  onBeforeDelete,
1800
2054
  headerLabel,
1801
2055
  headerSubtitle,
1802
- headerMeta
2056
+ headerMeta,
2057
+ clipboard,
2058
+ actionLabels,
2059
+ actionIcons
1803
2060
  }) {
2061
+ const saveLabel = actionLabels?.save ?? i18n.save;
2062
+ const discardLabel = actionLabels?.discard ?? i18n.discard;
2063
+ const deleteLabel = actionLabels?.delete ?? i18n.delete;
2064
+ const SaveIcon = actionIcons?.save;
2065
+ const DiscardIcon = actionIcons?.discard;
2066
+ const DeleteIcon = actionIcons?.delete;
1804
2067
  const sourceLabel = ctx.source === "self" ? "Customised" : ctx.source === "inherited" ? "Inherited" : null;
1805
2068
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
1806
2069
  /* @__PURE__ */ jsxs(
@@ -1878,15 +2141,18 @@ function RecordEditor({
1878
2141
  style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
1879
2142
  children: [
1880
2143
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1881
- /* @__PURE__ */ jsx(
2144
+ /* @__PURE__ */ jsxs(
1882
2145
  "button",
1883
2146
  {
1884
2147
  type: "button",
1885
2148
  onClick: ctx.reset,
1886
2149
  disabled: !ctx.isDirty || !!ctx.isSaving,
1887
- className: "text-xs px-3 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))]",
2150
+ className: "text-xs px-3 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
1888
2151
  style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
1889
- children: i18n.discard
2152
+ children: [
2153
+ DiscardIcon && /* @__PURE__ */ jsx(DiscardIcon, { className: "h-4 w-4" }),
2154
+ discardLabel
2155
+ ]
1890
2156
  }
1891
2157
  ),
1892
2158
  ctx.canRemove && /* @__PURE__ */ jsx(
@@ -1894,10 +2160,45 @@ function RecordEditor({
1894
2160
  {
1895
2161
  onConfirm: () => ctx.remove(),
1896
2162
  onBeforeDelete,
1897
- label: i18n.delete,
1898
- confirmLabel: i18n.confirmDelete
2163
+ label: deleteLabel,
2164
+ confirmLabel: i18n.confirmDelete,
2165
+ icon: DeleteIcon
1899
2166
  }
1900
2167
  ),
2168
+ clipboard && /* @__PURE__ */ jsxs(Fragment, { children: [
2169
+ /* @__PURE__ */ jsxs(
2170
+ "button",
2171
+ {
2172
+ type: "button",
2173
+ onClick: clipboard.onCopy,
2174
+ disabled: !clipboard.canCopy || !!ctx.isSaving,
2175
+ title: i18n.copy,
2176
+ "aria-label": i18n.copy,
2177
+ className: "text-xs px-2.5 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
2178
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2179
+ children: [
2180
+ /* @__PURE__ */ jsx(Copy, { className: "w-3 h-3" }),
2181
+ i18n.copy
2182
+ ]
2183
+ }
2184
+ ),
2185
+ /* @__PURE__ */ jsxs(
2186
+ "button",
2187
+ {
2188
+ type: "button",
2189
+ onClick: clipboard.onPaste,
2190
+ disabled: !clipboard.canPaste || !!ctx.isSaving,
2191
+ title: !clipboard.canPaste ? i18n.clipboardEmpty : clipboard.pasteSourceLabel ? i18n.pasteFrom.replace("{name}", clipboard.pasteSourceLabel) : i18n.paste,
2192
+ "aria-label": i18n.paste,
2193
+ className: "text-xs px-2.5 py-1.5 rounded-md border transition-opacity disabled:opacity-40 hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1.5",
2194
+ style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
2195
+ children: [
2196
+ /* @__PURE__ */ jsx(ClipboardPaste, { className: "w-3 h-3" }),
2197
+ clipboard.pasteWillReplace ? i18n.pasteReplace : i18n.paste
2198
+ ]
2199
+ }
2200
+ )
2201
+ ] }),
1901
2202
  footerExtra,
1902
2203
  ctx.saveError != null && !ctx.isSaving && /* @__PURE__ */ jsx(
1903
2204
  "span",
@@ -1909,7 +2210,7 @@ function RecordEditor({
1909
2210
  }
1910
2211
  )
1911
2212
  ] }),
1912
- /* @__PURE__ */ jsx(
2213
+ /* @__PURE__ */ jsxs(
1913
2214
  "button",
1914
2215
  {
1915
2216
  type: "button",
@@ -1918,9 +2219,12 @@ function RecordEditor({
1918
2219
  });
1919
2220
  },
1920
2221
  disabled: !ctx.isDirty || !!ctx.isSaving,
1921
- className: "text-xs px-3 py-1.5 rounded-md font-medium transition-opacity disabled:opacity-40",
2222
+ className: "text-xs px-3 py-1.5 rounded-md font-medium transition-opacity disabled:opacity-40 inline-flex items-center gap-1.5",
1922
2223
  style: { background: "hsl(var(--ra-accent))", color: "hsl(var(--ra-surface))" },
1923
- children: ctx.isSaving ? i18n.saving ?? "Saving\u2026" : i18n.save
2224
+ children: [
2225
+ SaveIcon && !ctx.isSaving && /* @__PURE__ */ jsx(SaveIcon, { className: "h-4 w-4" }),
2226
+ ctx.isSaving ? i18n.saving ?? "Saving\u2026" : saveLabel
2227
+ ]
1924
2228
  }
1925
2229
  )
1926
2230
  ]
@@ -1945,16 +2249,18 @@ var ProductDrillDown = ({
1945
2249
  batches,
1946
2250
  variantsLoading,
1947
2251
  batchesLoading,
1948
- children
2252
+ children,
2253
+ hideSingleTab
1949
2254
  }) => {
1950
2255
  const tabs = ["product"];
1951
2256
  if (showVariants) tabs.push("variant");
1952
2257
  if (showBatches) tabs.push("batch");
2258
+ const showTabStrip = tabs.length > 1 || !hideSingleTab;
1953
2259
  const childList = active === "variant" ? variants : active === "batch" ? batches : [];
1954
2260
  const childLoading = active === "variant" ? variantsLoading : active === "batch" ? batchesLoading : false;
1955
2261
  const childEmptyLabel = active === "variant" ? "No variants" : "No batches";
1956
2262
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
1957
- /* @__PURE__ */ jsx(
2263
+ showTabStrip && /* @__PURE__ */ jsx(
1958
2264
  "div",
1959
2265
  {
1960
2266
  className: "flex items-center gap-1 px-4 pt-3 border-b",
@@ -2503,8 +2809,12 @@ var UnsavedBanner = ({
2503
2809
  discardLabel,
2504
2810
  bodyTemplate,
2505
2811
  savingLabel,
2506
- errorLabel
2812
+ errorLabel,
2813
+ SaveIcon,
2814
+ DiscardIcon
2507
2815
  }) => {
2816
+ const SI = SaveIcon ?? Save;
2817
+ const DI = DiscardIcon ?? Undo2;
2508
2818
  const name = label ?? context ?? "this record";
2509
2819
  const message = bodyTemplate.replace("{name}", name);
2510
2820
  return /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-banner", role: "status", "aria-live": "polite", children: [
@@ -2526,7 +2836,7 @@ var UnsavedBanner = ({
2526
2836
  onClick: onDiscard,
2527
2837
  disabled: isSaving,
2528
2838
  children: [
2529
- /* @__PURE__ */ jsx(Undo2, { className: "w-3 h-3" }),
2839
+ /* @__PURE__ */ jsx(DI, { className: "w-3 h-3" }),
2530
2840
  discardLabel
2531
2841
  ]
2532
2842
  }
@@ -2539,7 +2849,7 @@ var UnsavedBanner = ({
2539
2849
  onClick: onSave,
2540
2850
  disabled: isSaving,
2541
2851
  children: [
2542
- /* @__PURE__ */ jsx(Save, { className: "w-3 h-3" }),
2852
+ /* @__PURE__ */ jsx(SI, { className: "w-3 h-3" }),
2543
2853
  isSaving ? savingLabel ?? "Saving\u2026" : saveLabel
2544
2854
  ]
2545
2855
  }
@@ -2547,6 +2857,25 @@ var UnsavedBanner = ({
2547
2857
  ] })
2548
2858
  ] });
2549
2859
  };
2860
+ var ClipboardToast = ({ message, variant = "copy", onDismiss }) => {
2861
+ useEffect(() => {
2862
+ const t = window.setTimeout(onDismiss, 2500);
2863
+ return () => window.clearTimeout(t);
2864
+ }, [message, onDismiss]);
2865
+ const Icon = variant === "paste" ? ClipboardPaste : Copy;
2866
+ return /* @__PURE__ */ jsxs(
2867
+ "div",
2868
+ {
2869
+ role: "status",
2870
+ "aria-live": "polite",
2871
+ className: "ra-clipboard-toast",
2872
+ children: [
2873
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5 shrink-0", "aria-hidden": "true" }),
2874
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: message })
2875
+ ]
2876
+ }
2877
+ );
2878
+ };
2550
2879
 
2551
2880
  // src/components/RecordsAdmin/data/csv.ts
2552
2881
  var escapeCell = (s) => {
@@ -2656,7 +2985,7 @@ var downloadBlob = (blob, filename) => {
2656
2985
  styleInject(':root {\n --ra-status-own: var(--ra-emerald, 142 71% 45%);\n --ra-status-shared: var(--ra-amber, 38 92% 50%);\n --ra-status-missing: var(--muted-foreground, 220 9% 46%);\n --ra-accent: var(--primary, 222 47% 11%);\n --ra-surface: var(--card, 0 0% 100%);\n --ra-border: var(--border, 220 13% 91%);\n --ra-text: var(--foreground, 222 47% 11%);\n --ra-muted: var(--muted, 220 14% 96%);\n --ra-muted-text: var(--muted-foreground, 220 9% 46%);\n --ra-radius: var(--radius, 0.625rem);\n --ra-dot-size: 0.5rem;\n --ra-page-bg: var(--background, 220 14% 98%);\n --ra-card-shadow: 0 1px 2px hsl(var(--ra-accent) / 0.04), 0 4px 12px hsl(var(--ra-accent) / 0.05);\n --ra-card-shadow-hover: 0 2px 4px hsl(var(--ra-accent) / 0.06), 0 8px 24px hsl(var(--ra-accent) / 0.08);\n --ra-row-hover: hsl(var(--ra-accent) / 0.05);\n --ra-row-active-bg: hsl(var(--ra-accent) / 0.10);\n --ra-row-active-bd: hsl(var(--ra-accent) / 0.45);\n --ra-focus-ring: hsl(var(--ra-accent) / 0.35);\n --ra-font-display: var(--font-display, var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif));\n --ra-font-ui: var(--font-sans, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif);\n --ra-title-weight: 600;\n --ra-display-weight: 700;\n --ra-info: var(--ra-blue, 214 95% 55%);\n --ra-success: var(--ra-emerald, 142 71% 45%);\n --ra-warning: var(--ra-amber, 38 92% 50%);\n --ra-danger: var(--destructive, 0 72% 51%);\n}\n.ra-status-dot {\n display: inline-block;\n width: var(--ra-dot-size);\n height: var(--ra-dot-size);\n border-radius: 9999px;\n flex-shrink: 0;\n}\n.ra-status-own {\n background: hsl(var(--ra-status-own));\n}\n.ra-status-shared {\n background: hsl(var(--ra-status-shared));\n}\n.ra-status-missing {\n background: hsl(var(--ra-status-missing) / 0.4);\n border: 1px solid hsl(var(--ra-status-missing) / 0.6);\n}\n.ra-row-active {\n background: var(--ra-row-active-bg);\n border-color: var(--ra-row-active-bd) !important;\n}\n');
2657
2986
 
2658
2987
  // src/components/RecordsAdmin/shell/shell.css
2659
- styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(--ra-page-bg));\n font-family: var(--ra-font-ui);\n}\n.ra-shell *,\n.ra-shell *::before,\n.ra-shell *::after {\n box-sizing: border-box;\n}\n.ra-shell .ra-card {\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-card-hover {\n transition:\n box-shadow .18s ease,\n transform .18s ease,\n border-color .18s ease;\n}\n.ra-shell .ra-card-hover:hover {\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-display {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-title {\n font-weight: var(--ra-title-weight);\n}\n.ra-shell :where(button, [role=button], input, select, textarea, a):focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n border-radius: calc(var(--ra-radius) * 0.6);\n}\n.ra-shell .ra-header {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.9rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-accent) / 0.12);\n background:\n linear-gradient(\n 135deg,\n hsl(var(--ra-accent) / 0.08),\n hsl(var(--ra-accent) / 0.02) 60%,\n hsl(var(--ra-surface)) 100%);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-header__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n.ra-shell .ra-header-aside {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-header-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: calc(var(--ra-radius) * 0.9);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n border: 1px solid hsl(var(--ra-accent) / 0.18);\n}\n.ra-shell .ra-header-text {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-header-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n letter-spacing: -0.01em;\n margin: 0;\n}\n.ra-shell .ra-header-subtitle {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-header-stats {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n padding: 0.15rem 0.4rem;\n border-radius: calc(var(--ra-radius) * 0.75);\n background: hsl(var(--ra-surface) / 0.7);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-header-stats--titled {\n flex-direction: column;\n align-items: stretch;\n padding: 0.4rem 0.55rem;\n gap: 0.3rem;\n}\n.ra-shell .ra-header-stats .ra-stats-items {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n}\n.ra-shell .ra-header-stats .ra-stats-heading {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon {\n display: inline-flex;\n align-items: center;\n color: hsl(var(--ra-text));\n opacity: 0.75;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon > svg {\n width: 0.85rem;\n height: 0.85rem;\n}\n.ra-shell .ra-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 0.15rem 0.45rem;\n min-width: 2.5rem;\n}\n.ra-shell .ra-stat-value {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n line-height: 1;\n}\n.ra-shell .ra-stat-label {\n font-size: 0.6rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-stat-divider {\n width: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-header-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-tabs {\n display: flex;\n gap: 0.25rem;\n padding: 0.25rem;\n background: hsl(var(--ra-muted));\n border-radius: calc(var(--ra-radius) * 0.85);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-tab {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.7rem;\n border-radius: calc(var(--ra-radius) * 0.65);\n font-size: 0.78rem;\n font-weight: 500;\n color: hsl(var(--ra-muted-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n transform .15s ease;\n white-space: nowrap;\n}\n.ra-shell .ra-tab:hover {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-tab[aria-selected=true] {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n box-shadow: var(--ra-card-shadow);\n font-weight: var(--ra-title-weight);\n}\n.ra-shell .ra-tab[aria-selected=true] .ra-tab-icon {\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-tab[disabled] {\n opacity: .5;\n cursor: not-allowed;\n}\n.ra-shell .ra-tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25rem;\n padding: 0 0.35rem;\n height: 1.1rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n font-size: 0.625rem;\n font-weight: 600;\n line-height: 1;\n}\n.ra-shell .ra-tab[aria-selected=false] .ra-tab-count {\n background: hsl(var(--ra-muted-text) / 0.15);\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.4rem;\n}\n.ra-shell[data-density=compact] .ra-header {\n padding: 0.75rem 1rem;\n}\n.ra-shell[data-density=compact] .ra-header-icon {\n width: 2.25rem;\n height: 2.25rem;\n}\n.ra-shell .ra-row {\n display: flex;\n align-items: center;\n gap: 0.65rem;\n width: 100%;\n text-align: left;\n padding: 0.65rem 0.85rem;\n border-left: 3px solid transparent;\n background: transparent;\n border-bottom: 1px solid transparent;\n transition: background .12s ease, border-color .12s ease;\n cursor: pointer;\n color: hsl(var(--ra-text));\n font-family: inherit;\n}\n.ra-shell .ra-row + .ra-row {\n border-top: 1px solid hsl(var(--ra-border) / 0.6);\n}\n.ra-shell .ra-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n border-left-color: var(--ra-row-active-bd);\n}\n.ra-shell .ra-row-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-icon {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-row-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n line-height: 1.25;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-actions {\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n margin-left: auto;\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-row:hover .ra-row-actions,\n.ra-shell .ra-row:focus-within .ra-row-actions {\n opacity: 1;\n}\n.ra-shell .ra-row-action {\n width: 1.6rem;\n height: 1.6rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .15s ease, color .15s ease;\n}\n.ra-shell .ra-row-action:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-action[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.12);\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.6875rem;\n font-weight: 500;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n white-space: nowrap;\n max-width: 14rem;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-chip[data-tone=success] {\n background: hsl(var(--ra-success) / 0.12);\n color: hsl(var(--ra-success));\n border-color: hsl(var(--ra-success) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=warning] {\n background: hsl(var(--ra-warning) / 0.14);\n color: hsl(var(--ra-warning));\n border-color: hsl(var(--ra-warning) / 0.35);\n}\n.ra-shell .ra-chip[data-tone=info] {\n background: hsl(var(--ra-info) / 0.10);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=danger] {\n background: hsl(var(--ra-danger) / 0.10);\n color: hsl(var(--ra-danger));\n border-color: hsl(var(--ra-danger) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=muted] {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-style: dashed;\n}\n.ra-shell .ra-group {\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-group:last-child {\n border-bottom: 0;\n}\n.ra-shell .ra-group-summary {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.6);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-group-summary:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-group-summary .ra-group-chevron {\n transition: transform .15s ease;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-chevron {\n transform: rotate(-90deg);\n}\n.ra-shell .ra-group-name {\n flex: 1;\n text-align: left;\n}\n.ra-shell .ra-group-count {\n font-size: 0.65rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.4rem;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-body {\n display: none;\n}\n.ra-shell .ra-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 2.5rem 1.5rem;\n gap: 0.75rem;\n}\n.ra-shell .ra-empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 3.25rem;\n height: 3.25rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.08);\n color: hsl(var(--ra-accent));\n margin-bottom: 0.25rem;\n}\n.ra-shell .ra-empty-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n color: hsl(var(--ra-text));\n margin: 0;\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-empty-body {\n font-size: 0.8125rem;\n color: hsl(var(--ra-muted-text));\n max-width: 22rem;\n line-height: 1.45;\n}\n.ra-shell .ra-empty-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n justify-content: center;\n}\n.ra-shell .ra-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.45rem 0.85rem;\n border-radius: calc(var(--ra-radius) * 0.7);\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n cursor: pointer;\n transition:\n background .15s ease,\n border-color .15s ease,\n box-shadow .15s ease,\n transform .1s ease;\n}\n.ra-shell .ra-btn:hover {\n background: hsl(var(--ra-muted));\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-btn:active {\n transform: translateY(1px);\n}\n.ra-shell .ra-btn[data-variant=primary] {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-surface));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-btn[data-variant=primary]:hover {\n background: hsl(var(--ra-accent) / 0.92);\n}\n.ra-shell .ra-btn[data-variant=ghost] {\n background: transparent;\n border-color: transparent;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-btn[data-variant=ghost]:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-btn[data-variant=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-btn[data-variant=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n border-color: hsl(var(--ra-danger) / 0.40);\n}\n.ra-shell .ra-intro {\n position: relative;\n display: flex;\n gap: 0.85rem;\n padding: 0.9rem 1rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-info) / 0.30);\n background: hsl(var(--ra-info) / 0.08);\n margin-bottom: 1rem;\n}\n.ra-shell .ra-intro[data-tone=success] {\n border-color: hsl(var(--ra-success) / 0.30);\n background: hsl(var(--ra-success) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning) / 0.35);\n background: hsl(var(--ra-warning) / 0.10);\n}\n.ra-shell .ra-intro-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info) / 0.18);\n color: hsl(var(--ra-info));\n}\n.ra-shell .ra-intro[data-tone=success] .ra-intro-icon {\n background: hsl(var(--ra-success) / 0.18);\n color: hsl(var(--ra-success));\n}\n.ra-shell .ra-intro[data-tone=warning] .ra-intro-icon {\n background: hsl(var(--ra-warning) / 0.20);\n color: hsl(var(--ra-warning));\n}\n.ra-shell .ra-intro-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-intro-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n color: hsl(var(--ra-text));\n margin: 0 0 0.2rem 0;\n}\n.ra-shell .ra-intro-text {\n font-size: 0.8125rem;\n color: hsl(var(--ra-text) / 0.85);\n line-height: 1.45;\n}\n.ra-shell .ra-intro-dismiss {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text));\n cursor: pointer;\n}\n.ra-shell .ra-intro-dismiss:hover {\n background: hsl(var(--ra-text) / 0.06);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-bulk-menu {\n min-width: 12rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow-hover);\n padding: 0.3rem;\n z-index: 60;\n}\n.ra-shell .ra-bulk-item {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n padding: 0.45rem 0.6rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-bulk-item:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-bulk-item[data-tone=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-bulk-item[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n}\n.ra-shell .ra-bulk-divider {\n height: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-preview-rail {\n background: hsl(var(--ra-surface));\n border-left: 1px solid hsl(var(--ra-border));\n box-shadow: -4px 0 16px hsl(var(--ra-accent) / 0.04);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n.ra-shell .ra-preview-rail-header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background:\n linear-gradient(\n 180deg,\n hsl(var(--ra-surface)) 0%,\n hsl(var(--ra-surface) / 0.92) 100%);\n border-bottom: 1px solid hsl(var(--ra-border));\n backdrop-filter: blur(6px);\n}\n.ra-shell .ra-preview-rail-title {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-preview-rail-body {\n flex: 1;\n overflow-y: auto;\n padding: 1rem;\n}\n.ra-confirm-root {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n}\n.ra-confirm-root .ra-confirm-backdrop {\n position: absolute;\n inset: 0;\n background: hsl(0 0% 0% / 0.45);\n backdrop-filter: blur(2px);\n animation: ra-confirm-fade .12s ease-out;\n}\n.ra-confirm-root .ra-confirm-card {\n position: relative;\n width: min(440px, 100%);\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 1px 2px hsl(0 0% 0% / 0.08), 0 24px 48px -12px hsl(0 0% 0% / 0.32);\n padding: 1.25rem;\n animation: ra-confirm-pop .14s ease-out;\n}\n.ra-confirm-root .ra-confirm-header {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n margin-bottom: 0.5rem;\n}\n.ra-confirm-root .ra-confirm-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: 999px;\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.12);\n color: hsl(var(--ra-warning, 38 92% 50%));\n}\n.ra-confirm-root .ra-confirm-title {\n font-family: var(--ra-font-display);\n font-weight: 600;\n font-size: 1rem;\n margin: 0;\n}\n.ra-confirm-root .ra-confirm-body {\n font-size: 0.875rem;\n color: hsl(var(--ra-muted-text));\n margin: 0 0 1.1rem;\n line-height: 1.45;\n}\n.ra-confirm-root .ra-confirm-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.ra-confirm-root .ra-confirm-btn {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.45rem 0.85rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease;\n}\n.ra-confirm-root .ra-confirm-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-confirm-root .ra-confirm-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-confirm-root .ra-confirm-btn-ghost:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-confirm-root .ra-confirm-btn-danger {\n background: transparent;\n color: hsl(var(--ra-danger, 0 72% 51%));\n border-color: hsl(var(--ra-danger, 0 72% 51%) / 0.45);\n}\n.ra-confirm-root .ra-confirm-btn-danger:hover {\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.08);\n border-color: hsl(var(--ra-danger, 0 72% 51%));\n}\n.ra-confirm-root .ra-confirm-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-confirm-root .ra-confirm-btn-primary:hover {\n filter: brightness(0.95);\n}\n@keyframes ra-confirm-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes ra-confirm-pop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ra-shell .ra-unsaved-banner {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 0.75rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: var(--ra-radius);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-warning, 38 92% 50%));\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-text {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ra-shell .ra-unsaved-context {\n color: hsl(var(--ra-muted-text));\n font-weight: 400;\n}\n.ra-shell .ra-unsaved-error {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.ra-shell .ra-unsaved-actions {\n display: inline-flex;\n gap: 0.4rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.3rem 0.6rem;\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease,\n opacity .12s ease;\n}\n.ra-shell .ra-unsaved-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ra-shell .ra-unsaved-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-shell .ra-unsaved-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-shell .ra-unsaved-btn-ghost:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-unsaved-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-unsaved-btn-primary:hover:not(:disabled) {\n filter: brightness(0.95);\n}\n@keyframes ra-unsaved-slide {\n from {\n opacity: 0;\n transform: translateY(-3px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n");
2988
+ styleInject(".ra-shell {\n color: hsl(var(--ra-text));\n background: hsl(var(--ra-page-bg));\n font-family: var(--ra-font-ui);\n}\n.ra-shell *,\n.ra-shell *::before,\n.ra-shell *::after {\n box-sizing: border-box;\n}\n.ra-shell .ra-card {\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-card-hover {\n transition:\n box-shadow .18s ease,\n transform .18s ease,\n border-color .18s ease;\n}\n.ra-shell .ra-card-hover:hover {\n box-shadow: var(--ra-card-shadow-hover);\n}\n.ra-shell .ra-display {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-title {\n font-weight: var(--ra-title-weight);\n}\n.ra-shell :where(button, [role=button], input, select, textarea, a):focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n border-radius: calc(var(--ra-radius) * 0.6);\n}\n.ra-shell .ra-header {\n position: relative;\n display: flex;\n align-items: center;\n gap: 0.75rem;\n padding: 0.65rem 0.9rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-accent) / 0.12);\n background:\n linear-gradient(\n 135deg,\n hsl(var(--ra-accent) / 0.08),\n hsl(var(--ra-accent) / 0.02) 60%,\n hsl(var(--ra-surface)) 100%);\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-header__main {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 0.625rem;\n}\n.ra-shell .ra-header-aside {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-header-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: calc(var(--ra-radius) * 0.9);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n border: 1px solid hsl(var(--ra-accent) / 0.18);\n}\n.ra-shell .ra-header-text {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-header-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n line-height: 1.2;\n color: hsl(var(--ra-text));\n letter-spacing: -0.01em;\n margin: 0;\n}\n.ra-shell .ra-header-subtitle {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.125rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-header-stats {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n padding: 0.15rem 0.4rem;\n border-radius: calc(var(--ra-radius) * 0.75);\n background: hsl(var(--ra-surface) / 0.7);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-header-stats--titled {\n flex-direction: column;\n align-items: stretch;\n padding: 0.4rem 0.55rem;\n gap: 0.3rem;\n}\n.ra-shell .ra-header-stats .ra-stats-items {\n display: flex;\n align-items: stretch;\n gap: 0.15rem;\n}\n.ra-shell .ra-header-stats .ra-stats-heading {\n display: flex;\n align-items: center;\n gap: 0.35rem;\n color: hsl(var(--ra-muted-text));\n font-size: 0.65rem;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon {\n display: inline-flex;\n align-items: center;\n color: hsl(var(--ra-text));\n opacity: 0.75;\n}\n.ra-shell .ra-header-stats .ra-stats-heading-icon > svg {\n width: 0.85rem;\n height: 0.85rem;\n}\n.ra-shell .ra-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 0.15rem 0.45rem;\n min-width: 2.5rem;\n}\n.ra-shell .ra-stat-value {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 0.85rem;\n color: hsl(var(--ra-text));\n line-height: 1;\n}\n.ra-shell .ra-stat-label {\n font-size: 0.6rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n}\n.ra-shell .ra-stat-divider {\n width: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-header-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n}\n.ra-shell .ra-tabs {\n display: flex;\n gap: 0.25rem;\n padding: 0.25rem;\n background: hsl(var(--ra-muted));\n border-radius: calc(var(--ra-radius) * 0.85);\n border: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-tab {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.4rem 0.7rem;\n border-radius: calc(var(--ra-radius) * 0.65);\n font-size: 0.78rem;\n font-weight: 500;\n color: hsl(var(--ra-muted-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n transition:\n background .15s ease,\n color .15s ease,\n transform .15s ease;\n white-space: nowrap;\n}\n.ra-shell .ra-tab:hover {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-tab[aria-selected=true] {\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n box-shadow: var(--ra-card-shadow);\n font-weight: var(--ra-title-weight);\n}\n.ra-shell .ra-tab[aria-selected=true] .ra-tab-icon {\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-tab[disabled] {\n opacity: .5;\n cursor: not-allowed;\n}\n.ra-shell .ra-tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 1.25rem;\n padding: 0 0.35rem;\n height: 1.1rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.12);\n color: hsl(var(--ra-accent));\n font-size: 0.625rem;\n font-weight: 600;\n line-height: 1;\n}\n.ra-shell .ra-tab[aria-selected=false] .ra-tab-count {\n background: hsl(var(--ra-muted-text) / 0.15);\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell[data-density=compact] .ra-row {\n padding-block: 0.4rem;\n}\n.ra-shell[data-density=compact] .ra-header {\n padding: 0.75rem 1rem;\n}\n.ra-shell[data-density=compact] .ra-header-icon {\n width: 2.25rem;\n height: 2.25rem;\n}\n.ra-shell .ra-row {\n display: flex;\n align-items: center;\n gap: 0.65rem;\n width: 100%;\n text-align: left;\n padding: 0.65rem 0.85rem;\n border-left: 3px solid transparent;\n background: transparent;\n border-bottom: 1px solid transparent;\n transition: background .12s ease, border-color .12s ease;\n cursor: pointer;\n color: hsl(var(--ra-text));\n font-family: inherit;\n}\n.ra-shell .ra-row + .ra-row {\n border-top: 1px solid hsl(var(--ra-border) / 0.6);\n}\n.ra-shell .ra-row:hover {\n background: var(--ra-row-hover);\n}\n.ra-shell .ra-row[data-selected=true] {\n background: var(--ra-row-active-bg);\n border-left-color: var(--ra-row-active-bd);\n}\n.ra-shell .ra-row-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n flex-shrink: 0;\n}\n.ra-shell .ra-row[data-selected=true] .ra-row-icon {\n background: hsl(var(--ra-accent) / 0.15);\n color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-row-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-row-title {\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n line-height: 1.25;\n color: hsl(var(--ra-text));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-sub {\n font-size: 0.75rem;\n color: hsl(var(--ra-muted-text));\n margin-top: 0.15rem;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-row-actions {\n display: inline-flex;\n align-items: center;\n gap: 0.15rem;\n margin-left: auto;\n opacity: 0;\n transition: opacity .15s ease;\n}\n.ra-shell .ra-row:hover .ra-row-actions,\n.ra-shell .ra-row:focus-within .ra-row-actions {\n opacity: 1;\n}\n.ra-shell .ra-row-action {\n width: 1.6rem;\n height: 1.6rem;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border-radius: 999px;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .15s ease, color .15s ease;\n}\n.ra-shell .ra-row-action:hover {\n background: hsl(var(--ra-accent) / 0.10);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-action[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.12);\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-chip {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n padding: 0.15rem 0.5rem;\n border-radius: 999px;\n font-size: 0.6875rem;\n font-weight: 500;\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-muted-text));\n border: 1px solid hsl(var(--ra-border));\n white-space: nowrap;\n max-width: 14rem;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.ra-shell .ra-chip[data-tone=success] {\n background: hsl(var(--ra-success) / 0.12);\n color: hsl(var(--ra-success));\n border-color: hsl(var(--ra-success) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=warning] {\n background: hsl(var(--ra-warning) / 0.14);\n color: hsl(var(--ra-warning));\n border-color: hsl(var(--ra-warning) / 0.35);\n}\n.ra-shell .ra-chip[data-tone=info] {\n background: hsl(var(--ra-info) / 0.10);\n color: hsl(var(--ra-info));\n border-color: hsl(var(--ra-info) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=danger] {\n background: hsl(var(--ra-danger) / 0.10);\n color: hsl(var(--ra-danger));\n border-color: hsl(var(--ra-danger) / 0.30);\n}\n.ra-shell .ra-chip[data-tone=muted] {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-style: dashed;\n}\n.ra-shell .ra-group {\n border-bottom: 1px solid hsl(var(--ra-border));\n}\n.ra-shell .ra-group:last-child {\n border-bottom: 0;\n}\n.ra-shell .ra-group-summary {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n width: 100%;\n padding: 0.5rem 0.85rem;\n background: hsl(var(--ra-muted) / 0.6);\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: hsl(var(--ra-muted-text));\n border: 0;\n cursor: pointer;\n transition: background .12s ease;\n}\n.ra-shell .ra-group-summary:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-group-summary .ra-group-chevron {\n transition: transform .15s ease;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-chevron {\n transform: rotate(-90deg);\n}\n.ra-shell .ra-group-name {\n flex: 1;\n text-align: left;\n}\n.ra-shell .ra-group-count {\n font-size: 0.65rem;\n font-weight: 600;\n color: hsl(var(--ra-muted-text));\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: 999px;\n padding: 0.05rem 0.4rem;\n}\n.ra-shell .ra-group[data-open=false] .ra-group-body {\n display: none;\n}\n.ra-shell .ra-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n text-align: center;\n padding: 2.5rem 1.5rem;\n gap: 0.75rem;\n}\n.ra-shell .ra-empty-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 3.25rem;\n height: 3.25rem;\n border-radius: 999px;\n background: hsl(var(--ra-accent) / 0.08);\n color: hsl(var(--ra-accent));\n margin-bottom: 0.25rem;\n}\n.ra-shell .ra-empty-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-display-weight);\n font-size: 1rem;\n color: hsl(var(--ra-text));\n margin: 0;\n letter-spacing: -0.01em;\n}\n.ra-shell .ra-empty-body {\n font-size: 0.8125rem;\n color: hsl(var(--ra-muted-text));\n max-width: 22rem;\n line-height: 1.45;\n}\n.ra-shell .ra-empty-actions {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n margin-top: 0.25rem;\n flex-wrap: wrap;\n justify-content: center;\n}\n.ra-shell .ra-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n padding: 0.45rem 0.85rem;\n border-radius: calc(var(--ra-radius) * 0.7);\n font-size: 0.8125rem;\n font-weight: 500;\n border: 1px solid hsl(var(--ra-border));\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n cursor: pointer;\n transition:\n background .15s ease,\n border-color .15s ease,\n box-shadow .15s ease,\n transform .1s ease;\n}\n.ra-shell .ra-btn:hover {\n background: hsl(var(--ra-muted));\n box-shadow: var(--ra-card-shadow);\n}\n.ra-shell .ra-btn:active {\n transform: translateY(1px);\n}\n.ra-shell .ra-btn[data-variant=primary] {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-surface));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-btn[data-variant=primary]:hover {\n background: hsl(var(--ra-accent) / 0.92);\n}\n.ra-shell .ra-btn[data-variant=ghost] {\n background: transparent;\n border-color: transparent;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-btn[data-variant=ghost]:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-btn[data-variant=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-btn[data-variant=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n border-color: hsl(var(--ra-danger) / 0.40);\n}\n.ra-shell .ra-intro {\n position: relative;\n display: flex;\n gap: 0.85rem;\n padding: 0.9rem 1rem;\n border-radius: var(--ra-radius);\n border: 1px solid hsl(var(--ra-info) / 0.30);\n background: hsl(var(--ra-info) / 0.08);\n margin-bottom: 1rem;\n}\n.ra-shell .ra-intro[data-tone=success] {\n border-color: hsl(var(--ra-success) / 0.30);\n background: hsl(var(--ra-success) / 0.08);\n}\n.ra-shell .ra-intro[data-tone=warning] {\n border-color: hsl(var(--ra-warning) / 0.35);\n background: hsl(var(--ra-warning) / 0.10);\n}\n.ra-shell .ra-intro-icon {\n flex-shrink: 0;\n width: 2rem;\n height: 2rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: hsl(var(--ra-info) / 0.18);\n color: hsl(var(--ra-info));\n}\n.ra-shell .ra-intro[data-tone=success] .ra-intro-icon {\n background: hsl(var(--ra-success) / 0.18);\n color: hsl(var(--ra-success));\n}\n.ra-shell .ra-intro[data-tone=warning] .ra-intro-icon {\n background: hsl(var(--ra-warning) / 0.20);\n color: hsl(var(--ra-warning));\n}\n.ra-shell .ra-intro-body {\n flex: 1;\n min-width: 0;\n}\n.ra-shell .ra-intro-title {\n font-family: var(--ra-font-display);\n font-weight: var(--ra-title-weight);\n font-size: 0.875rem;\n color: hsl(var(--ra-text));\n margin: 0 0 0.2rem 0;\n}\n.ra-shell .ra-intro-text {\n font-size: 0.8125rem;\n color: hsl(var(--ra-text) / 0.85);\n line-height: 1.45;\n}\n.ra-shell .ra-intro-dismiss {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n width: 1.6rem;\n height: 1.6rem;\n border-radius: 999px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: 0;\n color: hsl(var(--ra-muted-text));\n cursor: pointer;\n}\n.ra-shell .ra-intro-dismiss:hover {\n background: hsl(var(--ra-text) / 0.06);\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-bulk-menu {\n min-width: 12rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n border-radius: calc(var(--ra-radius) * 0.85);\n box-shadow: var(--ra-card-shadow-hover);\n padding: 0.3rem;\n z-index: 60;\n}\n.ra-shell .ra-bulk-item {\n display: flex;\n align-items: center;\n gap: 0.55rem;\n width: 100%;\n padding: 0.45rem 0.6rem;\n border-radius: calc(var(--ra-radius) * 0.6);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n cursor: pointer;\n text-align: left;\n transition: background .12s ease, color .12s ease;\n}\n.ra-shell .ra-bulk-item:hover {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-bulk-item[data-tone=danger] {\n color: hsl(var(--ra-danger));\n}\n.ra-shell .ra-bulk-item[data-tone=danger]:hover {\n background: hsl(var(--ra-danger) / 0.10);\n}\n.ra-shell .ra-bulk-divider {\n height: 1px;\n background: hsl(var(--ra-border));\n margin: 0.25rem 0;\n}\n.ra-shell .ra-preview-rail {\n background: hsl(var(--ra-surface));\n border-left: 1px solid hsl(var(--ra-border));\n box-shadow: -4px 0 16px hsl(var(--ra-accent) / 0.04);\n display: flex;\n flex-direction: column;\n height: 100%;\n overflow: hidden;\n}\n.ra-shell .ra-preview-rail-header {\n position: sticky;\n top: 0;\n z-index: 1;\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1rem;\n background:\n linear-gradient(\n 180deg,\n hsl(var(--ra-surface)) 0%,\n hsl(var(--ra-surface) / 0.92) 100%);\n border-bottom: 1px solid hsl(var(--ra-border));\n backdrop-filter: blur(6px);\n}\n.ra-shell .ra-preview-rail-title {\n display: inline-flex;\n align-items: center;\n gap: 0.4rem;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n color: hsl(var(--ra-muted-text));\n}\n.ra-shell .ra-preview-rail-body {\n flex: 1;\n overflow-y: auto;\n padding: 1rem;\n}\n.ra-confirm-root {\n position: fixed;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1rem;\n}\n.ra-confirm-root .ra-confirm-backdrop {\n position: absolute;\n inset: 0;\n background: hsl(0 0% 0% / 0.45);\n backdrop-filter: blur(2px);\n animation: ra-confirm-fade .12s ease-out;\n}\n.ra-confirm-root .ra-confirm-card {\n position: relative;\n width: min(440px, 100%);\n background: hsl(var(--ra-surface));\n color: hsl(var(--ra-text));\n border: 1px solid hsl(var(--ra-border));\n border-radius: var(--ra-radius);\n box-shadow: 0 1px 2px hsl(0 0% 0% / 0.08), 0 24px 48px -12px hsl(0 0% 0% / 0.32);\n padding: 1.25rem;\n animation: ra-confirm-pop .14s ease-out;\n}\n.ra-confirm-root .ra-confirm-header {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n margin-bottom: 0.5rem;\n}\n.ra-confirm-root .ra-confirm-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.75rem;\n height: 1.75rem;\n border-radius: 999px;\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.12);\n color: hsl(var(--ra-warning, 38 92% 50%));\n}\n.ra-confirm-root .ra-confirm-title {\n font-family: var(--ra-font-display);\n font-weight: 600;\n font-size: 1rem;\n margin: 0;\n}\n.ra-confirm-root .ra-confirm-body {\n font-size: 0.875rem;\n color: hsl(var(--ra-muted-text));\n margin: 0 0 1.1rem;\n line-height: 1.45;\n}\n.ra-confirm-root .ra-confirm-actions {\n display: flex;\n justify-content: flex-end;\n gap: 0.5rem;\n flex-wrap: wrap;\n}\n.ra-confirm-root .ra-confirm-btn {\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.45rem 0.85rem;\n font-size: 0.8125rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease;\n}\n.ra-confirm-root .ra-confirm-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-confirm-root .ra-confirm-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-confirm-root .ra-confirm-btn-ghost:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-confirm-root .ra-confirm-btn-danger {\n background: transparent;\n color: hsl(var(--ra-danger, 0 72% 51%));\n border-color: hsl(var(--ra-danger, 0 72% 51%) / 0.45);\n}\n.ra-confirm-root .ra-confirm-btn-danger:hover {\n background: hsl(var(--ra-danger, 0 72% 51%) / 0.08);\n border-color: hsl(var(--ra-danger, 0 72% 51%));\n}\n.ra-confirm-root .ra-confirm-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-confirm-root .ra-confirm-btn-primary:hover {\n filter: brightness(0.95);\n}\n@keyframes ra-confirm-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n@keyframes ra-confirm-pop {\n from {\n opacity: 0;\n transform: translateY(4px) scale(.98);\n }\n to {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n.ra-shell .ra-unsaved-banner {\n display: flex;\n align-items: center;\n gap: 0.6rem;\n padding: 0.5rem 0.75rem;\n border: 1px solid hsl(var(--ra-warning, 38 92% 50%) / 0.35);\n background: hsl(var(--ra-warning, 38 92% 50%) / 0.08);\n border-radius: var(--ra-radius);\n font-size: 0.8125rem;\n color: hsl(var(--ra-text));\n animation: ra-unsaved-slide .14s ease-out;\n}\n.ra-shell .ra-unsaved-icon {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n color: hsl(var(--ra-warning, 38 92% 50%));\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-text {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.ra-shell .ra-unsaved-context {\n color: hsl(var(--ra-muted-text));\n font-weight: 400;\n}\n.ra-shell .ra-unsaved-error {\n color: hsl(var(--ra-danger, 0 72% 51%));\n font-size: 0.75rem;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n font-weight: 500;\n}\n.ra-shell .ra-unsaved-actions {\n display: inline-flex;\n gap: 0.4rem;\n flex-shrink: 0;\n}\n.ra-shell .ra-unsaved-btn {\n display: inline-flex;\n align-items: center;\n gap: 0.3rem;\n -webkit-appearance: none;\n -moz-appearance: none;\n appearance: none;\n border: 1px solid transparent;\n border-radius: calc(var(--ra-radius) - 2px);\n padding: 0.3rem 0.6rem;\n font-size: 0.75rem;\n font-weight: 500;\n cursor: pointer;\n transition:\n background-color .12s ease,\n border-color .12s ease,\n color .12s ease,\n opacity .12s ease;\n}\n.ra-shell .ra-unsaved-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.ra-shell .ra-unsaved-btn:focus-visible {\n outline: none;\n box-shadow: 0 0 0 3px var(--ra-focus-ring);\n}\n.ra-shell .ra-unsaved-btn-ghost {\n background: transparent;\n color: hsl(var(--ra-muted-text));\n border-color: hsl(var(--ra-border));\n}\n.ra-shell .ra-unsaved-btn-ghost:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-unsaved-btn-primary {\n background: hsl(var(--ra-accent));\n color: hsl(var(--ra-accent-fg, 0 0% 100%));\n border-color: hsl(var(--ra-accent));\n}\n.ra-shell .ra-unsaved-btn-primary:hover:not(:disabled) {\n filter: brightness(0.95);\n}\n@keyframes ra-unsaved-slide {\n from {\n opacity: 0;\n transform: translateY(-3px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n.ra-shell .ra-clipboard-toast {\n position: fixed;\n bottom: 1.25rem;\n left: 50%;\n transform: translateX(-50%);\n z-index: 90;\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n max-width: min(28rem, calc(100vw - 2rem));\n padding: 0.55rem 0.85rem;\n border-radius: 999px;\n background: hsl(var(--ra-text));\n color: hsl(var(--ra-surface));\n font-size: 0.75rem;\n line-height: 1;\n box-shadow: 0 8px 24px -10px hsl(0 0% 0% / 0.45);\n animation: ra-clipboard-pop 0.18s ease-out both;\n pointer-events: none;\n}\n@keyframes ra-clipboard-pop {\n from {\n opacity: 0;\n transform: translate(-50%, 6px);\n }\n to {\n opacity: 1;\n transform: translate(-50%, 0);\n }\n}\n.ra-shell .ra-row-menu-wrap {\n display: inline-flex;\n align-items: center;\n margin-left: 0.25rem;\n}\n.ra-shell .ra-row-menu-trigger {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 1.5rem;\n height: 1.5rem;\n border-radius: 0.35rem;\n background: transparent;\n color: hsl(var(--ra-muted-text));\n opacity: 0;\n transition:\n opacity .15s ease,\n background .15s ease,\n color .15s ease;\n border: 1px solid transparent;\n}\n.ra-shell .ra-row:hover .ra-row-menu-trigger,\n.ra-shell .ra-card-hover:hover .ra-row-menu-trigger,\n.ra-shell .ra-row-menu-trigger:focus-visible,\n.ra-shell .ra-row-menu-trigger[aria-expanded=true] {\n opacity: 1;\n}\n.ra-shell .ra-row-menu-trigger:hover {\n background: hsl(var(--ra-muted));\n color: hsl(var(--ra-text));\n}\n.ra-shell .ra-row-menu {\n position: absolute;\n right: 0;\n top: calc(100% + 4px);\n z-index: 50;\n min-width: 11rem;\n padding: 0.25rem;\n border-radius: 0.5rem;\n background: hsl(var(--ra-surface));\n border: 1px solid hsl(var(--ra-border));\n box-shadow: 0 12px 28px -10px hsl(0 0% 0% / 0.25);\n display: flex;\n flex-direction: column;\n gap: 0.125rem;\n}\n.ra-shell .ra-row-menu-item {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.4rem 0.55rem;\n border-radius: 0.35rem;\n font-size: 0.75rem;\n color: hsl(var(--ra-text));\n background: transparent;\n border: 0;\n text-align: left;\n width: 100%;\n cursor: pointer;\n}\n.ra-shell .ra-row-menu-item:hover:not(:disabled) {\n background: hsl(var(--ra-muted));\n}\n.ra-shell .ra-row-menu-item:disabled {\n opacity: 0.45;\n cursor: not-allowed;\n}\n");
2660
2989
  var TOP_LEVEL_SCOPES = ["facet", "product"];
2661
2990
  var defaultItemId = () => {
2662
2991
  const time = Date.now().toString(36);
@@ -2724,10 +3053,23 @@ function RecordsAdminShell(props) {
2724
3053
  icons: iconsOverride,
2725
3054
  groupBy,
2726
3055
  renderEmptyState,
2727
- density = "comfortable"
3056
+ density = "comfortable",
3057
+ enableClipboard = true,
3058
+ onCopy: onCopyOverride,
3059
+ onPaste: onPasteOverride,
3060
+ actionLabels,
3061
+ actionIcons,
3062
+ editorTabs = "off"
2728
3063
  } = props;
2729
3064
  const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
2730
3065
  const icons = useMemo(() => mergeIcons(iconsOverride), [iconsOverride]);
3066
+ const multiOpenWarnedRef = useRef(false);
3067
+ if (editorTabs === "multi" && !multiOpenWarnedRef.current) {
3068
+ multiOpenWarnedRef.current = true;
3069
+ console.warn(
3070
+ '[RecordsAdminShell] editorTabs="multi" is reserved for a future release and currently behaves like "off". Track in records-admin-shell.md.'
3071
+ );
3072
+ }
2731
3073
  const [presentation, setPresentation] = usePresentationPref({
2732
3074
  appId,
2733
3075
  recordType,
@@ -2915,6 +3257,12 @@ function RecordsAdminShell(props) {
2915
3257
  disableParentMessage: dirtyStrategy === "keep"
2916
3258
  });
2917
3259
  const dirtyConfirm = useConfirmDialog();
3260
+ const pasteConfirm = usePasteConfirm();
3261
+ const clipboard = useRecordClipboard({
3262
+ appId,
3263
+ recordType: recordType ?? "__default"
3264
+ });
3265
+ const [clipboardNotice, setClipboardNotice] = useState(null);
2918
3266
  const { runWithGuard } = useDirtyNavigation({
2919
3267
  strategy: dirtyStrategy,
2920
3268
  isDirty: editorCtx.isDirty,
@@ -2991,6 +3339,161 @@ function RecordsAdminShell(props) {
2991
3339
  }
2992
3340
  return void 0;
2993
3341
  }, [activeScope, editingScope, selectedProductId, productBrowse.items]);
3342
+ const copyCurrent = useCallback(() => {
3343
+ if (!enableClipboard || !editingScope) return;
3344
+ const sourceValue = onCopyOverride ? onCopyOverride({ value: editorCtx.value, scope: editingScope }) : cloneValue(editorCtx.value);
3345
+ clipboard.set({
3346
+ value: sourceValue,
3347
+ sourceScope: editingScope.kind,
3348
+ sourceRef: editingScope.raw,
3349
+ sourceLabel: editorHeaderLabel
3350
+ });
3351
+ onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: editingScope.raw });
3352
+ setClipboardNotice({
3353
+ message: i18n.copyToast.replace("{name}", editorHeaderLabel ?? editingScope.raw),
3354
+ variant: "copy"
3355
+ });
3356
+ }, [
3357
+ enableClipboard,
3358
+ editingScope,
3359
+ onCopyOverride,
3360
+ editorCtx.value,
3361
+ clipboard,
3362
+ editorHeaderLabel,
3363
+ onTelemetry,
3364
+ recordType,
3365
+ i18n.copyToast
3366
+ ]);
3367
+ const pasteCurrent = useCallback(async () => {
3368
+ if (!enableClipboard || !editingScope || !clipboard.entry) return;
3369
+ const compat = checkPasteCompatibility(
3370
+ { kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
3371
+ editingScope
3372
+ );
3373
+ if (compat.status === "denied") {
3374
+ setClipboardNotice({ message: compat.reason ?? "Paste not allowed here", variant: "paste" });
3375
+ return;
3376
+ }
3377
+ if (compat.status === "warn") {
3378
+ const ok = await pasteConfirm.confirm({
3379
+ title: i18n.pasteWarnTitle,
3380
+ body: compat.reason ?? "",
3381
+ confirmLabel: i18n.pasteWarnContinue,
3382
+ cancelLabel: i18n.pasteConfirmCancel
3383
+ });
3384
+ if (!ok) return;
3385
+ }
3386
+ const willReplace = resolved.source === "self";
3387
+ if (willReplace) {
3388
+ const ok = await pasteConfirm.confirm({
3389
+ title: i18n.pasteConfirmTitle,
3390
+ body: i18n.pasteConfirmBody.replace(
3391
+ "{destination}",
3392
+ editorHeaderLabel ?? editingScope.raw
3393
+ ),
3394
+ confirmLabel: i18n.pasteConfirmReplace,
3395
+ cancelLabel: i18n.pasteConfirmCancel
3396
+ });
3397
+ if (!ok) return;
3398
+ }
3399
+ const sourceParsed = { kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef };
3400
+ const transformed = onPasteOverride ? onPasteOverride(
3401
+ { value: clipboard.entry.value, sourceScope: sourceParsed },
3402
+ { scope: editingScope, currentValue: editorCtx.value ?? null }
3403
+ ) : cloneValue(clipboard.entry.value);
3404
+ if (transformed === null) return;
3405
+ editorCtx.onChange(transformed);
3406
+ onTelemetry?.({
3407
+ type: "clipboard.paste",
3408
+ recordType,
3409
+ sourceRef: clipboard.entry.sourceRef,
3410
+ destinationRef: editingScope.raw,
3411
+ replaced: willReplace
3412
+ });
3413
+ setClipboardNotice({
3414
+ message: i18n.pasteToast.replace("{name}", clipboard.entry.sourceLabel ?? clipboard.entry.sourceRef),
3415
+ variant: "paste"
3416
+ });
3417
+ }, [
3418
+ enableClipboard,
3419
+ editingScope,
3420
+ clipboard.entry,
3421
+ pasteConfirm,
3422
+ resolved.source,
3423
+ onPasteOverride,
3424
+ editorCtx,
3425
+ onTelemetry,
3426
+ recordType,
3427
+ editorHeaderLabel,
3428
+ i18n.pasteWarnTitle,
3429
+ i18n.pasteWarnContinue,
3430
+ i18n.pasteConfirmCancel,
3431
+ i18n.pasteConfirmTitle,
3432
+ i18n.pasteConfirmBody,
3433
+ i18n.pasteConfirmReplace,
3434
+ i18n.pasteToast
3435
+ ]);
3436
+ const editorPasteCompat = useMemo(() => {
3437
+ if (!enableClipboard || !clipboard.entry || !editingScope) return null;
3438
+ if (clipboard.entry.sourceRef === editingScope.raw) return { status: "denied" };
3439
+ return checkPasteCompatibility(
3440
+ { kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
3441
+ editingScope
3442
+ );
3443
+ }, [enableClipboard, clipboard.entry, editingScope]);
3444
+ const editorClipboard = enableClipboard && editingScope ? {
3445
+ onCopy: copyCurrent,
3446
+ onPaste: () => {
3447
+ void pasteCurrent();
3448
+ },
3449
+ canCopy: true,
3450
+ canPaste: !!clipboard.entry && editorPasteCompat?.status !== "denied",
3451
+ pasteSourceLabel: clipboard.entry?.sourceLabel,
3452
+ pasteWillReplace: resolved.source === "self" && !!clipboard.entry
3453
+ } : void 0;
3454
+ const [pendingPasteRef, setPendingPasteRef] = useState(null);
3455
+ useEffect(() => {
3456
+ if (!pendingPasteRef) return;
3457
+ if (!editingScope || editingScope.raw !== pendingPasteRef) return;
3458
+ const t = window.setTimeout(() => {
3459
+ setPendingPasteRef(null);
3460
+ void pasteCurrent();
3461
+ }, 0);
3462
+ return () => window.clearTimeout(t);
3463
+ }, [pendingPasteRef, editingScope, pasteCurrent]);
3464
+ const rowClipboard = enableClipboard ? (record) => {
3465
+ const summaryHasData = record.data != null;
3466
+ const sourceParsed = record.scope;
3467
+ const compat = clipboard.entry ? checkPasteCompatibility(
3468
+ { kind: clipboard.entry.sourceScope, raw: clipboard.entry.sourceRef },
3469
+ sourceParsed
3470
+ ) : null;
3471
+ const sameRef = clipboard.entry?.sourceRef === record.ref;
3472
+ return {
3473
+ onCopy: summaryHasData ? () => {
3474
+ const value = onCopyOverride ? onCopyOverride({ value: record.data, scope: sourceParsed }) : cloneValue(record.data);
3475
+ clipboard.set({
3476
+ value,
3477
+ sourceScope: sourceParsed.kind,
3478
+ sourceRef: record.ref,
3479
+ sourceLabel: record.label
3480
+ });
3481
+ onTelemetry?.({ type: "clipboard.copy", recordType, sourceRef: record.ref });
3482
+ setClipboardNotice({
3483
+ message: i18n.copyToast.replace("{name}", record.label),
3484
+ variant: "copy"
3485
+ });
3486
+ } : void 0,
3487
+ onPaste: () => {
3488
+ onLeftSelectRef.current?.(record);
3489
+ setPendingPasteRef(record.ref);
3490
+ },
3491
+ canPaste: !!clipboard.entry && !sameRef && compat?.status !== "denied",
3492
+ pasteWillReplace: record.status === "configured",
3493
+ clipboardSourceLabel: clipboard.entry?.sourceLabel
3494
+ };
3495
+ } : void 0;
3496
+ const onLeftSelectRef = useRef(null);
2994
3497
  const renderEditorWithPreview = () => {
2995
3498
  if (!editingScope) return null;
2996
3499
  const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
@@ -3019,6 +3522,9 @@ function RecordsAdminShell(props) {
3019
3522
  headerLabel: editorHeaderLabel,
3020
3523
  headerSubtitle: editorHeaderSubtitle,
3021
3524
  headerMeta: editorHeaderMeta,
3525
+ clipboard: editorClipboard,
3526
+ actionLabels,
3527
+ actionIcons,
3022
3528
  children: renderEditor(editorCtx)
3023
3529
  }
3024
3530
  );
@@ -3125,6 +3631,7 @@ function RecordsAdminShell(props) {
3125
3631
  }
3126
3632
  });
3127
3633
  };
3634
+ onLeftSelectRef.current = onLeftSelect;
3128
3635
  return /* @__PURE__ */ jsxs(
3129
3636
  "div",
3130
3637
  {
@@ -3132,6 +3639,15 @@ function RecordsAdminShell(props) {
3132
3639
  "data-density": density,
3133
3640
  children: [
3134
3641
  dirtyConfirm.dialog,
3642
+ pasteConfirm.dialog,
3643
+ clipboardNotice && /* @__PURE__ */ jsx(
3644
+ ClipboardToast,
3645
+ {
3646
+ message: clipboardNotice.message,
3647
+ variant: clipboardNotice.variant,
3648
+ onDismiss: () => setClipboardNotice(null)
3649
+ }
3650
+ ),
3135
3651
  /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 space-y-3", children: [
3136
3652
  intro && !dismissed && /* @__PURE__ */ jsx(
3137
3653
  IntroCard,
@@ -3189,8 +3705,10 @@ function RecordsAdminShell(props) {
3189
3705
  });
3190
3706
  },
3191
3707
  onDiscard: editorCtx.reset,
3192
- saveLabel: i18n.save,
3193
- discardLabel: i18n.discard,
3708
+ saveLabel: actionLabels?.save ?? i18n.save,
3709
+ discardLabel: actionLabels?.discard ?? i18n.discard,
3710
+ SaveIcon: actionIcons?.save,
3711
+ DiscardIcon: actionIcons?.discard,
3194
3712
  savingLabel: i18n.saving,
3195
3713
  errorLabel: i18n.saveError,
3196
3714
  bodyTemplate: i18n.unsavedBannerBody
@@ -3303,7 +3821,8 @@ function RecordsAdminShell(props) {
3303
3821
  presentation: effectivePresentation,
3304
3822
  renderListRow,
3305
3823
  renderCard,
3306
- groupBy: effectiveGroupBy
3824
+ groupBy: effectiveGroupBy,
3825
+ rowClipboard
3307
3826
  }
3308
3827
  ),
3309
3828
  isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
@@ -3331,6 +3850,7 @@ function RecordsAdminShell(props) {
3331
3850
  productLabel: productBrowse.items.find((p) => p.id === selectedProductId)?.name ?? selectedProductId,
3332
3851
  showVariants: drillVariantsAllowed,
3333
3852
  showBatches: drillBatchesAllowed,
3853
+ hideSingleTab: editorTabs === "off" || editorTabs === "multi",
3334
3854
  active: drillTab,
3335
3855
  onChange: (t) => {
3336
3856
  void runWithGuard(() => {
@@ -3763,6 +4283,6 @@ function useMergedRecord(args) {
3763
4283
  };
3764
4284
  }
3765
4285
 
3766
- export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
4286
+ export { ALL_PRESENTATIONS, BatchList, BulkActionsMenu, DEFAULT_I18N, DEFAULT_ICONS, DefaultRecordCard, DefaultRecordRow, DeleteButton, DrawerPreview, EmptyState, ErrorState, FacetList, InheritanceMarker, InheritanceProvider, InlinePreview, IntroCard, LoadingState, PresentationSwitcher, PreviewScopePicker, PreviewToggleButton, ProductDrillDown, ProductList, RecordBrowser, RecordEditor, RecordList, RecordsAdminShell, ResolvedPreview, ScopeBreadcrumb, ScopeTabs, SidePreview, StatusDot, StatusFilterPills, TabbedPreview, UtilityRow, VariantList, buildRef, bulkDelete, bulkUpsert, checkPasteCompatibility, cloneValue, deleteRecord, downloadBlob, exportCsv, getRecordByRef, importCsv, listRecords, matchRecords, mergeIcons, parseRef, parsedRefToScope, parsedRefToTarget, pickHeaderIcon, resolutionChain, resolveRecord, restoreRecord, scopesEqual, upsertRecord, useCollectedRecords, useDirtyNavigation, useFacetBrowse, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordClipboard, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
3767
4287
  //# sourceMappingURL=index.js.map
3768
4288
  //# sourceMappingURL=index.js.map