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