@proveanything/smartlinks-utils-ui 0.3.5 → 0.3.8

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-NIAIQRFC.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, 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",
@@ -436,6 +438,7 @@ var useRecordList = (args) => {
436
438
  }, [queryClient, ctx.collectionId, ctx.appId, ctx.recordType]);
437
439
  const total = query.data?.pages[query.data.pages.length - 1]?.total ?? items.length;
438
440
  return {
441
+ allItems: items,
439
442
  items: filtered,
440
443
  total,
441
444
  counts,
@@ -454,12 +457,24 @@ var resolveRecord = async (args) => {
454
457
  const editingScope = parsedRefToScope(args.target);
455
458
  const result = await matchRecords(args.ctx, target, { strategy: "all" }).catch(() => null);
456
459
  const records = result?.records ?? [];
460
+ console.info("[RecordsAdmin/resolveRecord]", {
461
+ editingScope,
462
+ target,
463
+ matchCount: records.length,
464
+ winnerScope: records[0]?.record?.scope,
465
+ winnerRef: records[0]?.record?.ref
466
+ });
457
467
  if (records.length === 0) {
458
468
  return { data: null, source: "empty" };
459
469
  }
460
470
  const winnerEntry = records[0];
461
471
  const winner = winnerEntry.record;
462
472
  const winnerIsSelf = scopesEqual(winner.scope, editingScope);
473
+ console.info("[RecordsAdmin/resolveRecord] classification", {
474
+ winnerIsSelf,
475
+ winnerScope: winner.scope,
476
+ editingScope
477
+ });
463
478
  if (winnerIsSelf) {
464
479
  const parent = records[1]?.record;
465
480
  return {
@@ -750,7 +765,133 @@ var useScopeProbe = ({ SL, collectionId, admin = true, enabled = true }) => {
750
765
  error: query.error ?? null
751
766
  };
752
767
  };
753
- var QK = ["records-admin", "product-browse"];
768
+ var LOG = "[RecordsAdmin/useFacetBrowse]";
769
+ var QK = ["records-admin", "facet-browse"];
770
+ var toScaffoldSummary = (facet, value) => {
771
+ const facetKey = facet.key ?? "";
772
+ const valueKey = value.key ?? "";
773
+ const ref = buildRef({ facetId: facetKey, facetValue: valueKey });
774
+ return {
775
+ id: null,
776
+ ref,
777
+ scope: parseRef(ref),
778
+ data: null,
779
+ status: "empty",
780
+ label: value.name ?? valueKey ?? "Untitled value",
781
+ subtitle: facet.name ?? facetKey ?? void 0
782
+ };
783
+ };
784
+ var useFacetBrowse = ({
785
+ SL,
786
+ collectionId,
787
+ existing,
788
+ search = "",
789
+ filter = "all",
790
+ enabled = true
791
+ }) => {
792
+ const queryClient = useQueryClient();
793
+ const hasAdminList = !!SL?.facets?.list;
794
+ const hasPublicList = !!SL?.facets?.publicList;
795
+ const hasAnyList = hasAdminList || hasPublicList;
796
+ const queryEnabled = enabled && !!collectionId && hasAnyList;
797
+ const query = useQuery({
798
+ queryKey: [...QK, collectionId],
799
+ enabled: queryEnabled,
800
+ staleTime: 3e4,
801
+ queryFn: async () => {
802
+ const t0 = performance.now();
803
+ try {
804
+ if (SL?.facets?.list) {
805
+ console.info(`${LOG} \u2192 SL.facets.list("${collectionId}", { includeValues: true })`);
806
+ const res = await SL.facets.list(collectionId, { includeValues: true });
807
+ const items = res?.items ?? [];
808
+ console.info(
809
+ `${LOG} \u2190 SL.facets.list ok in ${Math.round(performance.now() - t0)}ms \u2014 ${items.length} facet(s)`,
810
+ items
811
+ );
812
+ return items;
813
+ }
814
+ if (SL?.facets?.publicList) {
815
+ console.info(`${LOG} \u2192 SL.facets.publicList("${collectionId}", { includeValues: true })`);
816
+ const res = await SL.facets.publicList(collectionId, { includeValues: true });
817
+ const items = res?.items ?? [];
818
+ console.info(
819
+ `${LOG} \u2190 SL.facets.publicList ok in ${Math.round(performance.now() - t0)}ms \u2014 ${items.length} facet(s)`,
820
+ items
821
+ );
822
+ return items;
823
+ }
824
+ console.warn(`${LOG} queryFn ran but no facets API is available on SL`);
825
+ return [];
826
+ } catch (err) {
827
+ console.error(`${LOG} \u2716 facets fetch failed`, err);
828
+ throw err;
829
+ }
830
+ }
831
+ });
832
+ const lastLoggedRef = useRef("");
833
+ useEffect(() => {
834
+ const signature = `${enabled}|${collectionId}|${hasAdminList}|${hasPublicList}`;
835
+ if (signature === lastLoggedRef.current) return;
836
+ lastLoggedRef.current = signature;
837
+ if (!enabled) {
838
+ console.info(`${LOG} skipped \u2014 enabled=false (shell not on facet tab yet)`);
839
+ return;
840
+ }
841
+ if (!collectionId) {
842
+ console.warn(`${LOG} skipped \u2014 no collectionId provided`);
843
+ return;
844
+ }
845
+ if (!hasAnyList) {
846
+ console.warn(
847
+ `${LOG} skipped \u2014 SDK is missing both SL.facets.list and SL.facets.publicList. Update @proveanything/smartlinks (>=1.8) or check the SDK build.`,
848
+ { facetsNamespace: SL?.facets ? Object.keys(SL.facets) : null }
849
+ );
850
+ return;
851
+ }
852
+ console.info(
853
+ `${LOG} will fetch facets for collection "${collectionId}" via ${hasAdminList ? "SL.facets.list (admin)" : "SL.facets.publicList (fallback)"}`
854
+ );
855
+ }, [enabled, collectionId, hasAdminList, hasPublicList, hasAnyList, SL]);
856
+ const mergedItems = useMemo(() => {
857
+ const existingByRef = new Map(existing.map((item) => [item.ref, item]));
858
+ const scaffolded = (query.data ?? []).flatMap(
859
+ (facet) => (facet.values ?? []).filter((value) => !!facet.key && !!value.key).map((value) => {
860
+ const scaffold = toScaffoldSummary(facet, value);
861
+ return existingByRef.get(scaffold.ref) ?? scaffold;
862
+ })
863
+ );
864
+ const extras = existing.filter((item) => !scaffolded.some((scaffold) => scaffold.ref === item.ref));
865
+ return [...scaffolded, ...extras];
866
+ }, [existing, query.data]);
867
+ const filteredItems = useMemo(() => {
868
+ let next = mergedItems;
869
+ if (filter !== "all") next = next.filter((item) => item.status === filter);
870
+ if (search.trim()) {
871
+ const q = search.trim().toLowerCase();
872
+ next = next.filter((item) => `${item.label} ${item.subtitle ?? ""} ${item.ref}`.toLowerCase().includes(q));
873
+ }
874
+ return next;
875
+ }, [mergedItems, filter, search]);
876
+ const counts = useMemo(() => ({
877
+ all: mergedItems.length,
878
+ configured: mergedItems.filter((item) => item.status === "configured").length,
879
+ partial: mergedItems.filter((item) => item.status === "partial").length,
880
+ empty: mergedItems.filter((item) => item.status === "empty").length
881
+ }), [mergedItems]);
882
+ const refetch = () => {
883
+ queryClient.invalidateQueries({ queryKey: [...QK, collectionId] });
884
+ };
885
+ return {
886
+ items: filteredItems,
887
+ total: mergedItems.length,
888
+ counts,
889
+ isLoading: query.isLoading,
890
+ error: query.error ?? null,
891
+ refetch
892
+ };
893
+ };
894
+ var QK2 = ["records-admin", "product-browse"];
754
895
  var toBrowseItem = (p) => ({
755
896
  id: p.id ?? p.productId ?? "",
756
897
  name: p.name ?? p.id ?? "Untitled",
@@ -761,12 +902,12 @@ var useProductBrowse = (args) => {
761
902
  const { SL, collectionId, search = "", pageSize = 50, enabled = true, admin = true } = args;
762
903
  const queryClient = useQueryClient();
763
904
  const queryKey = useMemo(
764
- () => [...QK, collectionId, search.trim(), pageSize, admin],
905
+ () => [...QK2, collectionId, search.trim(), pageSize, admin],
765
906
  [collectionId, search, pageSize, admin]
766
907
  );
767
908
  const query = useInfiniteQuery({
768
909
  queryKey,
769
- enabled: enabled && !!collectionId && !!SL?.product?.query,
910
+ enabled: enabled && !!collectionId && !!(SL?.products?.query || SL?.product?.query || SL?.products?.list || SL?.product?.list),
770
911
  initialPageParam: { offset: 0, cursor: null },
771
912
  queryFn: async ({ pageParam }) => {
772
913
  const body = {
@@ -774,7 +915,23 @@ var useProductBrowse = (args) => {
774
915
  sort: [{ field: "sortOrder", direction: "asc" }, { field: "name", direction: "asc" }]
775
916
  };
776
917
  if (search.trim()) body.query = { search: search.trim() };
777
- const res = await SL.product.query(collectionId, body, admin);
918
+ let res = null;
919
+ if (SL?.products?.query) {
920
+ res = await SL.products.query(collectionId, body);
921
+ } else if (SL?.product?.query) {
922
+ res = await SL.product.query(collectionId, body, admin);
923
+ } else {
924
+ const legacy = SL?.products?.list ? await SL.products.list(collectionId, admin) : SL?.product?.list ? await SL.product.list(collectionId, admin) : [];
925
+ const filteredLegacy = search.trim() ? legacy.filter((item) => `${item?.name ?? ""} ${item?.sku ?? ""} ${item?.id ?? ""}`.toLowerCase().includes(search.trim().toLowerCase())) : legacy;
926
+ const pageItems = filteredLegacy.slice(pageParam.offset, pageParam.offset + pageSize);
927
+ return {
928
+ items: pageItems.map(toBrowseItem),
929
+ nextOffset: pageParam.offset + pageItems.length,
930
+ nextCursor: null,
931
+ hasMore: pageParam.offset + pageItems.length < filteredLegacy.length,
932
+ total: filteredLegacy.length
933
+ };
934
+ }
778
935
  const items2 = (res?.items ?? []).map(toBrowseItem);
779
936
  const page = res?.page ?? {};
780
937
  return {
@@ -796,7 +953,7 @@ var useProductBrowse = (args) => {
796
953
  () => query.data?.pages.flatMap((p) => p.items) ?? [],
797
954
  [query.data]
798
955
  );
799
- const refetch = () => queryClient.invalidateQueries({ queryKey: [...QK, collectionId] });
956
+ const refetch = () => queryClient.invalidateQueries({ queryKey: [...QK2, collectionId] });
800
957
  return {
801
958
  items,
802
959
  total: query.data?.pages[query.data.pages.length - 1]?.total,
@@ -808,7 +965,7 @@ var useProductBrowse = (args) => {
808
965
  refetch
809
966
  };
810
967
  };
811
- var QK2 = ["records-admin", "product-children"];
968
+ var QK3 = ["records-admin", "product-children"];
812
969
  var variantToItem = (v) => ({
813
970
  id: v.id ?? v.variantId ?? "",
814
971
  name: v.name ?? v.label ?? v.id ?? "Untitled variant",
@@ -823,7 +980,7 @@ var useProductChildren = (args) => {
823
980
  const { SL, collectionId, productId, kind, enabled = true } = args;
824
981
  const queryClient = useQueryClient();
825
982
  const queryKey = useMemo(
826
- () => [...QK2, collectionId, productId ?? null, kind],
983
+ () => [...QK3, collectionId, productId ?? null, kind],
827
984
  [collectionId, productId, kind]
828
985
  );
829
986
  const query = useQuery({
@@ -841,7 +998,7 @@ var useProductChildren = (args) => {
841
998
  }
842
999
  });
843
1000
  const refetch = () => queryClient.invalidateQueries({
844
- queryKey: [...QK2, collectionId, productId ?? null]
1001
+ queryKey: [...QK3, collectionId, productId ?? null]
845
1002
  });
846
1003
  return {
847
1004
  items: query.data ?? [],
@@ -958,6 +1115,144 @@ var useDirtyNavigation = ({
958
1115
  );
959
1116
  return { runWithGuard };
960
1117
  };
1118
+ var ConfirmDialog = ({
1119
+ open,
1120
+ title,
1121
+ body,
1122
+ saveLabel,
1123
+ discardLabel,
1124
+ cancelLabel,
1125
+ onChoice
1126
+ }) => {
1127
+ const saveBtnRef = useRef(null);
1128
+ useEffect(() => {
1129
+ if (!open) return;
1130
+ const prev = document.body.style.overflow;
1131
+ document.body.style.overflow = "hidden";
1132
+ const t = window.setTimeout(() => saveBtnRef.current?.focus(), 0);
1133
+ const onKey = (e) => {
1134
+ if (e.key === "Escape") {
1135
+ e.stopPropagation();
1136
+ onChoice("cancel");
1137
+ }
1138
+ };
1139
+ window.addEventListener("keydown", onKey, true);
1140
+ return () => {
1141
+ window.clearTimeout(t);
1142
+ window.removeEventListener("keydown", onKey, true);
1143
+ document.body.style.overflow = prev;
1144
+ };
1145
+ }, [open, onChoice]);
1146
+ if (!open || typeof document === "undefined") return null;
1147
+ return createPortal(
1148
+ /* @__PURE__ */ jsxs(
1149
+ "div",
1150
+ {
1151
+ className: "ra-shell ra-confirm-root",
1152
+ role: "presentation",
1153
+ onMouseDown: (e) => e.stopPropagation(),
1154
+ onClick: (e) => e.stopPropagation(),
1155
+ children: [
1156
+ /* @__PURE__ */ jsx("div", { className: "ra-confirm-backdrop", onClick: () => onChoice("cancel") }),
1157
+ /* @__PURE__ */ jsxs(
1158
+ "div",
1159
+ {
1160
+ role: "alertdialog",
1161
+ "aria-modal": "true",
1162
+ "aria-labelledby": "ra-confirm-title",
1163
+ "aria-describedby": "ra-confirm-body",
1164
+ className: "ra-confirm-card",
1165
+ children: [
1166
+ /* @__PURE__ */ jsxs("div", { className: "ra-confirm-header", children: [
1167
+ /* @__PURE__ */ jsx("span", { className: "ra-confirm-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertTriangle, { className: "w-4 h-4" }) }),
1168
+ /* @__PURE__ */ jsx("h2", { id: "ra-confirm-title", className: "ra-confirm-title", children: title })
1169
+ ] }),
1170
+ /* @__PURE__ */ jsx("p", { id: "ra-confirm-body", className: "ra-confirm-body", children: body }),
1171
+ /* @__PURE__ */ jsxs("div", { className: "ra-confirm-actions", children: [
1172
+ /* @__PURE__ */ jsx(
1173
+ "button",
1174
+ {
1175
+ type: "button",
1176
+ className: "ra-confirm-btn ra-confirm-btn-ghost",
1177
+ onClick: () => onChoice("cancel"),
1178
+ children: cancelLabel
1179
+ }
1180
+ ),
1181
+ /* @__PURE__ */ jsx(
1182
+ "button",
1183
+ {
1184
+ type: "button",
1185
+ className: "ra-confirm-btn ra-confirm-btn-danger",
1186
+ onClick: () => onChoice("discard"),
1187
+ children: discardLabel
1188
+ }
1189
+ ),
1190
+ /* @__PURE__ */ jsx(
1191
+ "button",
1192
+ {
1193
+ type: "button",
1194
+ ref: saveBtnRef,
1195
+ className: "ra-confirm-btn ra-confirm-btn-primary",
1196
+ onClick: () => onChoice("save"),
1197
+ children: saveLabel
1198
+ }
1199
+ )
1200
+ ] })
1201
+ ]
1202
+ }
1203
+ )
1204
+ ]
1205
+ }
1206
+ ),
1207
+ document.body
1208
+ );
1209
+ };
1210
+ var DEFAULTS = {
1211
+ title: "Unsaved changes",
1212
+ body: "You have unsaved changes. Save them, discard them, or stay on this record?",
1213
+ saveLabel: "Save & continue",
1214
+ discardLabel: "Discard",
1215
+ cancelLabel: "Stay here"
1216
+ };
1217
+ var useConfirmDialog = () => {
1218
+ const [open, setOpen] = useState(false);
1219
+ const [state, setState] = useState(DEFAULTS);
1220
+ const resolverRef = useRef(null);
1221
+ const confirm = useCallback((i18n) => {
1222
+ setState({
1223
+ title: i18n?.title ?? DEFAULTS.title,
1224
+ body: i18n?.body ?? DEFAULTS.body,
1225
+ saveLabel: i18n?.save ?? DEFAULTS.saveLabel,
1226
+ discardLabel: i18n?.discard ?? DEFAULTS.discardLabel,
1227
+ cancelLabel: i18n?.cancel ?? DEFAULTS.cancelLabel
1228
+ });
1229
+ setOpen(true);
1230
+ return new Promise((resolve) => {
1231
+ resolverRef.current = resolve;
1232
+ });
1233
+ }, []);
1234
+ const handleChoice = useCallback((choice) => {
1235
+ setOpen(false);
1236
+ const r = resolverRef.current;
1237
+ resolverRef.current = null;
1238
+ r?.(choice);
1239
+ }, []);
1240
+ return {
1241
+ confirm,
1242
+ dialog: /* @__PURE__ */ jsx(
1243
+ ConfirmDialog,
1244
+ {
1245
+ open,
1246
+ title: state.title,
1247
+ body: state.body,
1248
+ saveLabel: state.saveLabel,
1249
+ discardLabel: state.discardLabel,
1250
+ cancelLabel: state.cancelLabel,
1251
+ onChoice: handleChoice
1252
+ }
1253
+ )
1254
+ };
1255
+ };
961
1256
  var LABELS = {
962
1257
  product: "Products",
963
1258
  facet: "Shared",
@@ -996,14 +1291,17 @@ var ScopeTabs = ({
996
1291
  );
997
1292
  }) });
998
1293
  };
999
- var StatusFilterPills = ({ value, onChange, counts, i18n }) => {
1294
+ var StatusFilterPills = ({ value, onChange, counts, i18n, hideZero }) => {
1000
1295
  const opts = [
1001
1296
  { key: "all", label: i18n.filterAll, count: counts.all },
1002
1297
  { key: "configured", label: i18n.filterConfigured, count: counts.configured },
1003
1298
  { key: "partial", label: i18n.filterPartial, count: counts.partial },
1004
1299
  { key: "empty", label: i18n.filterEmpty, count: counts.empty }
1005
1300
  ];
1006
- return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: opts.map((o) => {
1301
+ const visible = opts.filter(
1302
+ (o) => o.key === value || !hideZero?.includes(o.key) || o.count > 0
1303
+ );
1304
+ return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: visible.map((o) => {
1007
1305
  const active = value === o.key;
1008
1306
  return /* @__PURE__ */ jsxs(
1009
1307
  "button",
@@ -1498,25 +1796,49 @@ function RecordEditor({
1498
1796
  preview,
1499
1797
  bulkActions,
1500
1798
  footerExtra,
1501
- onBeforeDelete
1799
+ onBeforeDelete,
1800
+ headerLabel,
1801
+ headerSubtitle,
1802
+ headerMeta
1502
1803
  }) {
1503
- const sourceLabel = ctx.source === "self" ? "Own" : ctx.source === "inherited" ? "Inherited" : "New";
1804
+ const sourceLabel = ctx.source === "self" ? "Customised" : ctx.source === "inherited" ? "Inherited" : null;
1504
1805
  return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full", children: [
1505
1806
  /* @__PURE__ */ jsxs(
1506
1807
  "header",
1507
1808
  {
1508
- className: "sticky top-0 z-10 px-5 py-3 border-b flex items-center justify-between gap-3",
1809
+ className: "sticky top-0 z-40 px-5 py-3 border-b flex items-start justify-between gap-3",
1509
1810
  style: { borderColor: "hsl(var(--ra-border))", background: "hsl(var(--ra-surface))" },
1510
1811
  children: [
1511
1812
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1512
- /* @__PURE__ */ jsx(ScopeBreadcrumb, { scope: ctx.scope }),
1513
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1", children: [
1514
- /* @__PURE__ */ jsx(StatusDot, { source: ctx.source }),
1515
- /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: sourceLabel }),
1813
+ headerLabel ? /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1814
+ /* @__PURE__ */ jsx(
1815
+ "div",
1816
+ {
1817
+ className: "text-sm font-medium truncate",
1818
+ style: { color: "hsl(var(--ra-text))" },
1819
+ title: headerLabel,
1820
+ children: headerLabel
1821
+ }
1822
+ ),
1823
+ headerSubtitle && /* @__PURE__ */ jsx(
1824
+ "div",
1825
+ {
1826
+ className: "text-xs truncate",
1827
+ style: { color: "hsl(var(--ra-muted-text))" },
1828
+ title: headerSubtitle,
1829
+ children: headerSubtitle
1830
+ }
1831
+ )
1832
+ ] }) : /* @__PURE__ */ jsx(ScopeBreadcrumb, { scope: ctx.scope }),
1833
+ (sourceLabel || ctx.isDirty) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 mt-1", children: [
1834
+ sourceLabel && /* @__PURE__ */ jsxs(Fragment, { children: [
1835
+ /* @__PURE__ */ jsx(StatusDot, { source: ctx.source }),
1836
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] uppercase tracking-wide", style: { color: "hsl(var(--ra-muted-text))" }, children: sourceLabel })
1837
+ ] }),
1516
1838
  ctx.isDirty && /* @__PURE__ */ jsxs(
1517
1839
  "span",
1518
1840
  {
1519
- className: "ml-2 flex items-center gap-1 text-[10px] uppercase tracking-wide",
1841
+ className: `flex items-center gap-1 text-[10px] uppercase tracking-wide ${sourceLabel ? "ml-2" : ""}`,
1520
1842
  style: { color: "hsl(var(--ra-accent))" },
1521
1843
  children: [
1522
1844
  /* @__PURE__ */ jsx("span", { className: "ra-status-dot ra-status-shared", "aria-hidden": "true" }),
@@ -1526,7 +1848,22 @@ function RecordEditor({
1526
1848
  )
1527
1849
  ] })
1528
1850
  ] }),
1529
- bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
1851
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 shrink-0", children: [
1852
+ headerMeta && /* @__PURE__ */ jsx(
1853
+ "code",
1854
+ {
1855
+ className: "text-[10px] font-mono px-1.5 py-0.5 rounded truncate max-w-[14rem] mt-0.5",
1856
+ style: {
1857
+ color: "hsl(var(--ra-muted-text) / 0.85)",
1858
+ background: "hsl(var(--ra-muted) / 0.5)",
1859
+ border: "1px solid hsl(var(--ra-border) / 0.6)"
1860
+ },
1861
+ title: headerMeta,
1862
+ children: headerMeta
1863
+ }
1864
+ ),
1865
+ bulkActions && /* @__PURE__ */ jsx(BulkActionsMenu, { ...bulkActions, i18n })
1866
+ ] })
1530
1867
  ]
1531
1868
  }
1532
1869
  ),
@@ -1741,13 +2078,29 @@ var InlinePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE
1741
2078
  ] }),
1742
2079
  children
1743
2080
  ] });
1744
- var SidePreview = ({ children, scopePicker, label = "Preview" }) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
2081
+ var SidePreview = ({
2082
+ children,
2083
+ scopePicker,
2084
+ label = "Preview",
2085
+ onClose
2086
+ }) => /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail", children: [
1745
2087
  /* @__PURE__ */ jsxs("header", { className: "ra-preview-rail-header", children: [
1746
2088
  /* @__PURE__ */ jsxs("div", { className: "ra-preview-rail-title", children: [
1747
2089
  /* @__PURE__ */ jsx(Eye, { className: "w-3 h-3" }),
1748
2090
  label
1749
2091
  ] }),
1750
- scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker })
2092
+ scopePicker && /* @__PURE__ */ jsx("div", { className: "ml-auto", children: scopePicker }),
2093
+ onClose && /* @__PURE__ */ jsx(
2094
+ "button",
2095
+ {
2096
+ type: "button",
2097
+ onClick: onClose,
2098
+ "aria-label": "Close preview",
2099
+ className: scopePicker ? "p-1 rounded hover:bg-[hsl(var(--ra-muted))]" : "ml-auto p-1 rounded hover:bg-[hsl(var(--ra-muted))]",
2100
+ style: { color: "hsl(var(--ra-muted-text))" },
2101
+ children: /* @__PURE__ */ jsx(X, { className: "w-3.5 h-3.5" })
2102
+ }
2103
+ )
1751
2104
  ] }),
1752
2105
  /* @__PURE__ */ jsx("div", { className: "ra-preview-rail-body", children })
1753
2106
  ] });
@@ -1861,7 +2214,13 @@ var PreviewScopePicker = ({
1861
2214
  showBatches,
1862
2215
  i18n
1863
2216
  }) => {
1864
- const products = useProductBrowse({ SL, collectionId, pageSize: 100 });
2217
+ const productPinned = !!editingScope.productId;
2218
+ const products = useProductBrowse({
2219
+ SL,
2220
+ collectionId,
2221
+ pageSize: 100,
2222
+ enabled: !productPinned
2223
+ });
1865
2224
  const variants = useProductChildren({
1866
2225
  SL,
1867
2226
  collectionId,
@@ -1878,6 +2237,27 @@ var PreviewScopePicker = ({
1878
2237
  () => value.raw === editingScope.raw,
1879
2238
  [value.raw, editingScope.raw]
1880
2239
  );
2240
+ useEffect(() => {
2241
+ if (productPinned) return;
2242
+ if (value.productId) return;
2243
+ const first = products.items[0];
2244
+ if (!first) return;
2245
+ onChange({
2246
+ ...value,
2247
+ productId: first.id,
2248
+ variantId: void 0,
2249
+ batchId: void 0,
2250
+ raw: `product:${first.id}`
2251
+ });
2252
+ }, [productPinned, value.productId, products.items]);
2253
+ const [productPickerOpen, setProductPickerOpen] = useState(false);
2254
+ const showProductPicker = !productPinned && products.items.length > 1;
2255
+ const showVariantPicker = showVariants && !!value.productId && variants.items.length > 0;
2256
+ const showBatchPicker = showBatches && !!value.productId && batches.items.length > 0;
2257
+ if (!showProductPicker && !showVariantPicker && !showBatchPicker) {
2258
+ return null;
2259
+ }
2260
+ const currentProductName = products.items.find((p) => p.id === value.productId)?.name ?? value.productId ?? "";
1881
2261
  const selectStyle = {
1882
2262
  borderColor: "hsl(var(--ra-border))",
1883
2263
  color: "hsl(var(--ra-text))"
@@ -1907,30 +2287,44 @@ var PreviewScopePicker = ({
1907
2287
  ]
1908
2288
  }
1909
2289
  ),
1910
- /* @__PURE__ */ jsxs(
2290
+ showProductPicker && !productPickerOpen && currentProductName && /* @__PURE__ */ jsxs(
2291
+ "button",
2292
+ {
2293
+ type: "button",
2294
+ onClick: () => setProductPickerOpen(true),
2295
+ 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",
2296
+ style: selectStyle,
2297
+ title: `Preview as ${currentProductName} \u2014 click to change`,
2298
+ children: [
2299
+ "as ",
2300
+ /* @__PURE__ */ jsx("span", { className: "font-medium truncate", children: currentProductName }),
2301
+ /* @__PURE__ */ jsx(ChevronDown, { className: "w-3 h-3 opacity-60" })
2302
+ ]
2303
+ }
2304
+ ),
2305
+ showProductPicker && productPickerOpen && /* @__PURE__ */ jsx(
1911
2306
  "select",
1912
2307
  {
1913
2308
  className: SELECT_CLS,
1914
2309
  style: selectStyle,
2310
+ autoFocus: true,
2311
+ onBlur: () => setProductPickerOpen(false),
1915
2312
  value: value.productId ?? "",
1916
2313
  onChange: (e) => {
1917
2314
  const productId = e.target.value || void 0;
1918
2315
  onChange({
1919
2316
  ...value,
1920
2317
  productId,
1921
- // Clear variant/batch when switching products.
1922
2318
  variantId: void 0,
1923
2319
  batchId: void 0,
1924
2320
  raw: productId ? `product:${productId}` : ""
1925
2321
  });
2322
+ setProductPickerOpen(false);
1926
2323
  },
1927
- children: [
1928
- /* @__PURE__ */ jsx("option", { value: "", children: "\u2014 Any product \u2014" }),
1929
- products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
1930
- ]
2324
+ children: products.items.map((p) => /* @__PURE__ */ jsx("option", { value: p.id, children: p.name }, p.id))
1931
2325
  }
1932
2326
  ),
1933
- showVariants && value.productId && variants.items.length > 0 && /* @__PURE__ */ jsxs(
2327
+ showVariantPicker && /* @__PURE__ */ jsxs(
1934
2328
  "select",
1935
2329
  {
1936
2330
  className: SELECT_CLS,
@@ -1951,7 +2345,7 @@ var PreviewScopePicker = ({
1951
2345
  ]
1952
2346
  }
1953
2347
  ),
1954
- showBatches && value.productId && batches.items.length > 0 && /* @__PURE__ */ jsxs(
2348
+ showBatchPicker && /* @__PURE__ */ jsxs(
1955
2349
  "select",
1956
2350
  {
1957
2351
  className: SELECT_CLS,
@@ -2037,7 +2431,9 @@ function ShellHeader({
2037
2431
  recordType,
2038
2432
  icons,
2039
2433
  stats,
2040
- statsItems
2434
+ statsItems,
2435
+ statsTitle,
2436
+ statsIcon
2041
2437
  }) {
2042
2438
  let iconNode = null;
2043
2439
  if (showHeaderIcon) {
@@ -2061,6 +2457,7 @@ function ShellHeader({
2061
2457
  return items;
2062
2458
  })();
2063
2459
  const hasStats = resolvedItems.length > 0;
2460
+ const hasStatsHeading = !!(statsTitle || statsIcon);
2064
2461
  return /* @__PURE__ */ jsxs("header", { className: "ra-header", children: [
2065
2462
  /* @__PURE__ */ jsxs("div", { className: "ra-header__main", children: [
2066
2463
  iconNode ? /* @__PURE__ */ jsx("div", { className: "ra-header-icon", "aria-hidden": "true", children: iconNode }) : null,
@@ -2070,17 +2467,86 @@ function ShellHeader({
2070
2467
  ] })
2071
2468
  ] }),
2072
2469
  /* @__PURE__ */ jsxs("div", { className: "ra-header-aside", children: [
2073
- hasStats ? /* @__PURE__ */ jsx("div", { className: "ra-header-stats", role: "group", "aria-label": "Record counts", children: resolvedItems.map((item, idx) => /* @__PURE__ */ jsxs("span", { style: { display: "contents" }, children: [
2074
- idx > 0 && /* @__PURE__ */ jsx("span", { className: "ra-stat-divider", "aria-hidden": "true" }),
2075
- /* @__PURE__ */ jsxs("span", { className: "ra-stat", children: [
2076
- /* @__PURE__ */ jsx("span", { className: "ra-stat-value", children: item.value }),
2077
- /* @__PURE__ */ jsx("span", { className: "ra-stat-label", children: item.label })
2078
- ] })
2079
- ] }, `${item.label}-${idx}`)) }) : null,
2470
+ hasStats ? /* @__PURE__ */ jsxs(
2471
+ "div",
2472
+ {
2473
+ className: `ra-header-stats${hasStatsHeading ? " ra-header-stats--titled" : ""}`,
2474
+ role: "group",
2475
+ "aria-label": statsTitle ?? "Record counts",
2476
+ children: [
2477
+ hasStatsHeading ? /* @__PURE__ */ jsxs("div", { className: "ra-stats-heading", children: [
2478
+ statsIcon ? /* @__PURE__ */ jsx("span", { className: "ra-stats-heading-icon", "aria-hidden": "true", children: statsIcon }) : null,
2479
+ statsTitle ? /* @__PURE__ */ jsx("span", { className: "ra-stats-heading-label", children: statsTitle }) : null
2480
+ ] }) : null,
2481
+ /* @__PURE__ */ jsx("div", { className: "ra-stats-items", children: resolvedItems.map((item, idx) => /* @__PURE__ */ jsxs("span", { style: { display: "contents" }, children: [
2482
+ idx > 0 && /* @__PURE__ */ jsx("span", { className: "ra-stat-divider", "aria-hidden": "true" }),
2483
+ /* @__PURE__ */ jsxs("span", { className: "ra-stat", children: [
2484
+ /* @__PURE__ */ jsx("span", { className: "ra-stat-value", children: item.value }),
2485
+ /* @__PURE__ */ jsx("span", { className: "ra-stat-label", children: item.label })
2486
+ ] })
2487
+ ] }, `${item.label}-${idx}`)) })
2488
+ ]
2489
+ }
2490
+ ) : null,
2080
2491
  headerActions ? /* @__PURE__ */ jsx("div", { className: "ra-header-actions", children: headerActions }) : null
2081
2492
  ] })
2082
2493
  ] });
2083
2494
  }
2495
+ var UnsavedBanner = ({
2496
+ label,
2497
+ context,
2498
+ isSaving,
2499
+ saveError,
2500
+ onSave,
2501
+ onDiscard,
2502
+ saveLabel,
2503
+ discardLabel,
2504
+ bodyTemplate,
2505
+ savingLabel,
2506
+ errorLabel
2507
+ }) => {
2508
+ const name = label ?? context ?? "this record";
2509
+ const message = bodyTemplate.replace("{name}", name);
2510
+ return /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-banner", role: "status", "aria-live": "polite", children: [
2511
+ /* @__PURE__ */ jsx("span", { className: "ra-unsaved-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx(AlertCircle, { className: "w-3.5 h-3.5" }) }),
2512
+ /* @__PURE__ */ jsxs("span", { className: "ra-unsaved-text", children: [
2513
+ message,
2514
+ context && label && /* @__PURE__ */ jsxs("span", { className: "ra-unsaved-context", children: [
2515
+ " \xB7 ",
2516
+ context
2517
+ ] })
2518
+ ] }),
2519
+ saveError != null && !isSaving && /* @__PURE__ */ jsx("span", { className: "ra-unsaved-error", role: "alert", children: errorLabel ?? "Save failed" }),
2520
+ /* @__PURE__ */ jsxs("div", { className: "ra-unsaved-actions", children: [
2521
+ /* @__PURE__ */ jsxs(
2522
+ "button",
2523
+ {
2524
+ type: "button",
2525
+ className: "ra-unsaved-btn ra-unsaved-btn-ghost",
2526
+ onClick: onDiscard,
2527
+ disabled: isSaving,
2528
+ children: [
2529
+ /* @__PURE__ */ jsx(Undo2, { className: "w-3 h-3" }),
2530
+ discardLabel
2531
+ ]
2532
+ }
2533
+ ),
2534
+ /* @__PURE__ */ jsxs(
2535
+ "button",
2536
+ {
2537
+ type: "button",
2538
+ className: "ra-unsaved-btn ra-unsaved-btn-primary",
2539
+ onClick: onSave,
2540
+ disabled: isSaving,
2541
+ children: [
2542
+ /* @__PURE__ */ jsx(Save, { className: "w-3 h-3" }),
2543
+ isSaving ? savingLabel ?? "Saving\u2026" : saveLabel
2544
+ ]
2545
+ }
2546
+ )
2547
+ ] })
2548
+ ] });
2549
+ };
2084
2550
 
2085
2551
  // src/components/RecordsAdmin/data/csv.ts
2086
2552
  var escapeCell = (s) => {
@@ -2190,8 +2656,8 @@ var downloadBlob = (blob, filename) => {
2190
2656
  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');
2191
2657
 
2192
2658
  // src/components/RecordsAdmin/shell/shell.css
2193
- 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-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");
2194
- var TOP_LEVEL_SCOPES = ["product", "facet"];
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");
2660
+ var TOP_LEVEL_SCOPES = ["facet", "product"];
2195
2661
  var defaultItemId = () => {
2196
2662
  const time = Date.now().toString(36);
2197
2663
  const rand = Math.random().toString(36).slice(2, 8);
@@ -2230,7 +2696,11 @@ function RecordsAdminShell(props) {
2230
2696
  className,
2231
2697
  previewMode = "inline",
2232
2698
  previewScopePicker = false,
2233
- dirtyStrategy = "prompt",
2699
+ // Default to `keep` — admins can tab around freely while edits are
2700
+ // preserved per editor mount, and the inline UnsavedBanner surfaces the
2701
+ // dirty state at the top of the shell with Save/Discard buttons. Hosts
2702
+ // that want the old blocking behaviour can opt back into `'prompt'`.
2703
+ dirtyStrategy = "keep",
2234
2704
  onBeforeDelete,
2235
2705
  disableBeforeUnload = false,
2236
2706
  presentations = ["list"],
@@ -2248,6 +2718,9 @@ function RecordsAdminShell(props) {
2248
2718
  showStats = false,
2249
2719
  showHeaderIcon = true,
2250
2720
  statsItems,
2721
+ statsTitle,
2722
+ statsIcon,
2723
+ showHeader,
2251
2724
  icons: iconsOverride,
2252
2725
  groupBy,
2253
2726
  renderEmptyState,
@@ -2271,7 +2744,7 @@ function RecordsAdminShell(props) {
2271
2744
  );
2272
2745
  const probe = useScopeProbe({ SL, collectionId });
2273
2746
  const topLevelScopes = useMemo(
2274
- () => requestedScopes.filter((s) => TOP_LEVEL_SCOPES.includes(s)),
2747
+ () => TOP_LEVEL_SCOPES.filter((s) => requestedScopes.includes(s)),
2275
2748
  [requestedScopes]
2276
2749
  );
2277
2750
  const drillVariantsAllowed = useMemo(
@@ -2285,6 +2758,7 @@ function RecordsAdminShell(props) {
2285
2758
  const initialScope = useMemo(() => {
2286
2759
  if (contextScope?.productId && topLevelScopes.includes("product")) return "product";
2287
2760
  if (defaultScope && topLevelScopes.includes(defaultScope)) return defaultScope;
2761
+ if (topLevelScopes.includes("facet")) return "facet";
2288
2762
  return topLevelScopes[0] ?? "product";
2289
2763
  }, [contextScope?.productId, defaultScope, topLevelScopes]);
2290
2764
  const [activeScope, setActiveScope] = useState(initialScope);
@@ -2337,6 +2811,14 @@ function RecordsAdminShell(props) {
2337
2811
  contextScope,
2338
2812
  enabled: activeScope === "facet" && !probe.isLoading
2339
2813
  });
2814
+ const facetBrowse = useFacetBrowse({
2815
+ SL,
2816
+ collectionId,
2817
+ existing: recordList.allItems,
2818
+ search,
2819
+ filter,
2820
+ enabled: activeScope === "facet" && !probe.isLoading
2821
+ });
2340
2822
  const variantChildren = useProductChildren({
2341
2823
  SL,
2342
2824
  collectionId,
@@ -2358,9 +2840,9 @@ function RecordsAdminShell(props) {
2358
2840
  useEffect(() => {
2359
2841
  if (activeScope !== "facet") return;
2360
2842
  if (selectedFacetRef) return;
2361
- const first = recordList.items[0];
2843
+ const first = facetBrowse.items[0];
2362
2844
  if (first) setSelectedFacetRef(first.ref);
2363
- }, [activeScope, selectedFacetRef, recordList.items]);
2845
+ }, [activeScope, selectedFacetRef, facetBrowse.items]);
2364
2846
  useEffect(() => {
2365
2847
  setSearch("");
2366
2848
  setFilter("all");
@@ -2397,9 +2879,10 @@ function RecordsAdminShell(props) {
2397
2879
  const refetchAll = useCallback(() => {
2398
2880
  productBrowse.refetch();
2399
2881
  recordList.refetch();
2882
+ facetBrowse.refetch();
2400
2883
  variantChildren.refetch();
2401
2884
  batchChildren.refetch();
2402
- }, [productBrowse, recordList, variantChildren, batchChildren]);
2885
+ }, [productBrowse, recordList, facetBrowse, variantChildren, batchChildren]);
2403
2886
  const editorCtx = useRecordEditor({
2404
2887
  ctx,
2405
2888
  scope: editingScope ?? parseRef(""),
@@ -2425,13 +2908,19 @@ function RecordsAdminShell(props) {
2425
2908
  useUnsavedGuard({
2426
2909
  isDirty: editorCtx.isDirty,
2427
2910
  label: recordType,
2428
- disableBeforeUnload
2911
+ disableBeforeUnload,
2912
+ // In `keep` mode we don't want the host iframe to pop its own native
2913
+ // confirm — the banner is the canonical surface. Hosts on `prompt` /
2914
+ // `autosave` opt back into platform notifications.
2915
+ disableParentMessage: dirtyStrategy === "keep"
2429
2916
  });
2917
+ const dirtyConfirm = useConfirmDialog();
2430
2918
  const { runWithGuard } = useDirtyNavigation({
2431
2919
  strategy: dirtyStrategy,
2432
2920
  isDirty: editorCtx.isDirty,
2433
2921
  save: editorCtx.save,
2434
2922
  reset: editorCtx.reset,
2923
+ confirm: dirtyConfirm.confirm,
2435
2924
  i18n: {
2436
2925
  title: i18n.unsavedPromptTitle,
2437
2926
  body: i18n.unsavedPromptBody,
@@ -2468,11 +2957,40 @@ function RecordsAdminShell(props) {
2468
2957
  const csvBulk = csvSchema ? { onImportCsv: handleImport, onExportCsv: handleExport } : {};
2469
2958
  const [previewScope, setPreviewScope] = useState(null);
2470
2959
  const [drawerOpen, setDrawerOpen] = useState(false);
2960
+ const [sidePreviewOpen, setSidePreviewOpen] = useState(true);
2471
2961
  useEffect(() => {
2472
2962
  if (!editingScope) return;
2473
2963
  setPreviewScope((cur) => cur === null ? editingScope : cur);
2474
2964
  }, [editingScope]);
2475
2965
  const effectivePreviewScope = previewScope ?? editingScope;
2966
+ const editorHeaderLabel = useMemo(() => {
2967
+ if (!editingScope) return void 0;
2968
+ if (activeScope === "facet" && selectedFacetRef) {
2969
+ const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
2970
+ return hit?.label;
2971
+ }
2972
+ if (activeScope === "product" && selectedProductId) {
2973
+ const hit = productBrowse.items.find((it) => it.id === selectedProductId);
2974
+ return hit?.name ?? selectedProductId;
2975
+ }
2976
+ return void 0;
2977
+ }, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
2978
+ const editorHeaderSubtitle = useMemo(() => {
2979
+ if (!editingScope) return void 0;
2980
+ if (activeScope === "facet" && selectedFacetRef) {
2981
+ const hit = facetBrowse.items.find((it) => it.ref === selectedFacetRef);
2982
+ return hit?.subtitle;
2983
+ }
2984
+ return void 0;
2985
+ }, [activeScope, editingScope, selectedFacetRef, selectedProductId, facetBrowse.items, productBrowse.items]);
2986
+ const editorHeaderMeta = useMemo(() => {
2987
+ if (!editingScope) return void 0;
2988
+ if (activeScope === "product" && selectedProductId) {
2989
+ const hit = productBrowse.items.find((it) => it.id === selectedProductId);
2990
+ return hit?.sku ?? selectedProductId;
2991
+ }
2992
+ return void 0;
2993
+ }, [activeScope, editingScope, selectedProductId, productBrowse.items]);
2476
2994
  const renderEditorWithPreview = () => {
2477
2995
  if (!editingScope) return null;
2478
2996
  const previewBody = renderPreview && effectivePreviewScope ? renderPreview({ resolved: editorCtx.value, previewScope: effectivePreviewScope }) : null;
@@ -2498,6 +3016,9 @@ function RecordsAdminShell(props) {
2498
3016
  preview: inlinePreviewBody,
2499
3017
  footerExtra: extraFooter,
2500
3018
  onBeforeDelete: onBeforeDelete && editingScope ? () => onBeforeDelete(editingScope) : void 0,
3019
+ headerLabel: editorHeaderLabel,
3020
+ headerSubtitle: editorHeaderSubtitle,
3021
+ headerMeta: editorHeaderMeta,
2501
3022
  children: renderEditor(editorCtx)
2502
3023
  }
2503
3024
  );
@@ -2509,9 +3030,28 @@ function RecordsAdminShell(props) {
2509
3030
  );
2510
3031
  }
2511
3032
  if (previewMode === "side") {
3033
+ if (!sidePreviewOpen) {
3034
+ return /* @__PURE__ */ jsx("div", { className: "relative h-full", children: baseEditor(
3035
+ /* @__PURE__ */ jsx(
3036
+ PreviewToggleButton,
3037
+ {
3038
+ onClick: () => setSidePreviewOpen(true),
3039
+ label: i18n.openPreview
3040
+ }
3041
+ )
3042
+ ) });
3043
+ }
2512
3044
  return /* @__PURE__ */ jsxs("div", { className: "grid h-full", style: { gridTemplateColumns: "minmax(0, 1fr) minmax(280px, 420px)" }, children: [
2513
3045
  /* @__PURE__ */ jsx("div", { className: "overflow-hidden", children: baseEditor() }),
2514
- /* @__PURE__ */ jsx(SidePreview, { label: i18n.preview, scopePicker, children: previewBody })
3046
+ /* @__PURE__ */ jsx(
3047
+ SidePreview,
3048
+ {
3049
+ label: i18n.preview,
3050
+ scopePicker,
3051
+ onClose: () => setSidePreviewOpen(false),
3052
+ children: previewBody
3053
+ }
3054
+ )
2515
3055
  ] });
2516
3056
  }
2517
3057
  if (previewMode === "tab") {
@@ -2543,6 +3083,18 @@ function RecordsAdminShell(props) {
2543
3083
  };
2544
3084
  const isProductTab = activeScope === "product";
2545
3085
  const productPinned = !!contextScope?.productId;
3086
+ const effectivePresentation = isProductTab ? presentation : "list";
3087
+ const showPresentationSwitcher = isProductTab && presentations.length > 1;
3088
+ const effectiveGroupBy = useMemo(() => {
3089
+ if (groupBy) return groupBy;
3090
+ if (isProductTab) return void 0;
3091
+ return (record) => {
3092
+ const key = record.scope.facetId;
3093
+ if (!key) return null;
3094
+ const label2 = record.subtitle ?? key;
3095
+ return { key, label: label2 };
3096
+ };
3097
+ }, [groupBy, isProductTab]);
2546
3098
  const productListItems = useMemo(() => {
2547
3099
  if (productPinned) {
2548
3100
  return [{
@@ -2557,9 +3109,9 @@ function RecordsAdminShell(props) {
2557
3109
  }
2558
3110
  return productBrowse.items.map(productItemToSummary);
2559
3111
  }, [productPinned, contextScope, productBrowse.items]);
2560
- const leftItems = isProductTab ? productListItems : recordList.items;
2561
- const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : recordList.isLoading || probe.isLoading;
2562
- const leftError = isProductTab ? productBrowse.error : recordList.error;
3112
+ const leftItems = isProductTab ? productListItems : facetBrowse.items;
3113
+ const leftLoading = isProductTab ? !productPinned && productBrowse.isLoading : facetBrowse.isLoading || recordList.isLoading || probe.isLoading;
3114
+ const leftError = isProductTab ? productBrowse.error : facetBrowse.error ?? recordList.error;
2563
3115
  const leftSelectedRef = isProductTab ? selectedProductId ? buildRef({ productId: selectedProductId }) : void 0 : selectedFacetRef;
2564
3116
  const onLeftSelect = (item) => {
2565
3117
  void runWithGuard(() => {
@@ -2579,6 +3131,7 @@ function RecordsAdminShell(props) {
2579
3131
  className: `ra-shell flex flex-col h-full ${className ?? ""}`,
2580
3132
  "data-density": density,
2581
3133
  children: [
3134
+ dirtyConfirm.dialog,
2582
3135
  /* @__PURE__ */ jsxs("div", { className: "px-4 pt-4 space-y-3", children: [
2583
3136
  intro && !dismissed && /* @__PURE__ */ jsx(
2584
3137
  IntroCard,
@@ -2591,24 +3144,31 @@ function RecordsAdminShell(props) {
2591
3144
  }
2592
3145
  }
2593
3146
  ),
2594
- /* @__PURE__ */ jsx(
2595
- ShellHeader,
2596
- {
2597
- title: title ?? label ?? recordType,
2598
- subtitle,
2599
- headerIcon,
2600
- headerActions,
2601
- showStats,
2602
- showHeaderIcon,
2603
- recordType,
2604
- icons,
2605
- stats: {
2606
- products: productBrowse.items.length,
2607
- shared: recordList.items.length
2608
- },
2609
- statsItems
2610
- }
2611
- ),
3147
+ (() => {
3148
+ const headerCustomised = !!title || !!subtitle || !!headerIcon || !!headerActions || showStats || !!statsItems || !!statsTitle || !!statsIcon;
3149
+ const renderHeader = showHeader === true || showHeader !== false && headerCustomised;
3150
+ if (!renderHeader) return null;
3151
+ return /* @__PURE__ */ jsx(
3152
+ ShellHeader,
3153
+ {
3154
+ title: title ?? label ?? recordType ?? "",
3155
+ subtitle,
3156
+ headerIcon,
3157
+ headerActions,
3158
+ showStats,
3159
+ showHeaderIcon,
3160
+ recordType,
3161
+ icons,
3162
+ stats: {
3163
+ products: productBrowse.items.length,
3164
+ shared: recordList.items.length
3165
+ },
3166
+ statsItems,
3167
+ statsTitle,
3168
+ statsIcon
3169
+ }
3170
+ );
3171
+ })(),
2612
3172
  /* @__PURE__ */ jsx(
2613
3173
  UtilityRow,
2614
3174
  {
@@ -2616,6 +3176,25 @@ function RecordsAdminShell(props) {
2616
3176
  introHidden: dismissed && !!intro,
2617
3177
  onShowIntro: undismiss
2618
3178
  }
3179
+ ),
3180
+ editorCtx.isDirty && /* @__PURE__ */ jsx(
3181
+ UnsavedBanner,
3182
+ {
3183
+ label: editorHeaderLabel,
3184
+ context: editorHeaderSubtitle,
3185
+ isSaving: !!editorCtx.isSaving,
3186
+ saveError: editorCtx.saveError,
3187
+ onSave: () => {
3188
+ void editorCtx.save().catch(() => {
3189
+ });
3190
+ },
3191
+ onDiscard: editorCtx.reset,
3192
+ saveLabel: i18n.save,
3193
+ discardLabel: i18n.discard,
3194
+ savingLabel: i18n.saving,
3195
+ errorLabel: i18n.saveError,
3196
+ bodyTemplate: i18n.unsavedBannerBody
3197
+ }
2619
3198
  )
2620
3199
  ] }),
2621
3200
  /* @__PURE__ */ jsxs(
@@ -2639,7 +3218,7 @@ function RecordsAdminShell(props) {
2639
3218
  loading: probe.isLoading,
2640
3219
  counts: {
2641
3220
  product: productBrowse.items.length,
2642
- facet: recordList.items.length
3221
+ facet: facetBrowse.items.length
2643
3222
  },
2644
3223
  icons: icons.scope
2645
3224
  }
@@ -2663,14 +3242,23 @@ function RecordsAdminShell(props) {
2663
3242
  /* @__PURE__ */ jsx(
2664
3243
  PresentationSwitcher,
2665
3244
  {
2666
- options: presentations,
3245
+ options: showPresentationSwitcher ? presentations : [],
2667
3246
  value: presentation,
2668
3247
  onChange: onPresentationChange,
2669
3248
  i18n
2670
3249
  }
2671
3250
  )
2672
3251
  ] }),
2673
- !isProductTab && /* @__PURE__ */ jsx(StatusFilterPills, { value: filter, onChange: setFilter, counts: recordList.counts, i18n }),
3252
+ !isProductTab && /* @__PURE__ */ jsx(
3253
+ StatusFilterPills,
3254
+ {
3255
+ value: filter,
3256
+ onChange: setFilter,
3257
+ counts: facetBrowse.counts,
3258
+ hideZero: ["partial"],
3259
+ i18n
3260
+ }
3261
+ ),
2674
3262
  cardinality === "collection" && !isProductTab && editingScope && /* @__PURE__ */ jsxs(
2675
3263
  "button",
2676
3264
  {
@@ -2712,10 +3300,10 @@ function RecordsAdminShell(props) {
2712
3300
  selectedRef: leftSelectedRef,
2713
3301
  onSelect: onLeftSelect,
2714
3302
  dirtyRef: editorCtx.isDirty ? editingScope?.raw : void 0,
2715
- presentation,
3303
+ presentation: effectivePresentation,
2716
3304
  renderListRow,
2717
3305
  renderCard,
2718
- groupBy
3306
+ groupBy: effectiveGroupBy
2719
3307
  }
2720
3308
  ),
2721
3309
  isProductTab && !productPinned && productBrowse.hasNextPage && /* @__PURE__ */ jsx("div", { className: "p-2", children: /* @__PURE__ */ jsx(
@@ -3175,6 +3763,6 @@ function useMergedRecord(args) {
3175
3763
  };
3176
3764
  }
3177
3765
 
3178
- 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, useIntroDismissed, useMergedRecord, usePresentationPref, useProductBrowse, useProductChildren, useRecordEditor, useRecordList, useResolveAllRecords, useResolvedRecord, useRulePreview, useScopeProbe, useUnsavedGuard };
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 };
3179
3767
  //# sourceMappingURL=index.js.map
3180
3768
  //# sourceMappingURL=index.js.map