@drawnagency/primitives 0.1.79 → 0.1.80

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.
Files changed (35) hide show
  1. package/dist/{chunk-FRTJNS5F.js → chunk-4TVODHD2.js} +3 -3
  2. package/dist/{chunk-4XXLH5CL.js → chunk-7XWER6YR.js} +17 -10
  3. package/dist/{chunk-RWMSDDQM.js → chunk-KVQFMVXE.js} +77 -24
  4. package/dist/{chunk-DR22HM44.js → chunk-VI26O4E3.js} +1 -1
  5. package/dist/components/brandguide/Colors.d.ts +2 -1
  6. package/dist/components/brandguide/Colors.d.ts.map +1 -1
  7. package/dist/components/primitives/EditableGrid.d.ts +11 -1
  8. package/dist/components/primitives/EditableGrid.d.ts.map +1 -1
  9. package/dist/components/primitives/gridDrop.d.ts +18 -0
  10. package/dist/components/primitives/gridDrop.d.ts.map +1 -0
  11. package/dist/components/primitives/index.js +2 -2
  12. package/dist/components/primitives/useEditableCollection.d.ts +1 -0
  13. package/dist/components/primitives/useEditableCollection.d.ts.map +1 -1
  14. package/dist/components/sections/Colors/index.d.ts.map +1 -1
  15. package/dist/components/sections/brand-guide.js +2 -2
  16. package/dist/components/sections/register-schemas.js +4 -4
  17. package/dist/components/sections/register.js +4 -4
  18. package/dist/components/shell/EditorContext.d.ts +2 -0
  19. package/dist/components/shell/EditorContext.d.ts.map +1 -1
  20. package/dist/components/shell/EditorShell.d.ts +13 -0
  21. package/dist/components/shell/EditorShell.d.ts.map +1 -1
  22. package/dist/components/shell/HistoryToolbar.d.ts +8 -1
  23. package/dist/components/shell/HistoryToolbar.d.ts.map +1 -1
  24. package/dist/lib/registry.d.ts +3 -0
  25. package/dist/lib/registry.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/components/brandguide/Colors.tsx +15 -5
  28. package/src/components/primitives/EditableGrid.tsx +86 -24
  29. package/src/components/primitives/gridDrop.ts +26 -0
  30. package/src/components/primitives/useEditableCollection.ts +16 -1
  31. package/src/components/sections/Colors/index.tsx +2 -1
  32. package/src/components/shell/EditorContext.tsx +6 -1
  33. package/src/components/shell/EditorShell.tsx +43 -5
  34. package/src/components/shell/HistoryToolbar.tsx +24 -2
  35. package/src/lib/registry.ts +3 -0
@@ -3,7 +3,7 @@ import {
3
3
  pluralize,
4
4
  stripHtmlToPlainText,
5
5
  truncate
6
- } from "./chunk-4XXLH5CL.js";
6
+ } from "./chunk-7XWER6YR.js";
7
7
  import {
8
8
  DeleteButton,
9
9
  DragHandle,
@@ -19,7 +19,7 @@ import {
19
19
  import {
20
20
  EditableRichText,
21
21
  defineHeadingSection
22
- } from "./chunk-DR22HM44.js";
22
+ } from "./chunk-VI26O4E3.js";
23
23
  import {
24
24
  EditablePlainText,
25
25
  ImageDropZone,
@@ -28,7 +28,7 @@ import {
28
28
  colSpanClass,
29
29
  containerGridClass,
30
30
  spacerHiddenClass
31
- } from "./chunk-RWMSDDQM.js";
31
+ } from "./chunk-KVQFMVXE.js";
32
32
  import {
33
33
  useMediaLibrary,
34
34
  useResolvedMedia
@@ -9,7 +9,7 @@ import {
9
9
  MediaBlock,
10
10
  containerGridClass,
11
11
  useEditableCollection
12
- } from "./chunk-RWMSDDQM.js";
12
+ } from "./chunk-KVQFMVXE.js";
13
13
  import {
14
14
  AddIcon,
15
15
  DeleteIcon,
@@ -124,7 +124,7 @@ function buildContent(colors, options) {
124
124
  ...label || columns || collapsing !== void 0 || showLabel !== void 0 ? { options: { label, columns, collapsing, showLabel } } : {}
125
125
  };
126
126
  }
127
- function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal }) {
127
+ function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal, onDelete }) {
128
128
  if (onChange && openModal) {
129
129
  return /* @__PURE__ */ jsx2(
130
130
  ColorsEditable,
@@ -135,7 +135,8 @@ function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onCh
135
135
  collapsing,
136
136
  showLabel,
137
137
  onChange,
138
- openModal
138
+ openModal,
139
+ onDelete
139
140
  }
140
141
  );
141
142
  }
@@ -184,7 +185,7 @@ function ColorsView({
184
185
  /* @__PURE__ */ jsx2("div", { className: "@container", children: /* @__PURE__ */ jsx2("div", { className: cn("grid gap-4", containerGridClass[columns] || containerGridClass[3]), children: colors.map((color, i) => {
185
186
  const hex = color.spaces[0]?.hex;
186
187
  const contrast = getContrastClass(hex);
187
- return /* @__PURE__ */ jsxs2("div", { className: "overflow-hidden rounded-outer border border-base-200", children: [
188
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col overflow-hidden rounded-outer border border-base-200", children: [
188
189
  /* @__PURE__ */ jsxs2("div", { className: cn("relative flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: [
189
190
  color.name && /* @__PURE__ */ jsx2("span", { className: "text-sm font-bold", children: color.name }),
190
191
  copiedIndex === i && /* @__PURE__ */ jsx2("span", { className: cn(
@@ -192,7 +193,8 @@ function ColorsView({
192
193
  getContrastClass(hex) === "text-black" ? "bg-black/15 text-black" : "bg-white/25 text-white"
193
194
  ), children: "Copied!" })
194
195
  ] }),
195
- expanded && /* @__PURE__ */ jsx2("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
196
+ expanded && // flex-1 so every swatch's label block grows to the tallest in its row.
197
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
196
198
  (space, j) => Object.entries(space).map(
197
199
  ([key, value]) => value ? /* @__PURE__ */ jsxs2("button", { onClick: () => handleCopy(value, i), className: "cursor-pointer flex w-full justify-between hover:text-primary", children: [
198
200
  /* @__PURE__ */ jsx2("span", { className: "font-medium uppercase", children: key }),
@@ -211,7 +213,8 @@ function ColorsEditable({
211
213
  collapsing,
212
214
  showLabel,
213
215
  onChange,
214
- openModal
216
+ openModal,
217
+ onDelete
215
218
  }) {
216
219
  const [expanded, setExpanded] = useState2(!collapsing);
217
220
  const options = { label, columns, collapsing, showLabel };
@@ -257,6 +260,8 @@ function ColorsEditable({
257
260
  {
258
261
  items: colors,
259
262
  columns,
263
+ group: "colors",
264
+ onEmptied: onDelete,
260
265
  onChange: (newColors) => onChange(buildContent(newColors, options)),
261
266
  createItem: createColorItem,
262
267
  isEditMode: true,
@@ -290,7 +295,7 @@ function ColorSwatchEditable({
290
295
  const newColors = colors.map((c, i) => i === index ? updated : c);
291
296
  onChange(buildContent(newColors, options));
292
297
  };
293
- return /* @__PURE__ */ jsxs2("div", { className: "overflow-hidden rounded-outer border border-base-200", children: [
298
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-full flex-col overflow-hidden rounded-outer border border-base-200", children: [
294
299
  /* @__PURE__ */ jsx2("div", { className: cn("flex min-h-[80px] items-end p-3", contrast), style: { backgroundColor: hex || "#ccc" }, children: /* @__PURE__ */ jsx2(
295
300
  EditablePlainText,
296
301
  {
@@ -302,7 +307,8 @@ function ColorSwatchEditable({
302
307
  className: "text-sm font-bold"
303
308
  }
304
309
  ) }),
305
- expanded && /* @__PURE__ */ jsx2("div", { className: "space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
310
+ expanded && // flex-1 so every swatch's label block grows to the tallest in its row.
311
+ /* @__PURE__ */ jsx2("div", { className: "flex-1 space-y-1 bg-base-accent p-3 text-sm", children: color.spaces.map(
306
312
  (space, j) => Object.entries(space).map(
307
313
  ([key, value]) => value ? /* @__PURE__ */ jsxs2("div", { className: "flex w-full justify-between", children: [
308
314
  /* @__PURE__ */ jsx2("span", { className: "font-medium uppercase", children: key }),
@@ -346,7 +352,7 @@ var Colors_default = defineSection({
346
352
  icon: /* @__PURE__ */ jsx3(Palette, { size: 18 }),
347
353
  schema,
348
354
  authoringNote: "A brand color palette. Each color may carry hex/rgb/cmyk/pantone values. Use to document brand colors with swatches.",
349
- component: ({ content, options, onChange, openModal }) => /* @__PURE__ */ jsx3(
355
+ component: ({ content, options, onChange, openModal, onDelete }) => /* @__PURE__ */ jsx3(
350
356
  Colors,
351
357
  {
352
358
  colors: content.content.colors,
@@ -355,7 +361,8 @@ var Colors_default = defineSection({
355
361
  collapsing: options?.collapsing,
356
362
  showLabel: options?.showLabel,
357
363
  onChange: onChange ? (c) => onChange(c) : void 0,
358
- openModal
364
+ openModal,
365
+ onDelete
359
366
  }
360
367
  ),
361
368
  defaults: () => ({
@@ -319,7 +319,7 @@ function MediaBlock({ refValue, options, link, isEditMode, onRefChange }) {
319
319
  }
320
320
 
321
321
  // src/components/primitives/EditableGrid.tsx
322
- import { useRef as useRef4, useEffect as useEffect4, useState as useState5 } from "react";
322
+ import { useRef as useRef4, useEffect as useEffect4, useState as useState5, useId } from "react";
323
323
  import { ImageIcon as ImageIcon2 } from "lucide-react";
324
324
 
325
325
  // src/lib/container-grid.ts
@@ -411,6 +411,17 @@ function useEditableCollection({
411
411
  },
412
412
  [createItem, onChange]
413
413
  );
414
+ const onInsertItem = useCallback3(
415
+ (index, data) => {
416
+ setWrappedItems((prev) => {
417
+ const next = [...prev];
418
+ next.splice(index, 0, { id: uid(), data });
419
+ onChange(next.map((w) => w.data));
420
+ return next;
421
+ });
422
+ },
423
+ [onChange]
424
+ );
414
425
  const onRemove = useCallback3(
415
426
  (id) => {
416
427
  setWrappedItems((prev) => {
@@ -421,7 +432,19 @@ function useEditableCollection({
421
432
  },
422
433
  [onChange]
423
434
  );
424
- return { wrappedItems, onReorder, onAdd, onInsert, onRemove };
435
+ return { wrappedItems, onReorder, onAdd, onInsert, onInsertItem, onRemove };
436
+ }
437
+
438
+ // src/components/primitives/gridDrop.ts
439
+ function resolveDropIndex({
440
+ fromIndex,
441
+ targetIndex,
442
+ edge,
443
+ sameCollection
444
+ }) {
445
+ let toIndex = targetIndex + (edge === "right" ? 1 : 0);
446
+ if (sameCollection && fromIndex < toIndex) toIndex--;
447
+ return toIndex;
425
448
  }
426
449
 
427
450
  // src/components/primitives/EditableGrid.tsx
@@ -436,26 +459,57 @@ function EditableGrid({
436
459
  onItemSettings,
437
460
  onItemImageClick,
438
461
  chromeTopClass,
439
- className
462
+ className,
463
+ group,
464
+ onEmptied
440
465
  }) {
441
- const { wrappedItems, onReorder, onAdd, onInsert, onRemove } = useEditableCollection({
466
+ const gridId = useId();
467
+ const { wrappedItems, onReorder, onAdd, onInsert, onInsertItem, onRemove } = useEditableCollection({
442
468
  items,
443
469
  onChange,
444
470
  createItem
445
471
  });
446
472
  const [dragState, setDragState] = useState5({ sourceId: null, closestEdge: null });
447
473
  const [isDragging, setIsDragging] = useState5(false);
474
+ const cfgRef = useRef4({ gridId, group, itemCount: wrappedItems.length, onReorder, onRemove, onInsertItem, onEmptied });
475
+ cfgRef.current = { gridId, group, itemCount: wrappedItems.length, onReorder, onRemove, onInsertItem, onEmptied };
448
476
  useEffect4(() => {
449
477
  let cleanup;
450
478
  let cancelled = false;
451
- import("./adapter-HH47ZPGM.js").then(({ monitorForElements }) => {
479
+ Promise.all([
480
+ import("./adapter-HH47ZPGM.js"),
481
+ import("./closest-edge-EBOXL3YW.js")
482
+ ]).then(([{ monitorForElements }, { extractClosestEdge }]) => {
452
483
  if (cancelled) return;
453
484
  cleanup = monitorForElements({
454
485
  onDragStart: ({ source }) => {
455
486
  if (source.data.dragType === "grid-cell") setIsDragging(true);
456
487
  },
457
- onDrop: ({ source }) => {
458
- if (source.data.dragType === "grid-cell") setIsDragging(false);
488
+ onDrop: ({ source, location }) => {
489
+ if (source.data.dragType !== "grid-cell") return;
490
+ setIsDragging(false);
491
+ setDragState({ sourceId: null, closestEdge: null });
492
+ const { gridId: my, group: myGroup, itemCount, onReorder: onReorder2, onRemove: onRemove2, onInsertItem: onInsertItem2, onEmptied: onEmptied2 } = cfgRef.current;
493
+ const srcGridId = source.data.gridId;
494
+ const target = location.current.dropTargets[0];
495
+ const dstGridId = target?.data.gridId;
496
+ if (srcGridId === my && dstGridId === my && target) {
497
+ const fromIndex = source.data.index;
498
+ const edge = extractClosestEdge(target.data);
499
+ const toIndex = resolveDropIndex({ fromIndex, targetIndex: target.data.index, edge, sameCollection: true });
500
+ if (fromIndex !== toIndex) onReorder2(fromIndex, toIndex);
501
+ return;
502
+ }
503
+ const crossAllowed = myGroup != null && source.data.group === myGroup;
504
+ if (!crossAllowed) return;
505
+ if (srcGridId === my && dstGridId && dstGridId !== my) {
506
+ if (itemCount <= 1 && onEmptied2) onEmptied2();
507
+ else onRemove2(source.data.id);
508
+ } else if (dstGridId === my && srcGridId !== my && target) {
509
+ const edge = extractClosestEdge(target.data);
510
+ const toIndex = resolveDropIndex({ fromIndex: source.data.index, targetIndex: target.data.index, edge, sameCollection: false });
511
+ onInsertItem2(toIndex, source.data.item);
512
+ }
459
513
  }
460
514
  });
461
515
  });
@@ -473,9 +527,11 @@ function EditableGrid({
473
527
  {
474
528
  id: wrapped.id,
475
529
  index,
530
+ gridId,
531
+ group,
532
+ item: wrapped.data,
476
533
  isLast: index === wrappedItems.length - 1,
477
534
  isDragging,
478
- onReorder,
479
535
  onRemove,
480
536
  onInsert,
481
537
  onSettings: onItemSettings ? () => onItemSettings(index) : void 0,
@@ -503,9 +559,11 @@ function EditableGrid({
503
559
  function GridCell({
504
560
  id,
505
561
  index,
562
+ gridId,
563
+ group,
564
+ item,
506
565
  isLast,
507
566
  isDragging,
508
- onReorder,
509
567
  onRemove,
510
568
  onInsert,
511
569
  onSettings,
@@ -530,15 +588,18 @@ function GridCell({
530
588
  if (cancelled) return;
531
589
  const cleanupDraggable = draggable({
532
590
  element: handle,
533
- getInitialData: () => ({ dragType: "grid-cell", id, index }),
591
+ // `item` rides along so a cross-grid destination can insert it verbatim.
592
+ getInitialData: () => ({ dragType: "grid-cell", gridId, group, id, index, item }),
534
593
  onDragStart: () => setDragState({ sourceId: id, closestEdge: null }),
535
594
  onDrop: () => setDragState({ sourceId: null, closestEdge: null })
536
595
  });
537
596
  const cleanupDropTarget = dropTargetForElements({
538
597
  element: cell,
539
- canDrop: ({ source }) => source.data.dragType === "grid-cell",
598
+ // Accept a reorder within this grid, or a transfer from another grid in
599
+ // the same group. The actual move is performed by the grid-level monitor.
600
+ canDrop: ({ source }) => source.data.dragType === "grid-cell" && (source.data.gridId === gridId || group != null && source.data.group === group),
540
601
  getData: ({ input, element }) => attachClosestEdge(
541
- { id, index },
602
+ { gridId, id, index },
542
603
  { input, element, allowedEdges: ["left", "right"] }
543
604
  ),
544
605
  onDragEnter: ({ self }) => {
@@ -551,16 +612,6 @@ function GridCell({
551
612
  },
552
613
  onDragLeave: () => {
553
614
  setDragState((prev) => prev.closestEdge?.id === id ? { ...prev, closestEdge: null } : prev);
554
- },
555
- onDrop: ({ source, self }) => {
556
- const fromIndex = source.data.index;
557
- const edge = extractClosestEdge(self.data);
558
- let toIndex = index;
559
- if (edge === "right") toIndex = index + 1;
560
- if (fromIndex < toIndex) toIndex--;
561
- if (fromIndex !== toIndex) {
562
- onReorder(fromIndex, toIndex);
563
- }
564
615
  }
565
616
  });
566
617
  cleanup = () => {
@@ -572,7 +623,7 @@ function GridCell({
572
623
  cancelled = true;
573
624
  cleanup?.();
574
625
  };
575
- }, [id, index, onReorder, setDragState]);
626
+ }, [id, index, gridId, group, item, setDragState]);
576
627
  const isCellDragging = dragState.sourceId === id;
577
628
  const showLeftEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "left" && dragState.sourceId !== id;
578
629
  const showRightEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "right" && dragState.sourceId !== id;
@@ -581,7 +632,9 @@ function GridCell({
581
632
  {
582
633
  ref: cellRef,
583
634
  className: cn(
584
- "group/cell relative",
635
+ // h-full so a full-height renderItem (e.g. a colors swatch) fills the
636
+ // stretched grid track and can equalize height across the row.
637
+ "group/cell relative h-full",
585
638
  isCellDragging && "opacity-50"
586
639
  ),
587
640
  children: [
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  EditablePlainText
3
- } from "./chunk-RWMSDDQM.js";
3
+ } from "./chunk-KVQFMVXE.js";
4
4
  import {
5
5
  Button,
6
6
  usePages
@@ -9,7 +9,8 @@ interface Props {
9
9
  showLabel?: boolean;
10
10
  onChange?: (content: SectionContent) => void;
11
11
  openModal?: (title: string, content: ReactNode) => void;
12
+ onDelete?: () => void;
12
13
  }
13
- export default function Colors({ colors, columns, label, collapsing, showLabel, onChange, openModal }: Props): import("react/jsx-runtime").JSX.Element;
14
+ export default function Colors({ colors, columns, label, collapsing, showLabel, onChange, openModal, onDelete }: Props): import("react/jsx-runtime").JSX.Element;
14
15
  export {};
15
16
  //# sourceMappingURL=Colors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Colors.d.ts","sourceRoot":"","sources":["../../../src/components/brandguide/Colors.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,UAAU,KAAK;IACb,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;CACzD;AAgCD,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,MAAM,EAAE,OAAW,EAAE,KAAK,EAAE,UAAU,EAAE,SAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,KAAK,2CAgBtH"}
1
+ {"version":3,"file":"Colors.d.ts","sourceRoot":"","sources":["../../../src/components/brandguide/Colors.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,UAAU,KAAK;IACb,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;IAC7C,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,KAAK,IAAI,CAAC;IACxD,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;CACvB;AAgCD,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,EAAE,MAAM,EAAE,OAAW,EAAE,KAAK,EAAE,UAAU,EAAE,SAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAiBhI"}
@@ -13,7 +13,17 @@ interface EditableGridProps<T> {
13
13
  onItemImageClick?: (index: number) => void;
14
14
  chromeTopClass?: string;
15
15
  className?: string;
16
+ /**
17
+ * Items can be dragged BETWEEN two EditableGrids that share the same non-null
18
+ * `group` (e.g. two colors sections). Without a group, a grid only reorders
19
+ * its own items. Only pass the same group to grids whose item shape matches —
20
+ * a cross-grid drop inserts the source item verbatim into the destination.
21
+ */
22
+ group?: string;
23
+ /** Called instead of removing the last item when it is dragged out to another
24
+ * grid — lets the owner tear down a now-empty container (e.g. delete the section). */
25
+ onEmptied?: () => void;
16
26
  }
17
- export declare function EditableGrid<T>({ items, columns, onChange, createItem, isEditMode, renderItem, onItemSettings, onItemImageClick, chromeTopClass, className, }: EditableGridProps<T>): import("react/jsx-runtime").JSX.Element;
27
+ export declare function EditableGrid<T>({ items, columns, onChange, createItem, isEditMode, renderItem, onItemSettings, onItemImageClick, chromeTopClass, className, group, onEmptied, }: EditableGridProps<T>): import("react/jsx-runtime").JSX.Element;
18
28
  export {};
19
29
  //# sourceMappingURL=EditableGrid.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditableGrid.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/EditableGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAQpE,UAAU,iBAAiB,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC;IAClF,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAC9B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,SAAS,GACV,EAAE,iBAAiB,CAAC,CAAC,CAAC,2CAqFtB"}
1
+ {"version":3,"file":"EditableGrid.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/EditableGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAsC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAS3E,UAAU,iBAAiB,CAAC,CAAC;IAC3B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAC;IAClF,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;2FACuF;IACvF,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAC9B,KAAK,EACL,OAAO,EACP,QAAQ,EACR,UAAU,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,SAAS,EACT,KAAK,EACL,SAAS,GACV,EAAE,iBAAiB,CAAC,CAAC,CAAC,2CAqItB"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Convert a drop target (the cell being dropped onto + the closest edge) into
3
+ * the destination index for the moved item.
4
+ *
5
+ * Same-collection reorder: the dragged item is spliced OUT of the array before
6
+ * being re-inserted, so a destination past its original position shifts down by
7
+ * one — hence the `fromIndex < toIndex` decrement.
8
+ *
9
+ * Cross-collection insert: nothing is removed from the destination array, so no
10
+ * shift adjustment applies.
11
+ */
12
+ export declare function resolveDropIndex({ fromIndex, targetIndex, edge, sameCollection, }: {
13
+ fromIndex: number;
14
+ targetIndex: number;
15
+ edge: "left" | "right" | null;
16
+ sameCollection: boolean;
17
+ }): number;
18
+ //# sourceMappingURL=gridDrop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gridDrop.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/gridDrop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,WAAW,EACX,IAAI,EACJ,cAAc,GACf,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC9B,cAAc,EAAE,OAAO,CAAC;CACzB,GAAG,MAAM,CAIT"}
@@ -2,7 +2,7 @@ import {
2
2
  EditableRichText,
3
3
  LinkPopover,
4
4
  defineHeadingSection
5
- } from "../../chunk-DR22HM44.js";
5
+ } from "../../chunk-VI26O4E3.js";
6
6
  import {
7
7
  EditableGrid,
8
8
  EditablePlainText,
@@ -10,7 +10,7 @@ import {
10
10
  MediaBlock,
11
11
  ResolvedMedia,
12
12
  useEditableCollection
13
- } from "../../chunk-RWMSDDQM.js";
13
+ } from "../../chunk-KVQFMVXE.js";
14
14
  import "../../chunk-ZSPCE6MZ.js";
15
15
  import {
16
16
  DeleteIcon,
@@ -12,6 +12,7 @@ interface UseEditableCollectionReturn<T> {
12
12
  onReorder: (fromIndex: number, toIndex: number) => void;
13
13
  onAdd: () => void;
14
14
  onInsert: (index: number) => void;
15
+ onInsertItem: (index: number, data: T) => void;
15
16
  onRemove: (id: string) => void;
16
17
  }
17
18
  export declare function useEditableCollection<T>({ items, onChange, createItem, }: UseEditableCollectionOptions<T>): UseEditableCollectionReturn<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"useEditableCollection.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/useEditableCollection.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW,CAAC,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;CACT;AAED,UAAU,4BAA4B,CAAC,CAAC;IACtC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,CAAC;CACrB;AAED,UAAU,2BAA2B,CAAC,CAAC;IACrC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/B,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAaD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,EACvC,KAAK,EACL,QAAQ,EACR,UAAU,GACX,EAAE,4BAA4B,CAAC,CAAC,CAAC,GAAG,2BAA2B,CAAC,CAAC,CAAC,CAmElE"}
1
+ {"version":3,"file":"useEditableCollection.d.ts","sourceRoot":"","sources":["../../../src/components/primitives/useEditableCollection.ts"],"names":[],"mappings":"AAEA,UAAU,WAAW,CAAC,CAAC;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,CAAC,CAAC;CACT;AAED,UAAU,4BAA4B,CAAC,CAAC;IACtC,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC,CAAC;CACrB;AAED,UAAU,2BAA2B,CAAC,CAAC;IACrC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IAC/B,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;IAC/C,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAaD,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,EACvC,KAAK,EACL,QAAQ,EACR,UAAU,GACX,EAAE,4BAA4B,CAAC,CAAC,CAAC,GAAG,2BAA2B,CAAC,CAAC,CAAC,CAiFlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/sections/Colors/index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,wBAqCG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/sections/Colors/index.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBA,wBAsCG"}
@@ -2,9 +2,9 @@ import {
2
2
  ColorItemSchema,
3
3
  ColorSpaceSchema,
4
4
  brandGuideSectionDefs
5
- } from "../../chunk-4XXLH5CL.js";
5
+ } from "../../chunk-7XWER6YR.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
- import "../../chunk-RWMSDDQM.js";
7
+ import "../../chunk-KVQFMVXE.js";
8
8
  import "../../chunk-EMGLZQCM.js";
9
9
  import "../../chunk-3LNKOLZP.js";
10
10
  import "../../chunk-PZJL2S3Z.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  allSectionDefs
3
- } from "../../chunk-FRTJNS5F.js";
4
- import "../../chunk-4XXLH5CL.js";
3
+ } from "../../chunk-4TVODHD2.js";
4
+ import "../../chunk-7XWER6YR.js";
5
5
  import "../../chunk-2DDWWTRX.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
- import "../../chunk-DR22HM44.js";
8
- import "../../chunk-RWMSDDQM.js";
7
+ import "../../chunk-VI26O4E3.js";
8
+ import "../../chunk-KVQFMVXE.js";
9
9
  import "../../chunk-ZSPCE6MZ.js";
10
10
  import "../../chunk-EMGLZQCM.js";
11
11
  import "../../chunk-Y4JQGO6G.js";
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  allSectionDefs
3
- } from "../../chunk-FRTJNS5F.js";
4
- import "../../chunk-4XXLH5CL.js";
3
+ } from "../../chunk-4TVODHD2.js";
4
+ import "../../chunk-7XWER6YR.js";
5
5
  import "../../chunk-2DDWWTRX.js";
6
6
  import "../../chunk-5THS4K5D.js";
7
- import "../../chunk-DR22HM44.js";
8
- import "../../chunk-RWMSDDQM.js";
7
+ import "../../chunk-VI26O4E3.js";
8
+ import "../../chunk-KVQFMVXE.js";
9
9
  import "../../chunk-ZSPCE6MZ.js";
10
10
  import "../../chunk-EMGLZQCM.js";
11
11
  import "../../chunk-Y4JQGO6G.js";
@@ -13,10 +13,12 @@ interface EditorContextValue {
13
13
  showAllChrome: boolean;
14
14
  viewBranch: "saved" | "live";
15
15
  historyState: HistoryState;
16
+ historyPageId: string | null;
16
17
  toggleEditMode: () => void;
17
18
  toggleShowAllChrome: () => void;
18
19
  setViewBranch: (branch: "saved" | "live") => void;
19
20
  setHistoryState: (state: HistoryState) => void;
21
+ setHistoryPageId: (pageId: string | null) => void;
20
22
  }
21
23
  interface EditorProviderProps {
22
24
  children: ReactNode;
@@ -1 +1 @@
1
- {"version":3,"file":"EditorContext.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB,GAAG,IAAI,CAAC;AAET,UAAU,kBAAkB;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;CAChD;AAID,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CAyB/D;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,CAMrD"}
1
+ {"version":3,"file":"EditorContext.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAoD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEzF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;CACxB,GAAG,IAAI,CAAC;AAET,UAAU,kBAAkB;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,YAAY,EAAE,YAAY,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,mBAAmB,EAAE,MAAM,IAAI,CAAC;IAChC,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,KAAK,IAAI,CAAC;IAClD,eAAe,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAC;IAC/C,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;CACnD;AAID,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CA4B/D;AAED,wBAAgB,gBAAgB,IAAI,kBAAkB,CAMrD"}
@@ -1,4 +1,5 @@
1
1
  import type { Audience } from "../../auth/types";
2
+ import { type HistoryState } from "./EditorContext";
2
3
  import type { CollabBootstrap } from "../../collab/types";
3
4
  export { useMediaLibrary } from "./MediaLibraryContext";
4
5
  interface Props {
@@ -22,4 +23,16 @@ interface Props {
22
23
  collabBootstrap?: CollabBootstrap | null;
23
24
  }
24
25
  export default function EditorShell({ headSha, draftHeadSha, siteId, audiences: initialAudiences, capabilities, currentUser, chatbotPlatformEnabled, collabBootstrap, }: Props): import("react/jsx-runtime").JSX.Element;
26
+ /** Resolve which historical page to show: explicit selection → the page the
27
+ * editor is currently on (if it existed at that version) → home → first. */
28
+ export declare function resolveHistoryPage(historyState: NonNullable<HistoryState>, historyPageId: string | null, preferredPageId: string | null): {
29
+ id: string;
30
+ title: string;
31
+ slug: string;
32
+ isHome: boolean;
33
+ showInNav: boolean;
34
+ status: "live" | "archived";
35
+ access: string[];
36
+ order: string[];
37
+ };
25
38
  //# sourceMappingURL=EditorShell.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAkDjD,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AAmCnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAoBxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA68CP"}
1
+ {"version":3,"file":"EditorShell.d.ts","sourceRoot":"","sources":["../../../src/components/shell/EditorShell.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAKjD,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,iBAAiB,CAAC;AA6CtF,OAAO,KAAK,EAAE,eAAe,EAA2D,MAAM,oBAAoB,CAAC;AAmCnH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAoBxD,UAAU,KAAK;IACb,OAAO,EAAE,MAAM,CAAC;IAIhB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,EAAE,CAAC;IACtB,YAAY,EAAE;QACZ,KAAK,EAAE,OAAO,CAAC;QACf,aAAa,EAAE,OAAO,CAAC;QACvB,YAAY,EAAE,OAAO,CAAC;QACtB,cAAc,EAAE,OAAO,CAAC;QACxB,kBAAkB,EAAE,OAAO,CAAC;QAC5B,cAAc,EAAE,OAAO,CAAC;KACzB,CAAC;IACF,WAAW,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;KAAE,GAAG,IAAI,CAAC;IAIhE,sBAAsB,CAAC,EAAE,OAAO,CAAC;IAKjC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;CAC1C;AAED,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,OAAO,EACP,YAAY,EACZ,MAAM,EACN,SAAS,EAAE,gBAAgB,EAC3B,YAAY,EACZ,WAAW,EACX,sBAA8B,EAC9B,eAAe,GAChB,EAAE,KAAK,2CA88CP;AA2DD;6EAC6E;AAC7E,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,WAAW,CAAC,YAAY,CAAC,EACvC,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI;;;;;;;;;EAK/B"}
@@ -1,8 +1,15 @@
1
+ interface HistoryPageOption {
2
+ id: string;
3
+ title: string;
4
+ }
1
5
  interface HistoryToolbarProps {
2
6
  date: string;
7
+ pages: HistoryPageOption[];
8
+ activePageId: string;
9
+ onPageChange: (pageId: string) => void;
3
10
  onBackToCurrent: () => void;
4
11
  onRestore: () => void;
5
12
  }
6
- export declare function HistoryToolbar({ date, onBackToCurrent, onRestore }: HistoryToolbarProps): import("react/jsx-runtime").JSX.Element;
13
+ export declare function HistoryToolbar({ date, pages, activePageId, onPageChange, onBackToCurrent, onRestore }: HistoryToolbarProps): import("react/jsx-runtime").JSX.Element;
7
14
  export {};
8
15
  //# sourceMappingURL=HistoryToolbar.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"HistoryToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/shell/HistoryToolbar.tsx"],"names":[],"mappings":"AAGA,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAsBvF"}
1
+ {"version":3,"file":"HistoryToolbar.d.ts","sourceRoot":"","sources":["../../../src/components/shell/HistoryToolbar.tsx"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAoC1H"}
@@ -72,6 +72,9 @@ export interface SectionProps<T = unknown> {
72
72
  onChange?: (content: T) => void;
73
73
  isEditMode: boolean;
74
74
  openModal?: (title: string, content: ReactNode) => void;
75
+ /** Delete this whole section. Used e.g. when a colors section's last swatch
76
+ * is dragged into another section, leaving it empty. */
77
+ onDelete?: () => void;
75
78
  }
76
79
  export interface WrapperProps {
77
80
  sectionId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drawnagency/primitives",
3
- "version": "0.1.79",
3
+ "version": "0.1.80",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  "./package.json": "./package.json",
@@ -17,6 +17,7 @@ interface Props {
17
17
  showLabel?: boolean;
18
18
  onChange?: (content: SectionContent) => void;
19
19
  openModal?: (title: string, content: ReactNode) => void;
20
+ onDelete?: () => void;
20
21
  }
21
22
 
22
23
  function getContrastClass(hex: string | undefined): string {
@@ -49,7 +50,7 @@ function buildContent(
49
50
  };
50
51
  }
51
52
 
52
- export default function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal }: Props) {
53
+ export default function Colors({ colors, columns = 3, label, collapsing, showLabel = true, onChange, openModal, onDelete }: Props) {
53
54
  if (onChange && openModal) {
54
55
  return (
55
56
  <ColorsEditable
@@ -60,6 +61,7 @@ export default function Colors({ colors, columns = 3, label, collapsing, showLab
60
61
  showLabel={showLabel}
61
62
  onChange={onChange}
62
63
  openModal={openModal}
64
+ onDelete={onDelete}
63
65
  />
64
66
  );
65
67
  }
@@ -125,7 +127,7 @@ function ColorsView({
125
127
  const hex = color.spaces[0]?.hex;
126
128
  const contrast = getContrastClass(hex);
127
129
  return (
128
- <div key={i} className="overflow-hidden rounded-outer border border-base-200">
130
+ <div key={i} className="flex h-full flex-col overflow-hidden rounded-outer border border-base-200">
129
131
  <div className={cn("relative flex min-h-[80px] items-end p-3", contrast)} style={{ backgroundColor: hex || "#ccc" }}>
130
132
  {color.name && <span className="text-sm font-bold">{color.name}</span>}
131
133
  {copiedIndex === i && (
@@ -138,7 +140,8 @@ function ColorsView({
138
140
  )}
139
141
  </div>
140
142
  {expanded && (
141
- <div className="space-y-1 bg-base-accent p-3 text-sm">
143
+ // flex-1 so every swatch's label block grows to the tallest in its row.
144
+ <div className="flex-1 space-y-1 bg-base-accent p-3 text-sm">
142
145
  {color.spaces.map((space, j) =>
143
146
  Object.entries(space).map(([key, value]) =>
144
147
  value ? (
@@ -168,6 +171,7 @@ function ColorsEditable({
168
171
  showLabel,
169
172
  onChange,
170
173
  openModal,
174
+ onDelete,
171
175
  }: {
172
176
  colors: ColorItem[];
173
177
  columns: number;
@@ -176,6 +180,7 @@ function ColorsEditable({
176
180
  showLabel?: boolean;
177
181
  onChange: (content: SectionContent) => void;
178
182
  openModal: (title: string, content: ReactNode) => void;
183
+ onDelete?: () => void;
179
184
  }) {
180
185
  const [expanded, setExpanded] = useState(!collapsing);
181
186
  const options = { label, columns, collapsing, showLabel };
@@ -224,6 +229,10 @@ function ColorsEditable({
224
229
  <EditableGrid
225
230
  items={colors}
226
231
  columns={columns}
232
+ // Shared group lets a swatch be dragged from one colors section into another.
233
+ group="colors"
234
+ // When the last swatch is dragged out to another section, delete this now-empty one.
235
+ onEmptied={onDelete}
227
236
  onChange={(newColors) => onChange(buildContent(newColors, options))}
228
237
  createItem={createColorItem}
229
238
  isEditMode={true}
@@ -267,7 +276,7 @@ function ColorSwatchEditable({
267
276
  };
268
277
 
269
278
  return (
270
- <div className="overflow-hidden rounded-outer border border-base-200">
279
+ <div className="flex h-full flex-col overflow-hidden rounded-outer border border-base-200">
271
280
  <div className={cn("flex min-h-[80px] items-end p-3", contrast)} style={{ backgroundColor: hex || "#ccc" }}>
272
281
  <EditablePlainText
273
282
  tag="span"
@@ -279,7 +288,8 @@ function ColorSwatchEditable({
279
288
  />
280
289
  </div>
281
290
  {expanded && (
282
- <div className="space-y-1 bg-base-accent p-3 text-sm">
291
+ // flex-1 so every swatch's label block grows to the tallest in its row.
292
+ <div className="flex-1 space-y-1 bg-base-accent p-3 text-sm">
283
293
  {color.spaces.map((space, j) =>
284
294
  Object.entries(space).map(([key, value]) =>
285
295
  value ? (
@@ -1,10 +1,11 @@
1
- import { useRef, useEffect, useState, type ReactNode } from "react";
1
+ import { useRef, useEffect, useState, useId, type ReactNode } from "react";
2
2
  import { ImageIcon } from "lucide-react";
3
3
  import { AddIcon, DragHandle, DeleteIcon, SettingsIcon } from "../shared/icons";
4
4
  import { IconButton } from "../shared/IconButton";
5
5
  import { cn } from "../../lib/cn";
6
6
  import { containerGridClass } from "../../lib/container-grid";
7
7
  import { useEditableCollection } from "./useEditableCollection";
8
+ import { resolveDropIndex } from "./gridDrop";
8
9
 
9
10
  interface EditableGridProps<T> {
10
11
  items: T[];
@@ -17,6 +18,16 @@ interface EditableGridProps<T> {
17
18
  onItemImageClick?: (index: number) => void;
18
19
  chromeTopClass?: string;
19
20
  className?: string;
21
+ /**
22
+ * Items can be dragged BETWEEN two EditableGrids that share the same non-null
23
+ * `group` (e.g. two colors sections). Without a group, a grid only reorders
24
+ * its own items. Only pass the same group to grids whose item shape matches —
25
+ * a cross-grid drop inserts the source item verbatim into the destination.
26
+ */
27
+ group?: string;
28
+ /** Called instead of removing the last item when it is dragged out to another
29
+ * grid — lets the owner tear down a now-empty container (e.g. delete the section). */
30
+ onEmptied?: () => void;
20
31
  }
21
32
 
22
33
  export function EditableGrid<T>({
@@ -30,8 +41,11 @@ export function EditableGrid<T>({
30
41
  onItemImageClick,
31
42
  chromeTopClass,
32
43
  className,
44
+ group,
45
+ onEmptied,
33
46
  }: EditableGridProps<T>) {
34
- const { wrappedItems, onReorder, onAdd, onInsert, onRemove } = useEditableCollection({
47
+ const gridId = useId();
48
+ const { wrappedItems, onReorder, onAdd, onInsert, onInsertItem, onRemove } = useEditableCollection({
35
49
  items,
36
50
  onChange,
37
51
  createItem,
@@ -44,18 +58,63 @@ export function EditableGrid<T>({
44
58
 
45
59
  const [isDragging, setIsDragging] = useState(false);
46
60
 
61
+ // The drop transaction is centralized here (not in each GridCell) because this
62
+ // monitor is GLOBAL: it fires for every grid-cell drop on the page, so the
63
+ // source grid and destination grid each see the same drop and act on their own
64
+ // collection — source removes, destination inserts, same-grid reorders. Keep
65
+ // the latest handlers/config in a ref so the monitor can be set up once.
66
+ const cfgRef = useRef({ gridId, group, itemCount: wrappedItems.length, onReorder, onRemove, onInsertItem, onEmptied });
67
+ cfgRef.current = { gridId, group, itemCount: wrappedItems.length, onReorder, onRemove, onInsertItem, onEmptied };
68
+
47
69
  useEffect(() => {
48
70
  let cleanup: (() => void) | undefined;
49
71
  let cancelled = false;
50
72
 
51
- import("@atlaskit/pragmatic-drag-and-drop/element/adapter").then(({ monitorForElements }) => {
73
+ Promise.all([
74
+ import("@atlaskit/pragmatic-drag-and-drop/element/adapter"),
75
+ import("@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge"),
76
+ ]).then(([{ monitorForElements }, { extractClosestEdge }]) => {
52
77
  if (cancelled) return;
53
78
  cleanup = monitorForElements({
54
79
  onDragStart: ({ source }) => {
55
80
  if (source.data.dragType === "grid-cell") setIsDragging(true);
56
81
  },
57
- onDrop: ({ source }) => {
58
- if (source.data.dragType === "grid-cell") setIsDragging(false);
82
+ onDrop: ({ source, location }) => {
83
+ if (source.data.dragType !== "grid-cell") return;
84
+ setIsDragging(false);
85
+ // Every grid clears its own indicator on any drop — this is what stops
86
+ // the destination grid's drop line from sticking after a cross-grid drop.
87
+ setDragState({ sourceId: null, closestEdge: null });
88
+
89
+ const { gridId: my, group: myGroup, itemCount, onReorder, onRemove, onInsertItem, onEmptied } = cfgRef.current;
90
+ const srcGridId = source.data.gridId as string;
91
+ const target = location.current.dropTargets[0];
92
+ const dstGridId = target?.data.gridId as string | undefined;
93
+
94
+ // Same-grid reorder — only the owning grid acts on it.
95
+ if (srcGridId === my && dstGridId === my && target) {
96
+ const fromIndex = source.data.index as number;
97
+ const edge = extractClosestEdge(target.data) as "left" | "right" | null;
98
+ const toIndex = resolveDropIndex({ fromIndex, targetIndex: target.data.index as number, edge, sameCollection: true });
99
+ if (fromIndex !== toIndex) onReorder(fromIndex, toIndex);
100
+ return;
101
+ }
102
+
103
+ // Cross-grid transfer requires a shared group. The source grid removes
104
+ // its item; the destination grid inserts it. Two grids, two updates.
105
+ const crossAllowed = myGroup != null && source.data.group === myGroup;
106
+ if (!crossAllowed) return;
107
+
108
+ if (srcGridId === my && dstGridId && dstGridId !== my) {
109
+ // Dragging the last item out empties this grid — let the owner tear
110
+ // down the now-empty container instead of leaving it behind.
111
+ if (itemCount <= 1 && onEmptied) onEmptied();
112
+ else onRemove(source.data.id as string);
113
+ } else if (dstGridId === my && srcGridId !== my && target) {
114
+ const edge = extractClosestEdge(target.data) as "left" | "right" | null;
115
+ const toIndex = resolveDropIndex({ fromIndex: source.data.index as number, targetIndex: target.data.index as number, edge, sameCollection: false });
116
+ onInsertItem(toIndex, source.data.item as T);
117
+ }
59
118
  },
60
119
  });
61
120
  });
@@ -88,9 +147,11 @@ export function EditableGrid<T>({
88
147
  key={wrapped.id}
89
148
  id={wrapped.id}
90
149
  index={index}
150
+ gridId={gridId}
151
+ group={group}
152
+ item={wrapped.data}
91
153
  isLast={index === wrappedItems.length - 1}
92
154
  isDragging={isDragging}
93
- onReorder={onReorder}
94
155
  onRemove={onRemove}
95
156
  onInsert={onInsert}
96
157
  onSettings={onItemSettings ? () => onItemSettings(index) : undefined}
@@ -117,12 +178,14 @@ export function EditableGrid<T>({
117
178
  );
118
179
  }
119
180
 
120
- function GridCell({
181
+ function GridCell<T>({
121
182
  id,
122
183
  index,
184
+ gridId,
185
+ group,
186
+ item,
123
187
  isLast,
124
188
  isDragging,
125
- onReorder,
126
189
  onRemove,
127
190
  onInsert,
128
191
  onSettings,
@@ -134,9 +197,11 @@ function GridCell({
134
197
  }: {
135
198
  id: string;
136
199
  index: number;
200
+ gridId: string;
201
+ group?: string;
202
+ item: T;
137
203
  isLast: boolean;
138
204
  isDragging: boolean;
139
- onReorder: (from: number, to: number) => void;
140
205
  onRemove: (id: string) => void;
141
206
  onInsert: (index: number) => void;
142
207
  onSettings?: () => void;
@@ -165,17 +230,22 @@ function GridCell({
165
230
 
166
231
  const cleanupDraggable = draggable({
167
232
  element: handle,
168
- getInitialData: () => ({ dragType: "grid-cell", id, index }),
233
+ // `item` rides along so a cross-grid destination can insert it verbatim.
234
+ getInitialData: () => ({ dragType: "grid-cell", gridId, group, id, index, item }),
169
235
  onDragStart: () => setDragState({ sourceId: id, closestEdge: null }),
170
236
  onDrop: () => setDragState({ sourceId: null, closestEdge: null }),
171
237
  });
172
238
 
173
239
  const cleanupDropTarget = dropTargetForElements({
174
240
  element: cell,
175
- canDrop: ({ source }) => source.data.dragType === "grid-cell",
241
+ // Accept a reorder within this grid, or a transfer from another grid in
242
+ // the same group. The actual move is performed by the grid-level monitor.
243
+ canDrop: ({ source }) =>
244
+ source.data.dragType === "grid-cell" &&
245
+ (source.data.gridId === gridId || (group != null && source.data.group === group)),
176
246
  getData: ({ input, element }) =>
177
247
  attachClosestEdge(
178
- { id, index },
248
+ { gridId, id, index },
179
249
  { input, element, allowedEdges: ["left", "right"] },
180
250
  ),
181
251
  onDragEnter: ({ self }) => {
@@ -189,16 +259,6 @@ function GridCell({
189
259
  onDragLeave: () => {
190
260
  setDragState((prev) => prev.closestEdge?.id === id ? { ...prev, closestEdge: null } : prev);
191
261
  },
192
- onDrop: ({ source, self }) => {
193
- const fromIndex = source.data.index as number;
194
- const edge = extractClosestEdge(self.data);
195
- let toIndex = index;
196
- if (edge === "right") toIndex = index + 1;
197
- if (fromIndex < toIndex) toIndex--;
198
- if (fromIndex !== toIndex) {
199
- onReorder(fromIndex, toIndex);
200
- }
201
- },
202
262
  });
203
263
 
204
264
  cleanup = () => {
@@ -211,7 +271,7 @@ function GridCell({
211
271
  cancelled = true;
212
272
  cleanup?.();
213
273
  };
214
- }, [id, index, onReorder, setDragState]);
274
+ }, [id, index, gridId, group, item, setDragState]);
215
275
 
216
276
  const isCellDragging = dragState.sourceId === id;
217
277
  const showLeftEdge = dragState.closestEdge?.id === id && dragState.closestEdge.edge === "left" && dragState.sourceId !== id;
@@ -221,7 +281,9 @@ function GridCell({
221
281
  <div
222
282
  ref={cellRef}
223
283
  className={cn(
224
- "group/cell relative",
284
+ // h-full so a full-height renderItem (e.g. a colors swatch) fills the
285
+ // stretched grid track and can equalize height across the row.
286
+ "group/cell relative h-full",
225
287
  isCellDragging && "opacity-50",
226
288
  )}
227
289
  >
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Convert a drop target (the cell being dropped onto + the closest edge) into
3
+ * the destination index for the moved item.
4
+ *
5
+ * Same-collection reorder: the dragged item is spliced OUT of the array before
6
+ * being re-inserted, so a destination past its original position shifts down by
7
+ * one — hence the `fromIndex < toIndex` decrement.
8
+ *
9
+ * Cross-collection insert: nothing is removed from the destination array, so no
10
+ * shift adjustment applies.
11
+ */
12
+ export function resolveDropIndex({
13
+ fromIndex,
14
+ targetIndex,
15
+ edge,
16
+ sameCollection,
17
+ }: {
18
+ fromIndex: number;
19
+ targetIndex: number;
20
+ edge: "left" | "right" | null;
21
+ sameCollection: boolean;
22
+ }): number {
23
+ let toIndex = targetIndex + (edge === "right" ? 1 : 0);
24
+ if (sameCollection && fromIndex < toIndex) toIndex--;
25
+ return toIndex;
26
+ }
@@ -16,6 +16,7 @@ interface UseEditableCollectionReturn<T> {
16
16
  onReorder: (fromIndex: number, toIndex: number) => void;
17
17
  onAdd: () => void;
18
18
  onInsert: (index: number) => void;
19
+ onInsertItem: (index: number, data: T) => void;
19
20
  onRemove: (id: string) => void;
20
21
  }
21
22
 
@@ -89,6 +90,20 @@ export function useEditableCollection<T>({
89
90
  [createItem, onChange],
90
91
  );
91
92
 
93
+ // Insert an externally-provided item (e.g. one dragged in from another grid)
94
+ // at a specific index. Distinct from onInsert, which creates a blank item.
95
+ const onInsertItem = useCallback(
96
+ (index: number, data: T) => {
97
+ setWrappedItems((prev) => {
98
+ const next = [...prev];
99
+ next.splice(index, 0, { id: uid(), data });
100
+ onChange(next.map((w) => w.data));
101
+ return next;
102
+ });
103
+ },
104
+ [onChange]
105
+ );
106
+
92
107
  const onRemove = useCallback(
93
108
  (id: string) => {
94
109
  setWrappedItems((prev) => {
@@ -100,5 +115,5 @@ export function useEditableCollection<T>({
100
115
  [onChange]
101
116
  );
102
117
 
103
- return { wrappedItems, onReorder, onAdd, onInsert, onRemove };
118
+ return { wrappedItems, onReorder, onAdd, onInsert, onInsertItem, onRemove };
104
119
  }
@@ -30,7 +30,7 @@ export default defineSection({
30
30
  schema,
31
31
  authoringNote:
32
32
  "A brand color palette. Each color may carry hex/rgb/cmyk/pantone values. Use to document brand colors with swatches.",
33
- component: ({ content, options, onChange, openModal }) => (
33
+ component: ({ content, options, onChange, openModal, onDelete }) => (
34
34
  <Colors
35
35
  colors={content.content.colors}
36
36
  columns={options?.columns as number}
@@ -39,6 +39,7 @@ export default defineSection({
39
39
  showLabel={options?.showLabel as boolean}
40
40
  onChange={onChange ? (c) => onChange(c as typeof content) : undefined}
41
41
  openModal={openModal}
42
+ onDelete={onDelete}
42
43
  />
43
44
  ),
44
45
  defaults: () => ({
@@ -16,10 +16,12 @@ interface EditorContextValue {
16
16
  showAllChrome: boolean;
17
17
  viewBranch: "saved" | "live";
18
18
  historyState: HistoryState;
19
+ historyPageId: string | null;
19
20
  toggleEditMode: () => void;
20
21
  toggleShowAllChrome: () => void;
21
22
  setViewBranch: (branch: "saved" | "live") => void;
22
23
  setHistoryState: (state: HistoryState) => void;
24
+ setHistoryPageId: (pageId: string | null) => void;
23
25
  }
24
26
 
25
27
  const EditorContext = createContext<EditorContextValue | null>(null);
@@ -33,7 +35,10 @@ export function EditorProvider({ children }: EditorProviderProps) {
33
35
  const [showAllChrome, setShowAllChrome] = useState(false);
34
36
  const [viewBranch, setViewBranchState] = useState<"saved" | "live">("saved");
35
37
  const [historyState, setHistoryStateRaw] = useState<HistoryState>(null);
38
+ const [historyPageId, setHistoryPageId] = useState<string | null>(null);
36
39
  const setHistoryState = useCallback((state: HistoryState) => {
40
+ // null = "auto": resolved to the current page (or home) at render time
41
+ setHistoryPageId(null);
37
42
  setHistoryStateRaw(state);
38
43
  }, []);
39
44
 
@@ -49,7 +54,7 @@ export function EditorProvider({ children }: EditorProviderProps) {
49
54
  const setViewBranch = useCallback((b: "saved" | "live") => setViewBranchState(b), []);
50
55
 
51
56
  return (
52
- <EditorContext.Provider value={{ isEditMode, showAllChrome, viewBranch, historyState, toggleEditMode, toggleShowAllChrome, setViewBranch, setHistoryState }}>
57
+ <EditorContext.Provider value={{ isEditMode, showAllChrome, viewBranch, historyState, historyPageId, toggleEditMode, toggleShowAllChrome, setViewBranch, setHistoryState, setHistoryPageId }}>
53
58
  {children}
54
59
  </EditorContext.Provider>
55
60
  );
@@ -12,7 +12,7 @@ import type { MediaManifest } from "../../media/types";
12
12
  import type { QueueItem } from "../../media/queue";
13
13
  import { SiteConfigSchema, IndexSchema } from "../../schemas/site-config";
14
14
  import { ensureSanitizer } from "../../lib/sanitize";
15
- import { EditorProvider, useEditorContext } from "./EditorContext";
15
+ import { EditorProvider, useEditorContext, type HistoryState } from "./EditorContext";
16
16
  import { makeBlockMoveDispatch } from "./blockMoveDispatch";
17
17
  import { ROOT_CONTAINER_ID, resolveDropIndex } from "../../lib/block-dnd";
18
18
  import type { BlockMove } from "../../lib/block-move";
@@ -1454,6 +1454,7 @@ export default function EditorShell({
1454
1454
  onSectionChange={onSectionChange}
1455
1455
  onAddSection={onAddSection}
1456
1456
  onDeleteSection={setPendingDeleteSectionId}
1457
+ onDeleteSectionImmediate={performDeleteSection}
1457
1458
  onReorderSections={onReorderSections}
1458
1459
  onBlockMove={onBlockMove}
1459
1460
  onMoveSection={siteIndex.pages.length > 1 ? setMovingSectionId : undefined}
@@ -1693,6 +1694,18 @@ function HistoryWatcher() {
1693
1694
  return null;
1694
1695
  }
1695
1696
 
1697
+ /** Resolve which historical page to show: explicit selection → the page the
1698
+ * editor is currently on (if it existed at that version) → home → first. */
1699
+ export function resolveHistoryPage(
1700
+ historyState: NonNullable<HistoryState>,
1701
+ historyPageId: string | null,
1702
+ preferredPageId: string | null,
1703
+ ) {
1704
+ const pages = historyState.index.pages;
1705
+ const byId = (id: string | null) => (id ? pages.find((p) => p.id === id) : undefined);
1706
+ return byId(historyPageId) ?? byId(preferredPageId) ?? pages.find((p) => p.isHome) ?? pages[0];
1707
+ }
1708
+
1696
1709
  function HistoryOrEditorContent({
1697
1710
  children,
1698
1711
  index,
@@ -1704,9 +1717,11 @@ function HistoryOrEditorContent({
1704
1717
  activeSections: LoadedSection[];
1705
1718
  activePageId: string | null;
1706
1719
  }) {
1707
- const { historyState } = useEditorContext();
1720
+ const { historyState, historyPageId, setHistoryPageId } = useEditorContext();
1708
1721
  const wasInHistory = useRef(false);
1709
1722
 
1723
+ const historyPage = historyState ? resolveHistoryPage(historyState, historyPageId, activePageId) : null;
1724
+
1710
1725
  useEffect(() => {
1711
1726
  if (historyState) {
1712
1727
  wasInHistory.current = true;
@@ -1720,8 +1735,22 @@ function HistoryOrEditorContent({
1720
1735
  }
1721
1736
  }, [historyState, index, activeSections, activePageId]);
1722
1737
 
1723
- if (historyState) {
1724
- return <ViewRenderer sections={historyState.sections} />;
1738
+ // Pin the auto-resolved page into context so the toolbar selector agrees,
1739
+ // and keep the nav highlighting in sync with the page being viewed.
1740
+ useEffect(() => {
1741
+ if (!historyState || !historyPage) return;
1742
+ if (!historyPageId) setHistoryPageId(historyPage.id);
1743
+ siteNavChangeEvent.dispatch(
1744
+ generateSiteNav({ index: historyState.index, activeSections: [], activePageId: historyPage.id, mode: "editor", audience: null }),
1745
+ );
1746
+ }, [historyState, historyPage?.id]);
1747
+
1748
+ if (historyState && historyPage) {
1749
+ const position = new Map(historyPage.order.map((id, i) => [id, i]));
1750
+ const pageSections = historyState.sections
1751
+ .filter((s) => position.has(s.section.id))
1752
+ .sort((a, b) => (position.get(a.section.id) ?? 0) - (position.get(b.section.id) ?? 0));
1753
+ return <ViewRenderer sections={pageSections} />;
1725
1754
  }
1726
1755
  return <>{children}</>;
1727
1756
  }
@@ -1764,6 +1793,7 @@ function EditorContent({
1764
1793
  onSectionChange,
1765
1794
  onAddSection,
1766
1795
  onDeleteSection,
1796
+ onDeleteSectionImmediate,
1767
1797
  onReorderSections,
1768
1798
  onBlockMove,
1769
1799
  onMoveSection,
@@ -1784,6 +1814,7 @@ function EditorContent({
1784
1814
  onSectionChange: (sectionId: string, content: SectionContent) => void;
1785
1815
  onAddSection: (insertIndex: number, selection: string) => void;
1786
1816
  onDeleteSection: (sectionId: string) => void;
1817
+ onDeleteSectionImmediate: (sectionId: string) => void;
1787
1818
  onReorderSections: (fromIndex: number, toIndex: number) => void;
1788
1819
  onBlockMove: (move: BlockMove) => void;
1789
1820
  onMoveSection?: (sectionId: string) => void;
@@ -1978,6 +2009,9 @@ function EditorContent({
1978
2009
  onChange={editingEnabled ? (newContent) => onSectionChange(section.id, newContent as SectionContent) : undefined}
1979
2010
  isEditMode={isEditMode}
1980
2011
  openModal={editingEnabled ? openModal : undefined}
2012
+ // Immediate (no confirm modal): used when a colors section's
2013
+ // last swatch is dragged out, auto-removing the empty section.
2014
+ onDelete={editingEnabled ? () => onDeleteSectionImmediate(section.id) : undefined}
1981
2015
  />
1982
2016
  </SectionWrapper>
1983
2017
  </SectionLayout>
@@ -2150,12 +2184,16 @@ function EditorToolbar({
2150
2184
  collabSelfId: string | null;
2151
2185
  collabStatus: CollabStatus;
2152
2186
  }) {
2153
- const { isEditMode, viewBranch, setViewBranch, toggleEditMode, historyState, setHistoryState } = useEditorContext();
2187
+ const { isEditMode, viewBranch, setViewBranch, toggleEditMode, historyState, setHistoryState, historyPageId, setHistoryPageId } = useEditorContext();
2154
2188
 
2155
2189
  if (historyState) {
2190
+ const page = resolveHistoryPage(historyState, historyPageId, null);
2156
2191
  return (
2157
2192
  <HistoryToolbar
2158
2193
  date={historyState.date}
2194
+ pages={historyState.index.pages.map((p) => ({ id: p.id, title: p.title }))}
2195
+ activePageId={page?.id ?? ""}
2196
+ onPageChange={setHistoryPageId}
2159
2197
  onBackToCurrent={() => setHistoryState(null)}
2160
2198
  onRestore={onRestoreClick}
2161
2199
  />
@@ -1,13 +1,21 @@
1
1
  import { formatDateTime } from "../../lib/timestamp";
2
2
  import { Button } from "../shared/Button";
3
3
 
4
+ interface HistoryPageOption {
5
+ id: string;
6
+ title: string;
7
+ }
8
+
4
9
  interface HistoryToolbarProps {
5
10
  date: string;
11
+ pages: HistoryPageOption[];
12
+ activePageId: string;
13
+ onPageChange: (pageId: string) => void;
6
14
  onBackToCurrent: () => void;
7
15
  onRestore: () => void;
8
16
  }
9
17
 
10
- export function HistoryToolbar({ date, onBackToCurrent, onRestore }: HistoryToolbarProps) {
18
+ export function HistoryToolbar({ date, pages, activePageId, onPageChange, onBackToCurrent, onRestore }: HistoryToolbarProps) {
11
19
  return (
12
20
  <div className="fixed top-0 right-0 left-0 z-50 border-b border-base-200 bg-base">
13
21
  <div className="mx-auto max-w-screen-xl grid grid-cols-3 items-center px-4 py-2">
@@ -16,10 +24,24 @@ export function HistoryToolbar({ date, onBackToCurrent, onRestore }: HistoryTool
16
24
  Back to current
17
25
  </Button>
18
26
  </div>
19
- <div className="flex items-center justify-center">
27
+ <div className="flex items-center justify-center gap-2">
20
28
  <span className="text-xs font-medium text-base-contrast-light">
21
29
  Viewing {formatDateTime(date)}
22
30
  </span>
31
+ {pages.length > 1 && (
32
+ <select
33
+ value={activePageId}
34
+ onChange={(e) => onPageChange(e.target.value)}
35
+ className="rounded border border-base-200 bg-base px-1.5 py-0.5 text-xs font-medium text-base-contrast"
36
+ aria-label="Page at this version"
37
+ >
38
+ {pages.map((p) => (
39
+ <option key={p.id} value={p.id}>
40
+ {p.title}
41
+ </option>
42
+ ))}
43
+ </select>
44
+ )}
23
45
  </div>
24
46
  <div className="flex items-center justify-end">
25
47
  <Button variant="primary" size="sm" onClick={onRestore}>
@@ -83,6 +83,9 @@ export interface SectionProps<T = unknown> {
83
83
  onChange?: (content: T) => void;
84
84
  isEditMode: boolean;
85
85
  openModal?: (title: string, content: ReactNode) => void;
86
+ /** Delete this whole section. Used e.g. when a colors section's last swatch
87
+ * is dragged into another section, leaving it empty. */
88
+ onDelete?: () => void;
86
89
  }
87
90
 
88
91
  export interface WrapperProps {