@proveanything/smartlinks-utils-ui 0.3.7 → 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,8 +1,9 @@
1
- import { styleInject } from '../../chunk-WFNEZQCD.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, AlertTriangle, Info, X, HelpCircle, Search, CornerDownLeft, Circle } 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
+ import { createPortal } from 'react-dom';
6
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
7
8
 
8
9
  var DEFAULT_ICONS = {
@@ -83,6 +84,7 @@ var DEFAULT_I18N = {
83
84
  unsavedPromptDiscard: "Discard changes",
84
85
  unsavedPromptCancel: "Stay here",
85
86
  unsavedPromptSave: "Save & continue",
87
+ unsavedBannerBody: "Unsaved changes on {name}",
86
88
  presentationList: "List",
87
89
  presentationGrid: "Grid",
88
90
  presentationGallery: "Gallery",
@@ -91,7 +93,20 @@ var DEFAULT_I18N = {
91
93
  noItemsTitle: "No items yet",
92
94
  noItemsBody: "Create your first item to get started.",
93
95
  railEmptyTitle: "No records yet",
94
- 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"
95
110
  };
96
111
 
97
112
  // src/components/RecordsAdmin/types/presentation.ts
@@ -455,12 +470,24 @@ var resolveRecord = async (args) => {
455
470
  const editingScope = parsedRefToScope(args.target);
456
471
  const result = await matchRecords(args.ctx, target, { strategy: "all" }).catch(() => null);
457
472
  const records = result?.records ?? [];
473
+ console.info("[RecordsAdmin/resolveRecord]", {
474
+ editingScope,
475
+ target,
476
+ matchCount: records.length,
477
+ winnerScope: records[0]?.record?.scope,
478
+ winnerRef: records[0]?.record?.ref
479
+ });
458
480
  if (records.length === 0) {
459
481
  return { data: null, source: "empty" };
460
482
  }
461
483
  const winnerEntry = records[0];
462
484
  const winner = winnerEntry.record;
463
485
  const winnerIsSelf = scopesEqual(winner.scope, editingScope);
486
+ console.info("[RecordsAdmin/resolveRecord] classification", {
487
+ winnerIsSelf,
488
+ winnerScope: winner.scope,
489
+ editingScope
490
+ });
464
491
  if (winnerIsSelf) {
465
492
  const parent = records[1]?.record;
466
493
  return {
@@ -1101,6 +1128,260 @@ var useDirtyNavigation = ({
1101
1128
  );
1102
1129
  return { runWithGuard };
1103
1130
  };
1131
+ var ConfirmDialog = ({
1132
+ open,
1133
+ title,
1134
+ body,
1135
+ saveLabel,
1136
+ discardLabel,
1137
+ cancelLabel,
1138
+ onChoice
1139
+ }) => {
1140
+ const saveBtnRef = useRef(null);
1141
+ useEffect(() => {
1142
+ if (!open) return;
1143
+ const prev = document.body.style.overflow;
1144
+ document.body.style.overflow = "hidden";
1145
+ const t = window.setTimeout(() => saveBtnRef.current?.focus(), 0);
1146
+ const onKey = (e) => {
1147
+ if (e.key === "Escape") {
1148
+ e.stopPropagation();
1149
+ onChoice("cancel");
1150
+ }
1151
+ };
1152
+ window.addEventListener("keydown", onKey, true);
1153
+ return () => {
1154
+ window.clearTimeout(t);
1155
+ window.removeEventListener("keydown", onKey, true);
1156
+ document.body.style.overflow = prev;
1157
+ };
1158
+ }, [open, onChoice]);
1159
+ if (!open || typeof document === "undefined") return null;
1160
+ return createPortal(
1161
+ /* @__PURE__ */ jsxs(
1162
+ "div",
1163
+ {
1164
+ className: "ra-shell ra-confirm-root",
1165
+ role: "presentation",
1166
+ onMouseDown: (e) => e.stopPropagation(),
1167
+ onClick: (e) => e.stopPropagation(),
1168
+ children: [
1169
+ /* @__PURE__ */ jsx("div", { className: "ra-confirm-backdrop", onClick: () => onChoice("cancel") }),
1170
+ /* @__PURE__ */ jsxs(
1171
+ "div",
1172
+ {
1173
+ role: "alertdialog",
1174
+ "aria-modal": "true",
1175
+ "aria-labelledby": "ra-confirm-title",
1176
+ "aria-describedby": "ra-confirm-body",
1177
+ className: "ra-confirm-card",
1178
+ children: [
1179
+ /* @__PURE__ */ jsxs("div", { className: "ra-confirm-header", children: [
1180
+ /* @__PURE__ */ jsx("span", { className: "ra-confirm-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4" }) }),
1181
+ /* @__PURE__ */ jsx("h2", { id: "ra-confirm-title", className: "ra-confirm-title", children: title })
1182
+ ] }),
1183
+ /* @__PURE__ */ jsx("p", { id: "ra-confirm-body", className: "ra-confirm-body", children: body }),
1184
+ /* @__PURE__ */ jsxs("div", { className: "ra-confirm-actions", children: [
1185
+ /* @__PURE__ */ jsx(
1186
+ "button",
1187
+ {
1188
+ type: "button",
1189
+ className: "ra-confirm-btn ra-confirm-btn-ghost",
1190
+ onClick: () => onChoice("cancel"),
1191
+ children: cancelLabel
1192
+ }
1193
+ ),
1194
+ /* @__PURE__ */ jsx(
1195
+ "button",
1196
+ {
1197
+ type: "button",
1198
+ className: "ra-confirm-btn ra-confirm-btn-danger",
1199
+ onClick: () => onChoice("discard"),
1200
+ children: discardLabel
1201
+ }
1202
+ ),
1203
+ saveLabel ? /* @__PURE__ */ jsx(
1204
+ "button",
1205
+ {
1206
+ type: "button",
1207
+ ref: saveBtnRef,
1208
+ className: "ra-confirm-btn ra-confirm-btn-primary",
1209
+ onClick: () => onChoice("save"),
1210
+ children: saveLabel
1211
+ }
1212
+ ) : null
1213
+ ] })
1214
+ ]
1215
+ }
1216
+ )
1217
+ ]
1218
+ }
1219
+ ),
1220
+ document.body
1221
+ );
1222
+ };
1223
+ var DEFAULTS = {
1224
+ title: "Unsaved changes",
1225
+ body: "You have unsaved changes. Save them, discard them, or stay on this record?",
1226
+ saveLabel: "Save & continue",
1227
+ discardLabel: "Discard",
1228
+ cancelLabel: "Stay here"
1229
+ };
1230
+ var useConfirmDialog = () => {
1231
+ const [open, setOpen] = useState(false);
1232
+ const [state, setState] = useState(DEFAULTS);
1233
+ const resolverRef = useRef(null);
1234
+ const confirm = useCallback((i18n) => {
1235
+ setState({
1236
+ title: i18n?.title ?? DEFAULTS.title,
1237
+ body: i18n?.body ?? DEFAULTS.body,
1238
+ saveLabel: i18n?.save ?? DEFAULTS.saveLabel,
1239
+ discardLabel: i18n?.discard ?? DEFAULTS.discardLabel,
1240
+ cancelLabel: i18n?.cancel ?? DEFAULTS.cancelLabel
1241
+ });
1242
+ setOpen(true);
1243
+ return new Promise((resolve) => {
1244
+ resolverRef.current = resolve;
1245
+ });
1246
+ }, []);
1247
+ const handleChoice = useCallback((choice) => {
1248
+ setOpen(false);
1249
+ const r = resolverRef.current;
1250
+ resolverRef.current = null;
1251
+ r?.(choice);
1252
+ }, []);
1253
+ return {
1254
+ confirm,
1255
+ dialog: /* @__PURE__ */ jsx(
1256
+ ConfirmDialog,
1257
+ {
1258
+ open,
1259
+ title: state.title,
1260
+ body: state.body,
1261
+ saveLabel: state.saveLabel,
1262
+ discardLabel: state.discardLabel,
1263
+ cancelLabel: state.cancelLabel,
1264
+ onChoice: handleChoice
1265
+ }
1266
+ )
1267
+ };
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
+ }
1104
1385
  var LABELS = {
1105
1386
  product: "Products",
1106
1387
  facet: "Shared",
@@ -1139,14 +1420,17 @@ var ScopeTabs = ({
1139
1420
  );
1140
1421
  }) });
1141
1422
  };
1142
- var StatusFilterPills = ({ value, onChange, counts, i18n }) => {
1423
+ var StatusFilterPills = ({ value, onChange, counts, i18n, hideZero }) => {
1143
1424
  const opts = [
1144
1425
  { key: "all", label: i18n.filterAll, count: counts.all },
1145
1426
  { key: "configured", label: i18n.filterConfigured, count: counts.configured },
1146
1427
  { key: "partial", label: i18n.filterPartial, count: counts.partial },
1147
1428
  { key: "empty", label: i18n.filterEmpty, count: counts.empty }
1148
1429
  ];
1149
- return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: opts.map((o) => {
1430
+ const visible = opts.filter(
1431
+ (o) => o.key === value || !hideZero?.includes(o.key) || o.count > 0
1432
+ );
1433
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: visible.map((o) => {
1150
1434
  const active = value === o.key;
1151
1435
  return /* @__PURE__ */ jsxs(
1152
1436
  "button",
@@ -1177,8 +1461,90 @@ var StatusDot = ({ source, status, className }) => {
1177
1461
  else if (source === "inherited" || status === "partial") cls = "ra-status-shared";
1178
1462
  return /* @__PURE__ */ jsx("span", { className: cn("ra-status-dot", cls, className), "aria-hidden": "true" });
1179
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
+ };
1180
1546
  var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1181
- const { selected, onSelect, isDirty } = ctx;
1547
+ const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
1182
1548
  const ScopeIcon = record.scope.kind && record.scope.kind !== "collection" ? DEFAULT_ICONS.scope[record.scope.kind] : DEFAULT_ICONS.scope.product;
1183
1549
  return /* @__PURE__ */ jsxs(
1184
1550
  "button",
@@ -1203,6 +1569,23 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1203
1569
  "aria-label": "Unsaved changes",
1204
1570
  className: "ra-status-dot ra-status-shared shrink-0"
1205
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
+ }
1206
1589
  )
1207
1590
  ]
1208
1591
  }
@@ -1210,7 +1593,7 @@ var DefaultRecordRow = ({ record, ctx, compact = false }) => {
1210
1593
  };
1211
1594
  var initials = (s) => s.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
1212
1595
  var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1213
- const { selected, onSelect, isDirty } = ctx;
1596
+ const { selected, onSelect, isDirty, onCopy, onPaste, canPaste, pasteWillReplace, clipboardSourceLabel } = ctx;
1214
1597
  const aspect = variant === "gallery" ? "aspect-video" : "aspect-square";
1215
1598
  return /* @__PURE__ */ jsxs(
1216
1599
  "button",
@@ -1262,7 +1645,26 @@ var DefaultRecordCard = ({ record, ctx, variant = "grid" }) => {
1262
1645
  }
1263
1646
  ),
1264
1647
  /* @__PURE__ */ jsxs("div", { className: "p-2.5 min-w-0", children: [
1265
- /* @__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
+ ] }),
1266
1668
  variant === "gallery" && record.subtitle && /* @__PURE__ */ jsx("div", { className: "ra-row-sub", children: record.subtitle }),
1267
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}`)) })
1268
1670
  ] })
@@ -1278,13 +1680,18 @@ var RecordList = ({
1278
1680
  presentation = "list",
1279
1681
  renderListRow,
1280
1682
  renderCard,
1281
- groupBy
1683
+ groupBy,
1684
+ rowClipboard
1282
1685
  }) => {
1283
- const buildCtx = (item) => ({
1284
- selected: item.ref === selectedRef,
1285
- onSelect: () => onSelect(item),
1286
- isDirty: !!dirtyRef && item.ref === dirtyRef
1287
- });
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
+ };
1288
1695
  const groups = useMemo(() => {
1289
1696
  if (!groupBy) return null;
1290
1697
  const buckets = /* @__PURE__ */ new Map();
@@ -1566,8 +1973,10 @@ var DeleteButton = ({
1566
1973
  label = "Delete record",
1567
1974
  confirmLabel = "Confirm delete",
1568
1975
  revertMs = 3e3,
1569
- disabled
1976
+ disabled,
1977
+ icon
1570
1978
  }) => {
1979
+ const Icon = icon ?? Trash2;
1571
1980
  const [armed, setArmed] = useState(false);
1572
1981
  const [busy, setBusy] = useState(false);
1573
1982
  const timerRef = useRef(null);
@@ -1628,7 +2037,7 @@ var DeleteButton = ({
1628
2037
  color: "hsl(var(--ra-danger, var(--ra-text)))"
1629
2038
  },
1630
2039
  children: [
1631
- /* @__PURE__ */ jsx(Trash2, { className: "w-3.5 h-3.5" }),
2040
+ /* @__PURE__ */ jsx(Icon, { className: "w-3.5 h-3.5" }),
1632
2041
  label
1633
2042
  ]
1634
2043
  }
@@ -1641,25 +2050,58 @@ function RecordEditor({
1641
2050
  preview,
1642
2051
  bulkActions,
1643
2052
  footerExtra,
1644
- onBeforeDelete
2053
+ onBeforeDelete,
2054
+ headerLabel,
2055
+ headerSubtitle,
2056
+ headerMeta,
2057
+ clipboard,
2058
+ actionLabels,
2059
+ actionIcons
1645
2060
  }) {
1646
- const sourceLabel = ctx.source === "self" ? "Own" : ctx.source === "inherited" ? "Inherited" : "New";
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;
2067
+ const sourceLabel = ctx.source === "self" ? "Customised" : ctx.source === "inherited" ? "Inherited" : null;
1647
2068
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
1648
2069
  /* @__PURE__ */ jsxs(
1649
2070
  "header",
1650
2071
  {
1651
- className: "sticky top-0 z-10 px-5 py-3 border-b flex items-center justify-between gap-3",
2072
+ className: "sticky top-0 z-40 px-5 py-3 border-b flex items-start justify-between gap-3",
1652
2073
  style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
1653
2074
  children: [
1654
2075
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1655
- /* @__PURE__ */ jsx(ScopeBreadcrumb, { scope: ctx.scope }),
1656
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1", children: [
1657
- /* @__PURE__ */ jsx(StatusDot, { source: ctx.source }),
1658
- /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: sourceLabel }),
2076
+ headerLabel ? /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2077
+ /* @__PURE__ */ jsx(
2078
+ "div",
2079
+ {
2080
+ className: "text-sm font-medium truncate",
2081
+ style: { color: "hsl(var(--ra-text))" },
2082
+ title: headerLabel,
2083
+ children: headerLabel
2084
+ }
2085
+ ),
2086
+ headerSubtitle && /* @__PURE__ */ jsx(
2087
+ "div",
2088
+ {
2089
+ className: "text-xs truncate",
2090
+ style: { color: "hsl(var(--ra-muted-text))" },
2091
+ title: headerSubtitle,
2092
+ children: headerSubtitle
2093
+ }
2094
+ )
2095
+ ] }) : /* @__PURE__ */ jsx(ScopeBreadcrumb, { scope: ctx.scope }),
2096
+ (sourceLabel || ctx.isDirty) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1", children: [
2097
+ sourceLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
2098
+ /* @__PURE__ */ jsx(StatusDot, { source: ctx.source }),
2099
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: sourceLabel })
2100
+ ] }),
1659
2101
  ctx.isDirty && /* @__PURE__ */ jsxs(
1660
2102
  "span",
1661
2103
  {
1662
- className: "ml-2 flex items-center gap-1 text-[10px] uppercase tracking-wide",
2104
+ className: `flex items-center gap-1 text-[10px] uppercase tracking-wide ${sourceLabel ? "ml-2" : ""}`,
1663
2105
  style: { color: "hsl(var(--ra-accent))" },
1664
2106
  children: [
1665
2107
  /* @__PURE__ */ jsx("span", { className: "ra-status-dot ra-status-shared", "aria-hidden": "true" }),
@@ -1669,7 +2111,22 @@ function RecordEditor({
1669
2111
  )
1670
2112
  ] })
1671
2113
  ] }),
1672
- bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
2114
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 shrink-0", children: [
2115
+ headerMeta && /* @__PURE__ */ jsx(
2116
+ "code",
2117
+ {
2118
+ className: "text-[10px] font-mono px-1.5 py-0.5 rounded truncate max-w-[14rem] mt-0.5",
2119
+ style: {
2120
+ color: "hsl(var(--ra-muted-text) / 0.85)",
2121
+ background: "hsl(var(--ra-muted) / 0.5)",
2122
+ border: "1px solid hsl(var(--ra-border) / 0.6)"
2123
+ },
2124
+ title: headerMeta,
2125
+ children: headerMeta
2126
+ }
2127
+ ),
2128
+ bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
2129
+ ] })
1673
2130
  ]
1674
2131
  }
1675
2132
  ),
@@ -1684,15 +2141,18 @@ function RecordEditor({
1684
2141
  style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
1685
2142
  children: [
1686
2143
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1687
- /* @__PURE__ */ jsx(
2144
+ /* @__PURE__ */ jsxs(
1688
2145
  "button",
1689
2146
  {
1690
2147
  type: "button",
1691
2148
  onClick: ctx.reset,
1692
2149
  disabled: !ctx.isDirty || !!ctx.isSaving,
1693
- 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",
1694
2151
  style: { borderColor: "hsl(var(--ra-border))", color: "hsl(var(--ra-text))" },
1695
- children: i18n.discard
2152
+ children: [
2153
+ DiscardIcon && /* @__PURE__ */ jsx(DiscardIcon, { className: "h-4 w-4" }),
2154
+ discardLabel
2155
+ ]
1696
2156
  }
1697
2157
  ),
1698
2158
  ctx.canRemove && /* @__PURE__ */ jsx(
@@ -1700,10 +2160,45 @@ function RecordEditor({
1700
2160
  {
1701
2161
  onConfirm: () => ctx.remove(),
1702
2162
  onBeforeDelete,
1703
- label: i18n.delete,
1704
- confirmLabel: i18n.confirmDelete
2163
+ label: deleteLabel,
2164
+ confirmLabel: i18n.confirmDelete,
2165
+ icon: DeleteIcon
1705
2166
  }
1706
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
+ ] }),
1707
2202
  footerExtra,
1708
2203
  ctx.saveError != null && !ctx.isSaving && /* @__PURE__ */ jsx(
1709
2204
  "span",
@@ -1715,7 +2210,7 @@ function RecordEditor({
1715
2210
  }
1716
2211
  )
1717
2212
  ] }),
1718
- /* @__PURE__ */ jsx(
2213
+ /* @__PURE__ */ jsxs(
1719
2214
  "button",
1720
2215
  {
1721
2216
  type: "button",
@@ -1724,9 +2219,12 @@ function RecordEditor({
1724
2219
  });
1725
2220
  },
1726
2221
  disabled: !ctx.isDirty || !!ctx.isSaving,
1727
- 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",
1728
2223
  style: { background: "hsl(var(--ra-accent))", color: "hsl(var(--ra-surface))" },
1729
- 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
+ ]
1730
2228
  }
1731
2229
  )
1732
2230
  ]
@@ -1751,16 +2249,18 @@ var ProductDrillDown = ({
1751
2249
  batches,
1752
2250
  variantsLoading,
1753
2251
  batchesLoading,
1754
- children
2252
+ children,
2253
+ hideSingleTab
1755
2254
  }) => {
1756
2255
  const tabs = ["product"];
1757
2256
  if (showVariants) tabs.push("variant");
1758
2257
  if (showBatches) tabs.push("batch");
2258
+ const showTabStrip = tabs.length > 1 || !hideSingleTab;
1759
2259
  const childList = active === "variant" ? variants : active === "batch" ? batches : [];
1760
2260
  const childLoading = active === "variant" ? variantsLoading : active === "batch" ? batchesLoading : false;
1761
2261
  const childEmptyLabel = active === "variant" ? "No variants" : "No batches";
1762
2262
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
1763
- /* @__PURE__ */ jsx(
2263
+ showTabStrip && /* @__PURE__ */ jsx(
1764
2264
  "div",
1765
2265
  {
1766
2266
  className: "flex items-center gap-1 px-4 pt-3 border-b",
@@ -1884,13 +2384,29 @@ var InlinePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE
1884
2384
  ] }),
1885
2385
  children
1886
2386
  ] });
1887
- var SidePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
2387
+ var SidePreview = ({
2388
+ children,
2389
+ scopePicker,
2390
+ label = "Preview",
2391
+ onClose
2392
+ }) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
1888
2393
  /* @__PURE__ */ jsxs("header", { className: "ra-preview-rail-header", children: [
1889
2394
  /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail-title", children: [
1890
2395
  /* @__PURE__ */ jsx(Eye, { className: "w-3 h-3" }),
1891
2396
  label
1892
2397
  ] }),
1893
- scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker })
2398
+ scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker }),
2399
+ onClose && /* @__PURE__ */ jsx(
2400
+ "button",
2401
+ {
2402
+ type: "button",
2403
+ onClick: onClose,
2404
+ "aria-label": "Close preview",
2405
+ className: scopePicker ? "p-1 rounded hover:bg-[hsl(var(--ra-muted))]" : "ml-auto p-1 rounded hover:bg-[hsl(var(--ra-muted))]",
2406
+ style: { color: "hsl(var(--ra-muted-text))" },
2407
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
2408
+ }
2409
+ )
1894
2410
  ] }),
1895
2411
  /* @__PURE__ */ jsx("div", { className: "ra-preview-rail-body", children })
1896
2412
  ] });
@@ -2004,7 +2520,13 @@ var PreviewScopePicker = ({
2004
2520
  showBatches,
2005
2521
  i18n
2006
2522
  }) => {
2007
- const products = useProductBrowse({ SL, collectionId, pageSize: 100 });
2523
+ const productPinned = !!editingScope.productId;
2524
+ const products = useProductBrowse({
2525
+ SL,
2526
+ collectionId,
2527
+ pageSize: 100,
2528
+ enabled: !productPinned
2529
+ });
2008
2530
  const variants = useProductChildren({
2009
2531
  SL,
2010
2532
  collectionId,
@@ -2021,6 +2543,27 @@ var PreviewScopePicker = ({
2021
2543
  () => value.raw === editingScope.raw,
2022
2544
  [value.raw, editingScope.raw]
2023
2545
  );
2546
+ useEffect(() => {
2547
+ if (productPinned) return;
2548
+ if (value.productId) return;
2549
+ const first = products.items[0];
2550
+ if (!first) return;
2551
+ onChange({
2552
+ ...value,
2553
+ productId: first.id,
2554
+ variantId: void 0,
2555
+ batchId: void 0,
2556
+ raw: `product:${first.id}`
2557
+ });
2558
+ }, [productPinned, value.productId, products.items]);
2559
+ const [productPickerOpen, setProductPickerOpen] = useState(false);
2560
+ const showProductPicker = !productPinned && products.items.length > 1;
2561
+ const showVariantPicker = showVariants && !!value.productId && variants.items.length > 0;
2562
+ const showBatchPicker = showBatches && !!value.productId && batches.items.length > 0;
2563
+ if (!showProductPicker && !showVariantPicker && !showBatchPicker) {
2564
+ return null;
2565
+ }
2566
+ const currentProductName = products.items.find((p) => p.id === value.productId)?.name ?? value.productId ?? "";
2024
2567
  const selectStyle = {
2025
2568
  borderColor: "hsl(var(--ra-border))",
2026
2569
  color: "hsl(var(--ra-text))"
@@ -2050,30 +2593,44 @@ var PreviewScopePicker = ({
2050
2593
  ]
2051
2594
  }
2052
2595
  ),
2053
- /* @__PURE__ */ jsxs(
2596
+ showProductPicker && !productPickerOpen && currentProductName && /* @__PURE__ */ jsxs(
2597
+ "button",
2598
+ {
2599
+ type: "button",
2600
+ onClick: () => setProductPickerOpen(true),
2601
+ className: "text-[10px] px-2 py-1 rounded-md border hover:bg-[hsl(var(--ra-muted))] inline-flex items-center gap-1 max-w-[12rem] truncate",
2602
+ style: selectStyle,
2603
+ title: `Preview as ${currentProductName} \u2014 click to change`,
2604
+ children: [
2605
+ "as ",
2606
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate", children: currentProductName }),
2607
+ /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 opacity-60" })
2608
+ ]
2609
+ }
2610
+ ),
2611
+ showProductPicker && productPickerOpen && /* @__PURE__ */ jsx(
2054
2612
  "select",
2055
2613
  {
2056
2614
  className: SELECT_CLS,
2057
2615
  style: selectStyle,
2616
+ autoFocus: true,
2617
+ onBlur: () => setProductPickerOpen(false),
2058
2618
  value: value.productId ?? "",
2059
2619
  onChange: (e) => {
2060
2620
  const productId = e.target.value || void 0;
2061
2621
  onChange({
2062
2622
  ...value,
2063
2623
  productId,
2064
- // Clear variant/batch when switching products.
2065
2624
  variantId: void 0,
2066
2625
  batchId: void 0,
2067
2626
  raw: productId ? `product:${productId}` : ""
2068
2627
  });
2628
+ setProductPickerOpen(false);
2069
2629
  },
2070
- children: [
2071
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Any product \u2014" }),
2072
- products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
2073
- ]
2630
+ children: products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
2074
2631
  }
2075
2632
  ),
2076
- showVariants && value.productId && variants.items.length > 0 && /* @__PURE__ */ jsxs(
2633
+ showVariantPicker && /* @__PURE__ */ jsxs(
2077
2634
  "select",
2078
2635
  {
2079
2636
  className: SELECT_CLS,
@@ -2094,7 +2651,7 @@ var PreviewScopePicker = ({
2094
2651
  ]
2095
2652
  }
2096
2653
  ),
2097
- showBatches && value.productId && batches.items.length > 0 && /* @__PURE__ */ jsxs(
2654
+ showBatchPicker && /* @__PURE__ */ jsxs(
2098
2655
  "select",
2099
2656
  {
2100
2657
  className: SELECT_CLS,
@@ -2241,6 +2798,84 @@ function ShellHeader({
2241
2798
  ] })
2242
2799
  ] });
2243
2800
  }
2801
+ var UnsavedBanner = ({
2802
+ label,
2803
+ context,
2804
+ isSaving,
2805
+ saveError,
2806
+ onSave,
2807
+ onDiscard,
2808
+ saveLabel,
2809
+ discardLabel,
2810
+ bodyTemplate,
2811
+ savingLabel,
2812
+ errorLabel,
2813
+ SaveIcon,
2814
+ DiscardIcon
2815
+ }) => {
2816
+ const SI = SaveIcon ?? Save;
2817
+ const DI = DiscardIcon ?? Undo2;
2818
+ const name = label ?? context ?? "this record";
2819
+ const message = bodyTemplate.replace("{name}", name);
2820
+ return /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-banner", role: "status", "aria-live": "polite", children: [
2821
+ /* @__PURE__ */ jsx("span", { className: "ra-unsaved-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5" }) }),
2822
+ /* @__PURE__ */ jsxs("span", { className: "ra-unsaved-text", children: [
2823
+ message,
2824
+ context && label && /* @__PURE__ */ jsxs("span", { className: "ra-unsaved-context", children: [
2825
+ " \xB7 ",
2826
+ context
2827
+ ] })
2828
+ ] }),
2829
+ saveError != null && !isSaving && /* @__PURE__ */ jsx("span", { className: "ra-unsaved-error", role: "alert", children: errorLabel ?? "Save failed" }),
2830
+ /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-actions", children: [
2831
+ /* @__PURE__ */ jsxs(
2832
+ "button",
2833
+ {
2834
+ type: "button",
2835
+ className: "ra-unsaved-btn ra-unsaved-btn-ghost",
2836
+ onClick: onDiscard,
2837
+ disabled: isSaving,
2838
+ children: [
2839
+ /* @__PURE__ */ jsx(DI, { className: "w-3 h-3" }),
2840
+ discardLabel
2841
+ ]
2842
+ }
2843
+ ),
2844
+ /* @__PURE__ */ jsxs(
2845
+ "button",
2846
+ {
2847
+ type: "button",
2848
+ className: "ra-unsaved-btn ra-unsaved-btn-primary",
2849
+ onClick: onSave,
2850
+ disabled: isSaving,
2851
+ children: [
2852
+ /* @__PURE__ */ jsx(SI, { className: "w-3 h-3" }),
2853
+ isSaving ? savingLabel ?? "Saving\u2026" : saveLabel
2854
+ ]
2855
+ }
2856
+ )
2857
+ ] })
2858
+ ] });
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
+ };
2244
2879
 
2245
2880
  // src/components/RecordsAdmin/data/csv.ts
2246
2881
  var escapeCell = (s) => {
@@ -2350,8 +2985,8 @@ var downloadBlob = (blob, filename) => {
2350
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');
2351
2986
 
2352
2987
  // src/components/RecordsAdmin/shell/shell.css
2353
- 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: 30;\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");
2354
- var TOP_LEVEL_SCOPES = ["product", "facet"];
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");
2989
+ var TOP_LEVEL_SCOPES = ["facet", "product"];
2355
2990
  var defaultItemId = () => {
2356
2991
  const time = Date.now().toString(36);
2357
2992
  const rand = Math.random().toString(36).slice(2, 8);
@@ -2390,7 +3025,11 @@ function RecordsAdminShell(props) {
2390
3025
  className,
2391
3026
  previewMode = "inline",
2392
3027
  previewScopePicker = false,
2393
- dirtyStrategy = "prompt",
3028
+ // Default to `keep` — admins can tab around freely while edits are
3029
+ // preserved per editor mount, and the inline UnsavedBanner surfaces the
3030
+ // dirty state at the top of the shell with Save/Discard buttons. Hosts
3031
+ // that want the old blocking behaviour can opt back into `'prompt'`.
3032
+ dirtyStrategy = "keep",
2394
3033
  onBeforeDelete,
2395
3034
  disableBeforeUnload = false,
2396
3035
  presentations = ["list"],
@@ -2414,10 +3053,23 @@ function RecordsAdminShell(props) {
2414
3053
  icons: iconsOverride,
2415
3054
  groupBy,
2416
3055
  renderEmptyState,
2417
- density = "comfortable"
3056
+ density = "comfortable",
3057
+ enableClipboard = true,
3058
+ onCopy: onCopyOverride,
3059
+ onPaste: onPasteOverride,
3060
+ actionLabels,
3061
+ actionIcons,
3062
+ editorTabs = "off"
2418
3063
  } = props;
2419
3064
  const i18n = { ...DEFAULT_I18N, ...i18nOverride ?? {} };
2420
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
+ }
2421
3073
  const [presentation, setPresentation] = usePresentationPref({
2422
3074
  appId,
2423
3075
  recordType,
@@ -2434,7 +3086,7 @@ function RecordsAdminShell(props) {
2434
3086
  );
2435
3087
  const probe = useScopeProbe({ SL, collectionId });
2436
3088
  const topLevelScopes = useMemo(
2437
- () => requestedScopes.filter((s) => TOP_LEVEL_SCOPES.includes(s)),
3089
+ () => TOP_LEVEL_SCOPES.filter((s) => requestedScopes.includes(s)),
2438
3090
  [requestedScopes]
2439
3091
  );
2440
3092
  const drillVariantsAllowed = useMemo(
@@ -2448,6 +3100,7 @@ function RecordsAdminShell(props) {
2448
3100
  const initialScope = useMemo(() => {
2449
3101
  if (contextScope?.productId && topLevelScopes.includes("product")) return "product";
2450
3102
  if (defaultScope && topLevelScopes.includes(defaultScope)) return defaultScope;
3103
+ if (topLevelScopes.includes("facet")) return "facet";
2451
3104
  return topLevelScopes[0] ?? "product";
2452
3105
  }, [contextScope?.productId, defaultScope, topLevelScopes]);
2453
3106
  const [activeScope, setActiveScope] = useState(initialScope);
@@ -2597,13 +3250,25 @@ function RecordsAdminShell(props) {
2597
3250
  useUnsavedGuard({
2598
3251
  isDirty: editorCtx.isDirty,
2599
3252
  label: recordType,
2600
- disableBeforeUnload
3253
+ disableBeforeUnload,
3254
+ // In `keep` mode we don't want the host iframe to pop its own native
3255
+ // confirm — the banner is the canonical surface. Hosts on `prompt` /
3256
+ // `autosave` opt back into platform notifications.
3257
+ disableParentMessage: dirtyStrategy === "keep"
3258
+ });
3259
+ const dirtyConfirm = useConfirmDialog();
3260
+ const pasteConfirm = usePasteConfirm();
3261
+ const clipboard = useRecordClipboard({
3262
+ appId,
3263
+ recordType: recordType ?? "__default"
2601
3264
  });
3265
+ const [clipboardNotice, setClipboardNotice] = useState(null);
2602
3266
  const { runWithGuard } = useDirtyNavigation({
2603
3267
  strategy: dirtyStrategy,
2604
3268
  isDirty: editorCtx.isDirty,
2605
3269
  save: editorCtx.save,
2606
3270
  reset: editorCtx.reset,
3271
+ confirm: dirtyConfirm.confirm,
2607
3272
  i18n: {
2608
3273
  title: i18n.unsavedPromptTitle,
2609
3274
  body: i18n.unsavedPromptBody,
@@ -2640,11 +3305,195 @@ function RecordsAdminShell(props) {
2640
3305
  const csvBulk = csvSchema ? { onImportCsv: handleImport, onExportCsv: handleExport } : {};
2641
3306
  const [previewScope, setPreviewScope] = useState(null);
2642
3307
  const [drawerOpen, setDrawerOpen] = useState(false);
3308
+ const [sidePreviewOpen, setSidePreviewOpen] = useState(true);
2643
3309
  useEffect(() => {
2644
3310
  if (!editingScope) return;
2645
3311
  setPreviewScope((cur) => cur === null ? editingScope : cur);
2646
3312
  }, [editingScope]);
2647
3313
  const effectivePreviewScope = previewScope ?? editingScope;
3314
+ const editorHeaderLabel = useMemo(() => {
3315
+ if (!editingScope) return void 0;
3316
+ if (activeScope === "facet" && selectedFacetRef) {
3317
+ const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
3318
+ return hit?.label;
3319
+ }
3320
+ if (activeScope === "product" && selectedProductId) {
3321
+ const hit = productBrowse.items.find((it) => it.id === selectedProductId);
3322
+ return hit?.name ?? selectedProductId;
3323
+ }
3324
+ return void 0;
3325
+ }, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
3326
+ const editorHeaderSubtitle = useMemo(() => {
3327
+ if (!editingScope) return void 0;
3328
+ if (activeScope === "facet" && selectedFacetRef) {
3329
+ const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
3330
+ return hit?.subtitle;
3331
+ }
3332
+ return void 0;
3333
+ }, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
3334
+ const editorHeaderMeta = useMemo(() => {
3335
+ if (!editingScope) return void 0;
3336
+ if (activeScope === "product" && selectedProductId) {
3337
+ const hit = productBrowse.items.find((it) => it.id === selectedProductId);
3338
+ return hit?.sku ?? selectedProductId;
3339
+ }
3340
+ return void 0;
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);
2648
3497
  const renderEditorWithPreview = () => {
2649
3498
  if (!editingScope) return null;
2650
3499
  const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
@@ -2670,6 +3519,12 @@ function RecordsAdminShell(props) {
2670
3519
  preview: inlinePreviewBody,
2671
3520
  footerExtra: extraFooter,
2672
3521
  onBeforeDelete: onBeforeDelete && editingScope ? () => onBeforeDelete(editingScope) : void 0,
3522
+ headerLabel: editorHeaderLabel,
3523
+ headerSubtitle: editorHeaderSubtitle,
3524
+ headerMeta: editorHeaderMeta,
3525
+ clipboard: editorClipboard,
3526
+ actionLabels,
3527
+ actionIcons,
2673
3528
  children: renderEditor(editorCtx)
2674
3529
  }
2675
3530
  );
@@ -2681,9 +3536,28 @@ function RecordsAdminShell(props) {
2681
3536
  );
2682
3537
  }
2683
3538
  if (previewMode === "side") {
3539
+ if (!sidePreviewOpen) {
3540
+ return /* @__PURE__ */ jsx("div", { className: "relative h-full", children: baseEditor(
3541
+ /* @__PURE__ */ jsx(
3542
+ PreviewToggleButton,
3543
+ {
3544
+ onClick: () => setSidePreviewOpen(true),
3545
+ label: i18n.openPreview
3546
+ }
3547
+ )
3548
+ ) });
3549
+ }
2684
3550
  return /* @__PURE__ */ jsxs("div", { className: "grid h-full", style: { gridTemplateColumns: "minmax(0, 1fr) minmax(280px, 420px)" }, children: [
2685
3551
  /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: baseEditor() }),
2686
- /* @__PURE__ */ jsx(SidePreview, { label: i18n.preview, scopePicker, children: previewBody })
3552
+ /* @__PURE__ */ jsx(
3553
+ SidePreview,
3554
+ {
3555
+ label: i18n.preview,
3556
+ scopePicker,
3557
+ onClose: () => setSidePreviewOpen(false),
3558
+ children: previewBody
3559
+ }
3560
+ )
2687
3561
  ] });
2688
3562
  }
2689
3563
  if (previewMode === "tab") {
@@ -2715,6 +3589,18 @@ function RecordsAdminShell(props) {
2715
3589
  };
2716
3590
  const isProductTab = activeScope === "product";
2717
3591
  const productPinned = !!contextScope?.productId;
3592
+ const effectivePresentation = isProductTab ? presentation : "list";
3593
+ const showPresentationSwitcher = isProductTab && presentations.length > 1;
3594
+ const effectiveGroupBy = useMemo(() => {
3595
+ if (groupBy) return groupBy;
3596
+ if (isProductTab) return void 0;
3597
+ return (record) => {
3598
+ const key = record.scope.facetId;
3599
+ if (!key) return null;
3600
+ const label2 = record.subtitle ?? key;
3601
+ return { key, label: label2 };
3602
+ };
3603
+ }, [groupBy, isProductTab]);
2718
3604
  const productListItems = useMemo(() => {
2719
3605
  if (productPinned) {
2720
3606
  return [{
@@ -2745,12 +3631,23 @@ function RecordsAdminShell(props) {
2745
3631
  }
2746
3632
  });
2747
3633
  };
3634
+ onLeftSelectRef.current = onLeftSelect;
2748
3635
  return /* @__PURE__ */ jsxs(
2749
3636
  "div",
2750
3637
  {
2751
3638
  className: `ra-shell flex flex-col h-full ${className ?? ""}`,
2752
3639
  "data-density": density,
2753
3640
  children: [
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
+ ),
2754
3651
  /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 space-y-3", children: [
2755
3652
  intro && !dismissed && /* @__PURE__ */ jsx(
2756
3653
  IntroCard,
@@ -2795,6 +3692,27 @@ function RecordsAdminShell(props) {
2795
3692
  introHidden: dismissed && !!intro,
2796
3693
  onShowIntro: undismiss
2797
3694
  }
3695
+ ),
3696
+ editorCtx.isDirty && /* @__PURE__ */ jsx(
3697
+ UnsavedBanner,
3698
+ {
3699
+ label: editorHeaderLabel,
3700
+ context: editorHeaderSubtitle,
3701
+ isSaving: !!editorCtx.isSaving,
3702
+ saveError: editorCtx.saveError,
3703
+ onSave: () => {
3704
+ void editorCtx.save().catch(() => {
3705
+ });
3706
+ },
3707
+ onDiscard: editorCtx.reset,
3708
+ saveLabel: actionLabels?.save ?? i18n.save,
3709
+ discardLabel: actionLabels?.discard ?? i18n.discard,
3710
+ SaveIcon: actionIcons?.save,
3711
+ DiscardIcon: actionIcons?.discard,
3712
+ savingLabel: i18n.saving,
3713
+ errorLabel: i18n.saveError,
3714
+ bodyTemplate: i18n.unsavedBannerBody
3715
+ }
2798
3716
  )
2799
3717
  ] }),
2800
3718
  /* @__PURE__ */ jsxs(
@@ -2842,14 +3760,23 @@ function RecordsAdminShell(props) {
2842
3760
  /* @__PURE__ */ jsx(
2843
3761
  PresentationSwitcher,
2844
3762
  {
2845
- options: presentations,
3763
+ options: showPresentationSwitcher ? presentations : [],
2846
3764
  value: presentation,
2847
3765
  onChange: onPresentationChange,
2848
3766
  i18n
2849
3767
  }
2850
3768
  )
2851
3769
  ] }),
2852
- !isProductTab && /* @__PURE__ */ jsx(StatusFilterPills, { value: filter, onChange: setFilter, counts: facetBrowse.counts, i18n }),
3770
+ !isProductTab && /* @__PURE__ */ jsx(
3771
+ StatusFilterPills,
3772
+ {
3773
+ value: filter,
3774
+ onChange: setFilter,
3775
+ counts: facetBrowse.counts,
3776
+ hideZero: ["partial"],
3777
+ i18n
3778
+ }
3779
+ ),
2853
3780
  cardinality === "collection" && !isProductTab && editingScope && /* @__PURE__ */ jsxs(
2854
3781
  "button",
2855
3782
  {
@@ -2891,10 +3818,11 @@ function RecordsAdminShell(props) {
2891
3818
  selectedRef: leftSelectedRef,
2892
3819
  onSelect: onLeftSelect,
2893
3820
  dirtyRef: editorCtx.isDirty ? editingScope?.raw : void 0,
2894
- presentation,
3821
+ presentation: effectivePresentation,
2895
3822
  renderListRow,
2896
3823
  renderCard,
2897
- groupBy
3824
+ groupBy: effectiveGroupBy,
3825
+ rowClipboard
2898
3826
  }
2899
3827
  ),
2900
3828
  isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
@@ -2922,6 +3850,7 @@ function RecordsAdminShell(props) {
2922
3850
  productLabel: productBrowse.items.find((p) => p.id === selectedProductId)?.name ?? selectedProductId,
2923
3851
  showVariants: drillVariantsAllowed,
2924
3852
  showBatches: drillBatchesAllowed,
3853
+ hideSingleTab: editorTabs === "off" || editorTabs === "multi",
2925
3854
  active: drillTab,
2926
3855
  onChange: (t) => {
2927
3856
  void runWithGuard(() => {
@@ -3354,6 +4283,6 @@ function useMergedRecord(args) {
3354
4283
  };
3355
4284
  }
3356
4285
 
3357
- 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 };
3358
4287
  //# sourceMappingURL=index.js.map
3359
4288
  //# sourceMappingURL=index.js.map