@starrykit/slides-editor 0.1.30 → 0.1.31

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,4 +1,5 @@
1
1
  import type { PdfExportSelection } from "@starrykit/slides-core";
2
+ import type { DeckSwitcherOption } from "../index";
2
3
  export interface PdfExportSlideOption {
3
4
  id: string;
4
5
  title: string;
@@ -6,7 +7,11 @@ export interface PdfExportSlideOption {
6
7
  }
7
8
  interface EditorHeaderProps {
8
9
  title: string;
10
+ decks?: DeckSwitcherOption[];
11
+ currentDeckId?: string | null;
9
12
  onTitleChange?: (t: string) => void;
13
+ onDeckSwitch?: (deckId: string) => void;
14
+ onDeckImport?: (files: FileList) => void;
10
15
  onPresent?: () => void;
11
16
  onExportPdf?: (selection: PdfExportSelection) => void;
12
17
  onExportHtml?: () => void;
@@ -14,6 +19,7 @@ interface EditorHeaderProps {
14
19
  pdfSlides?: PdfExportSlideOption[];
15
20
  pdfThumbnails?: Record<string, string>;
16
21
  isSaving: boolean;
22
+ isSwitchingDeck?: boolean;
17
23
  }
18
- export declare function EditorHeader({ title, onTitleChange, onPresent, onExportPdf, onExportHtml, onExportSourceFiles, pdfSlides, pdfThumbnails, isSaving, }: EditorHeaderProps): import("react/jsx-runtime").JSX.Element;
24
+ export declare function EditorHeader({ title, decks, currentDeckId, onTitleChange, onDeckSwitch, onDeckImport, onPresent, onExportPdf, onExportHtml, onExportSourceFiles, pdfSlides, pdfThumbnails, isSaving, isSwitchingDeck, }: EditorHeaderProps): import("react/jsx-runtime").JSX.Element;
19
25
  export {};
@@ -2,6 +2,7 @@ import type { EditableType, PdfExportSelection, SlideModel, StageRect } from "@s
2
2
  import type { MouseEvent as ReactMouseEvent, RefObject } from "react";
3
3
  import type { BlockManipulationOverlay as BlockManipulationOverlayModel } from "../hooks/block-manipulation-types";
4
4
  import type { ImageCropOverlay as ImageCropOverlayModel } from "../hooks/use-image-crop";
5
+ import type { DeckSwitcherOption } from "../index";
5
6
  import type { ResizeHandleCorner, ResizeHandlePosition } from "../lib/block-snap-types";
6
7
  import type { CssPropertyRow } from "../lib/collect-css-properties";
7
8
  import type { SelectionCommandAvailability } from "./floating-toolbar";
@@ -9,7 +10,10 @@ interface EditorWorkspaceProps {
9
10
  slides: SlideModel[];
10
11
  activeSlide: SlideModel;
11
12
  deckTitle: string;
13
+ decks?: DeckSwitcherOption[];
14
+ currentDeckId?: string | null;
12
15
  isSaving: boolean;
16
+ isSwitchingDeck: boolean;
13
17
  isPresenting: boolean;
14
18
  thumbnails: Record<string, string>;
15
19
  slideWidth: number;
@@ -42,6 +46,8 @@ interface EditorWorkspaceProps {
42
46
  isManipulating: boolean;
43
47
  isToolbarSuppressed: boolean;
44
48
  onDeckTitleChange?: (title: string) => void;
49
+ onDeckSwitch?: (deckId: string) => void;
50
+ onDeckImport?: (files: FileList) => void;
45
51
  onExportPdf?: (selection: PdfExportSelection) => void;
46
52
  onExportHtml?: () => void;
47
53
  onExportSourceFiles?: () => void;
@@ -84,5 +90,5 @@ interface EditorWorkspaceProps {
84
90
  onDuplicateElement: () => void;
85
91
  onDeleteElement: () => void;
86
92
  }
87
- declare function EditorWorkspace({ slides, activeSlide, deckTitle, isSaving, isPresenting, thumbnails, slideWidth, slideHeight, offsetX, offsetY, scale, preselectionOverlay, marqueeOverlay, selectionOverlay, toolbarKey, inspectedStyles, selectedElementType, selectionCommandAvailability, isSelectedElementLocked, groupScopeOverlayPassive, isEditingText, isCropMode, cropOverlay, manipulationOverlay, attributeValues, iframeRef, stageViewportRef, selectionOverlayRef, selectionContextMenuTriggerRef, isManipulating, isToolbarSuppressed, onDeckTitleChange, onExportPdf, onExportHtml, onExportSourceFiles, onPresent, onExitPresenting, onSelectSlide, onSidebarSlideFocusChange, onAddSlide, onAddSlideAbove, onAddSlideBelow, onDuplicateSlide, onDeleteSlide, onToggleSlideHidden, onRenameSlide, onReorderSlide, onSidebarFocusChange, onSelectionOverlayMouseDown, onSelectionOverlayMouseUp, onSelectionOverlayMouseMove, onSelectionOverlayContextMenu, onStageMouseLeave, onResizeHandleMouseDown, onCornerRotationZoneMouseDown, onCropHandleMouseDown, onSelectionOverlayDoubleClick, onBackgroundClick, onStyleChange, onStyleChanges, onStylePreview, onAttributeChange, onAlignToSlide, onCropImage, onDistribute, onGroup, onLayerOrder, onUngroup, onDuplicateElement, onDeleteElement, }: EditorWorkspaceProps): import("react/jsx-runtime").JSX.Element;
93
+ declare function EditorWorkspace({ slides, activeSlide, deckTitle, decks, currentDeckId, isSaving, isSwitchingDeck, isPresenting, thumbnails, slideWidth, slideHeight, offsetX, offsetY, scale, preselectionOverlay, marqueeOverlay, selectionOverlay, toolbarKey, inspectedStyles, selectedElementType, selectionCommandAvailability, isSelectedElementLocked, groupScopeOverlayPassive, isEditingText, isCropMode, cropOverlay, manipulationOverlay, attributeValues, iframeRef, stageViewportRef, selectionOverlayRef, selectionContextMenuTriggerRef, isManipulating, isToolbarSuppressed, onDeckTitleChange, onDeckSwitch, onDeckImport, onExportPdf, onExportHtml, onExportSourceFiles, onPresent, onExitPresenting, onSelectSlide, onSidebarSlideFocusChange, onAddSlide, onAddSlideAbove, onAddSlideBelow, onDuplicateSlide, onDeleteSlide, onToggleSlideHidden, onRenameSlide, onReorderSlide, onSidebarFocusChange, onSelectionOverlayMouseDown, onSelectionOverlayMouseUp, onSelectionOverlayMouseMove, onSelectionOverlayContextMenu, onStageMouseLeave, onResizeHandleMouseDown, onCornerRotationZoneMouseDown, onCropHandleMouseDown, onSelectionOverlayDoubleClick, onBackgroundClick, onStyleChange, onStyleChanges, onStylePreview, onAttributeChange, onAlignToSlide, onCropImage, onDistribute, onGroup, onLayerOrder, onUngroup, onDuplicateElement, onDeleteElement, }: EditorWorkspaceProps): import("react/jsx-runtime").JSX.Element;
88
94
  export { EditorWorkspace };
package/dist/index.d.ts CHANGED
@@ -2,13 +2,26 @@ import { type PdfExportSelection, type SlideModel } from "@starrykit/slides-core
2
2
  export interface SlidesEditorProps {
3
3
  slides: SlideModel[];
4
4
  deckTitle?: string;
5
+ decks?: DeckSwitcherOption[];
6
+ currentDeckId?: string | null;
5
7
  isSaving?: boolean;
8
+ isSwitchingDeck?: boolean;
6
9
  onSlidesChange?: (slides: SlideModel[]) => void;
7
10
  onDeckTitleChange?: (title: string) => void;
11
+ onDeckSwitch?: (deckId: string) => void;
12
+ onDeckImport?: (files: FileList) => void;
8
13
  onExportPdf?: (selection: PdfExportSelection) => void;
9
14
  onExportHtml?: () => void;
10
15
  onExportSourceFiles?: () => void;
11
16
  }
12
- declare function SlidesEditor({ slides: loadedSlides, deckTitle, isSaving, onSlidesChange, onDeckTitleChange, onExportPdf, onExportHtml, onExportSourceFiles, }: SlidesEditorProps): import("react/jsx-runtime").JSX.Element;
17
+ export interface DeckSwitcherOption {
18
+ id: string;
19
+ title: string;
20
+ description?: string;
21
+ directoryName: string;
22
+ relativePath: string;
23
+ isCurrent: boolean;
24
+ }
25
+ declare function SlidesEditor({ slides: loadedSlides, deckTitle, decks, currentDeckId, isSaving, isSwitchingDeck, onSlidesChange, onDeckTitleChange, onDeckSwitch, onDeckImport, onExportPdf, onExportHtml, onExportSourceFiles, }: SlidesEditorProps): import("react/jsx-runtime").JSX.Element;
13
26
  export { SlidesEditor };
14
27
  export * from "@starrykit/slides-core";
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  FileCheck2,
15
15
  FileCode2,
16
16
  FileText,
17
+ FolderInput,
17
18
  Layers3,
18
19
  Play,
19
20
  Presentation,
@@ -33,7 +34,7 @@ function cn(...inputs) {
33
34
  }
34
35
 
35
36
  // src/components/editor-header.tsx
36
- import { jsx, jsxs } from "react/jsx-runtime";
37
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
37
38
  var EXPORTS = [
38
39
  {
39
40
  id: "html",
@@ -60,23 +61,37 @@ var EXPORTS = [
60
61
  var GITHUB_REPO_URL = "https://github.com/StarryKit/starry-slides";
61
62
  function EditorHeader({
62
63
  title,
64
+ decks = [],
65
+ currentDeckId,
63
66
  onTitleChange,
67
+ onDeckSwitch,
68
+ onDeckImport,
64
69
  onPresent,
65
70
  onExportPdf,
66
71
  onExportHtml,
67
72
  onExportSourceFiles,
68
73
  pdfSlides = [],
69
74
  pdfThumbnails = {},
70
- isSaving
75
+ isSaving,
76
+ isSwitchingDeck = false
71
77
  }) {
72
- const [open, setOpen] = useState(false);
78
+ const [exportOpen, setExportOpen] = useState(false);
79
+ const [deckOpen, setDeckOpen] = useState(false);
73
80
  const [pdfDialogOpen, setPdfDialogOpen] = useState(false);
74
81
  const [titleWidth, setTitleWidth] = useState(0);
75
- const ref = useRef(null);
82
+ const exportRef = useRef(null);
83
+ const deckRef = useRef(null);
84
+ const deckImportInputRef = useRef(null);
76
85
  const titleDisplay = title || "Untitled presentation";
86
+ const canOpenDeckMenu = decks.length > 0 || Boolean(onDeckImport);
77
87
  useEffect(() => {
78
88
  const onDoc = (e) => {
79
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
89
+ if (exportRef.current && !exportRef.current.contains(e.target)) {
90
+ setExportOpen(false);
91
+ }
92
+ if (deckRef.current && !deckRef.current.contains(e.target)) {
93
+ setDeckOpen(false);
94
+ }
80
95
  };
81
96
  document.addEventListener("mousedown", onDoc);
82
97
  return () => document.removeEventListener("mousedown", onDoc);
@@ -89,7 +104,7 @@ function EditorHeader({
89
104
  setTitleWidth(Math.ceil(nextWidth));
90
105
  }, []);
91
106
  const handleExport = (e) => {
92
- setOpen(false);
107
+ setExportOpen(false);
93
108
  if (e.soon) {
94
109
  toast(`${e.label} is not available yet.`, {
95
110
  description: "We are still building this export option."
@@ -124,28 +139,134 @@ function EditorHeader({
124
139
  }
125
140
  ),
126
141
  /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-foreground/10" }),
127
- /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 flex-1", children: [
128
- /* @__PURE__ */ jsx(
129
- "input",
130
- {
131
- value: title,
132
- onChange: (e) => onTitleChange?.(e.target.value),
133
- "aria-label": "Deck title",
134
- className: "max-w-full min-w-0 flex-none rounded-md bg-transparent px-2 py-1 text-[18px] font-semibold text-foreground outline-none focus:bg-foreground/[0.04]",
135
- style: titleWidth ? { width: `${titleWidth}px` } : void 0,
136
- placeholder: "Untitled presentation"
137
- }
138
- ),
139
- /* @__PURE__ */ jsx(
140
- "span",
141
- {
142
- ref: measureTitleRef,
143
- "aria-hidden": "true",
144
- className: "pointer-events-none invisible absolute left-0 top-0 whitespace-pre rounded-md px-2 py-1 text-[18px] font-semibold",
145
- children: titleDisplay
146
- },
147
- titleDisplay
148
- )
142
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-1.5", children: [
143
+ /* @__PURE__ */ jsxs("div", { className: "relative min-w-0 shrink", children: [
144
+ /* @__PURE__ */ jsx(
145
+ "input",
146
+ {
147
+ value: title,
148
+ onChange: (e) => onTitleChange?.(e.target.value),
149
+ "aria-label": "Deck title",
150
+ className: "max-w-full min-w-0 flex-none rounded-md bg-transparent px-2 py-1 text-[18px] font-semibold text-foreground outline-none focus:bg-foreground/[0.04]",
151
+ style: titleWidth ? { width: `${titleWidth}px` } : void 0,
152
+ placeholder: "Untitled presentation"
153
+ }
154
+ ),
155
+ /* @__PURE__ */ jsx(
156
+ "span",
157
+ {
158
+ ref: measureTitleRef,
159
+ "aria-hidden": "true",
160
+ className: "pointer-events-none invisible absolute left-0 top-0 whitespace-pre rounded-md px-2 py-1 text-[18px] font-semibold",
161
+ children: titleDisplay
162
+ },
163
+ titleDisplay
164
+ )
165
+ ] }),
166
+ /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: deckRef, children: [
167
+ /* @__PURE__ */ jsx(
168
+ "button",
169
+ {
170
+ type: "button",
171
+ "aria-label": "Switch deck",
172
+ "aria-expanded": deckOpen,
173
+ "aria-haspopup": "menu",
174
+ disabled: !canOpenDeckMenu || isSwitchingDeck,
175
+ onClick: () => {
176
+ if (canOpenDeckMenu && !isSwitchingDeck) {
177
+ setDeckOpen((open) => !open);
178
+ }
179
+ },
180
+ className: "flex h-7 w-7 items-center justify-center rounded-md text-foreground/45 transition-colors hover:bg-foreground/[0.04] hover:text-foreground disabled:cursor-not-allowed disabled:opacity-35",
181
+ children: /* @__PURE__ */ jsx(
182
+ ChevronDown,
183
+ {
184
+ className: cn("h-3.5 w-3.5 transition-transform", deckOpen && "rotate-180")
185
+ }
186
+ )
187
+ }
188
+ ),
189
+ /* @__PURE__ */ jsx(
190
+ "input",
191
+ {
192
+ ref: deckImportInputRef,
193
+ type: "file",
194
+ multiple: true,
195
+ "aria-label": "Import deck folder",
196
+ className: "hidden",
197
+ ...{ webkitdirectory: "", directory: "" },
198
+ onChange: (event) => {
199
+ const files = event.currentTarget.files;
200
+ if (files?.length) {
201
+ onDeckImport?.(files);
202
+ }
203
+ event.currentTarget.value = "";
204
+ }
205
+ }
206
+ ),
207
+ deckOpen && /* @__PURE__ */ jsxs(
208
+ "div",
209
+ {
210
+ role: "menu",
211
+ "aria-label": "Local decks",
212
+ className: "absolute left-0 z-50 mt-1.5 w-[300px] rounded-lg border border-foreground/[0.08] bg-white p-1.5 shadow-[0_4px_20px_rgba(0,0,0,0.06),0_12px_40px_rgba(0,0,0,0.08)] animate-fade-in",
213
+ children: [
214
+ /* @__PURE__ */ jsx("div", { className: "px-2.5 py-1.5 text-[10px] font-medium uppercase tracking-wider text-foreground/40", children: "Local decks" }),
215
+ decks.map((deck) => {
216
+ const current = deck.isCurrent || deck.id === currentDeckId;
217
+ return /* @__PURE__ */ jsxs(
218
+ "button",
219
+ {
220
+ type: "button",
221
+ role: "menuitemradio",
222
+ "aria-checked": current,
223
+ disabled: current || isSwitchingDeck,
224
+ onClick: () => {
225
+ setDeckOpen(false);
226
+ onDeckSwitch?.(deck.id);
227
+ },
228
+ className: cn(
229
+ "flex w-full items-start gap-2.5 rounded-md px-2.5 py-2 text-left transition-colors",
230
+ current ? "bg-foreground/[0.045] text-foreground" : "text-foreground/72 hover:bg-foreground/[0.04] hover:text-foreground",
231
+ isSwitchingDeck && "cursor-wait opacity-60"
232
+ ),
233
+ children: [
234
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center", children: current ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }) : null }),
235
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
236
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] font-medium", children: deck.title }),
237
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate text-[11px] text-foreground/45", children: deck.relativePath === "." ? deck.directoryName : deck.relativePath })
238
+ ] })
239
+ ]
240
+ },
241
+ deck.id
242
+ );
243
+ }),
244
+ onDeckImport ? /* @__PURE__ */ jsxs(Fragment, { children: [
245
+ /* @__PURE__ */ jsx("div", { className: "my-1 h-px bg-foreground/[0.08]" }),
246
+ /* @__PURE__ */ jsxs(
247
+ "button",
248
+ {
249
+ type: "button",
250
+ role: "menuitem",
251
+ onClick: () => {
252
+ setDeckOpen(false);
253
+ deckImportInputRef.current?.click();
254
+ },
255
+ className: "flex w-full items-start gap-2.5 rounded-md px-2.5 py-2 text-left text-foreground/72 transition-colors hover:bg-foreground/[0.04] hover:text-foreground",
256
+ children: [
257
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center", children: /* @__PURE__ */ jsx(FolderInput, { className: "h-3.5 w-3.5" }) }),
258
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
259
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-[13px] font-medium", children: "Import deck..." }),
260
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 block truncate text-[11px] text-foreground/45", children: "Choose a folder with manifest.json" })
261
+ ] })
262
+ ]
263
+ }
264
+ )
265
+ ] }) : null
266
+ ]
267
+ }
268
+ )
269
+ ] })
149
270
  ] }),
150
271
  isSaving ? /* @__PURE__ */ jsx(
151
272
  "span",
@@ -157,21 +278,26 @@ function EditorHeader({
157
278
  ) : null
158
279
  ] }),
159
280
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
160
- /* @__PURE__ */ jsxs("div", { className: "relative", ref, children: [
281
+ /* @__PURE__ */ jsxs("div", { className: "relative", ref: exportRef, children: [
161
282
  /* @__PURE__ */ jsxs(
162
283
  "button",
163
284
  {
164
285
  type: "button",
165
- onClick: () => setOpen((o) => !o),
286
+ onClick: () => setExportOpen((o) => !o),
166
287
  className: "h-8 px-3 rounded-md flex items-center gap-1.5 text-[13px] text-foreground/70 hover:bg-foreground/[0.04] hover:text-foreground transition-colors",
167
288
  children: [
168
289
  /* @__PURE__ */ jsx(Download, { className: "w-3.5 h-3.5" }),
169
290
  /* @__PURE__ */ jsx("span", { children: "Export" }),
170
- /* @__PURE__ */ jsx(ChevronDown, { className: cn("w-3 h-3 transition-transform", open && "rotate-180") })
291
+ /* @__PURE__ */ jsx(
292
+ ChevronDown,
293
+ {
294
+ className: cn("w-3 h-3 transition-transform", exportOpen && "rotate-180")
295
+ }
296
+ )
171
297
  ]
172
298
  }
173
299
  ),
174
- open && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 mt-1.5 w-[280px] bg-white rounded-xl border border-foreground/[0.08] shadow-[0_4px_20px_rgba(0,0,0,0.06),0_12px_40px_rgba(0,0,0,0.08)] p-1.5 animate-fade-in z-50", children: [
300
+ exportOpen && /* @__PURE__ */ jsxs("div", { className: "absolute right-0 mt-1.5 w-[280px] bg-white rounded-xl border border-foreground/[0.08] shadow-[0_4px_20px_rgba(0,0,0,0.06),0_12px_40px_rgba(0,0,0,0.08)] p-1.5 animate-fade-in z-50", children: [
175
301
  /* @__PURE__ */ jsx("div", { className: "text-[10px] uppercase tracking-wider text-foreground/40 px-2.5 py-1.5 font-medium", children: "Export formats" }),
176
302
  EXPORTS.map((e) => {
177
303
  const Icon = e.icon;
@@ -1060,7 +1186,7 @@ function Input({ className, type, ...props }) {
1060
1186
  }
1061
1187
 
1062
1188
  // src/components/slide-sidebar.tsx
1063
- import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1189
+ import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
1064
1190
  function SlideSidebar({
1065
1191
  slides,
1066
1192
  activeSlideId,
@@ -1132,7 +1258,7 @@ function SlideSidebar({
1132
1258
  onRename?.(renameDialogSlideId, renameDraft);
1133
1259
  cancelRename();
1134
1260
  }, [cancelRename, onRename, renameDialogSlideId, renameDraft]);
1135
- return /* @__PURE__ */ jsxs4(Fragment, { children: [
1261
+ return /* @__PURE__ */ jsxs4(Fragment2, { children: [
1136
1262
  /* @__PURE__ */ jsx6(
1137
1263
  "aside",
1138
1264
  {
@@ -1482,7 +1608,7 @@ function rangesOverlapOrNear(startA, endA, startB, endB, proximity) {
1482
1608
  }
1483
1609
 
1484
1610
  // src/components/block-manipulation-overlay.tsx
1485
- import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1611
+ import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
1486
1612
  function makeRotationCursor(svgBody) {
1487
1613
  return encodeURIComponent(
1488
1614
  `<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><g fill="none">${svgBody}</g></svg>`
@@ -1515,7 +1641,7 @@ function BlockManipulationOverlay({
1515
1641
  onCornerRotationZoneMouseDown
1516
1642
  }) {
1517
1643
  const handleClassName = "absolute z-[5] size-[13px] -translate-x-1/2 -translate-y-1/2 rounded-full border border-white bg-foreground shadow-[0_2px_8px_rgba(0,0,0,0.16)] transition-colors before:absolute before:inset-[3px] before:rounded-full before:bg-white/90 hover:bg-foreground/80";
1518
- return /* @__PURE__ */ jsxs5(Fragment2, { children: [
1644
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1519
1645
  snapGuides.map((guide, index) => {
1520
1646
  const lineWidth = guide.variant === "spacing" ? "2px" : "1px";
1521
1647
  const capLength = 14;
@@ -1558,7 +1684,7 @@ function BlockManipulationOverlay({
1558
1684
  "data-testid": `snap-guide-${guide.orientation}`,
1559
1685
  "data-variant": guide.variant,
1560
1686
  style: lineStyle,
1561
- children: guide.variant === "spacing" ? /* @__PURE__ */ jsxs5(Fragment2, { children: [
1687
+ children: guide.variant === "spacing" ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
1562
1688
  /* @__PURE__ */ jsx7(
1563
1689
  "span",
1564
1690
  {
@@ -3820,7 +3946,7 @@ import {
3820
3946
  useRef as useRef7,
3821
3947
  useState as useState8
3822
3948
  } from "react";
3823
- import { Fragment as Fragment3, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
3949
+ import { Fragment as Fragment4, jsx as jsx18, jsxs as jsxs11 } from "react/jsx-runtime";
3824
3950
  function ColorPopover({
3825
3951
  activePopoverId,
3826
3952
  commitFeature,
@@ -3956,10 +4082,10 @@ function OptionsPopover({
3956
4082
  onPointerDown: () => {
3957
4083
  skipNextPreviewClearRef.current = true;
3958
4084
  },
3959
- children: showOptionPreviewAfterLabel ? /* @__PURE__ */ jsxs11(Fragment3, { children: [
4085
+ children: showOptionPreviewAfterLabel ? /* @__PURE__ */ jsxs11(Fragment4, { children: [
3960
4086
  /* @__PURE__ */ jsx18("span", { className: "min-w-0 truncate", children: option.label }),
3961
4087
  /* @__PURE__ */ jsx18(OptionSwatch, { feature, option })
3962
- ] }) : /* @__PURE__ */ jsxs11(Fragment3, { children: [
4088
+ ] }) : /* @__PURE__ */ jsxs11(Fragment4, { children: [
3963
4089
  Icon ? /* @__PURE__ */ jsx18(ToolbarIcon, { icon: Icon }) : /* @__PURE__ */ jsx18(OptionSwatch, { feature, option }),
3964
4090
  /* @__PURE__ */ jsx18("span", { className: "truncate", children: option.label })
3965
4091
  ] })
@@ -4186,7 +4312,7 @@ var LINE_HEIGHT_SLIDER_STEP = 0.01;
4186
4312
  var LINE_HEIGHT_SLIDER_DEFAULT = 1.2;
4187
4313
 
4188
4314
  // src/components/floating-toolbar-sections.tsx
4189
- import { Fragment as Fragment4, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
4315
+ import { Fragment as Fragment5, jsx as jsx19, jsxs as jsxs12 } from "react/jsx-runtime";
4190
4316
  var BORDER_RANGE_DEBOUNCE_MS = 160;
4191
4317
  function FloatingToolbarSections({
4192
4318
  activePopoverId,
@@ -4203,7 +4329,7 @@ function FloatingToolbarSections({
4203
4329
  setActivePopoverId
4204
4330
  }) {
4205
4331
  const isImageSelection = selectedElementType === "image";
4206
- return /* @__PURE__ */ jsxs12(Fragment4, { children: [
4332
+ return /* @__PURE__ */ jsxs12(Fragment5, { children: [
4207
4333
  /* @__PURE__ */ jsx19(
4208
4334
  LockSection,
4209
4335
  {
@@ -4214,9 +4340,9 @@ function FloatingToolbarSections({
4214
4340
  showGroupTool
4215
4341
  }
4216
4342
  ),
4217
- isSelectedElementLocked ? null : /* @__PURE__ */ jsxs12(Fragment4, { children: [
4343
+ isSelectedElementLocked ? null : /* @__PURE__ */ jsxs12(Fragment5, { children: [
4218
4344
  /* @__PURE__ */ jsx19(Divider, {}),
4219
- isImageSelection ? /* @__PURE__ */ jsx19(ImageSection, { commitFeature, getFeature: getFeature2 }) : /* @__PURE__ */ jsxs12(Fragment4, { children: [
4345
+ isImageSelection ? /* @__PURE__ */ jsx19(ImageSection, { commitFeature, getFeature: getFeature2 }) : /* @__PURE__ */ jsxs12(Fragment5, { children: [
4220
4346
  /* @__PURE__ */ jsx19(
4221
4347
  FontSection,
4222
4348
  {
@@ -4263,7 +4389,7 @@ function FloatingToolbarSections({
4263
4389
  }
4264
4390
  )
4265
4391
  ] }),
4266
- isImageSelection ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
4392
+ isImageSelection ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
4267
4393
  /* @__PURE__ */ jsx19(Divider, {}),
4268
4394
  /* @__PURE__ */ jsx19(
4269
4395
  BorderSection,
@@ -4278,7 +4404,7 @@ function FloatingToolbarSections({
4278
4404
  }
4279
4405
  )
4280
4406
  ] }) : null,
4281
- showMultiTools ? /* @__PURE__ */ jsxs12(Fragment4, { children: [
4407
+ showMultiTools ? /* @__PURE__ */ jsxs12(Fragment5, { children: [
4282
4408
  /* @__PURE__ */ jsx19(Divider, {}),
4283
4409
  /* @__PURE__ */ jsx19(
4284
4410
  MultiArrangeSection,
@@ -5242,7 +5368,7 @@ function getFeature(featureId) {
5242
5368
  }
5243
5369
 
5244
5370
  // src/components/image-crop-overlay.tsx
5245
- import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
5371
+ import { Fragment as Fragment6, jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
5246
5372
  var CROP_CURSOR_PATHS = {
5247
5373
  "top-left": "M12 22V12H22",
5248
5374
  "top-right": "M2 12h10v10",
@@ -5252,7 +5378,7 @@ var CROP_CURSOR_PATHS = {
5252
5378
  function ImageCropOverlay({ overlay, onCropHandleMouseDown }) {
5253
5379
  const handleClassName = "absolute z-[7] size-8 -translate-x-1/2 -translate-y-1/2 rounded-sm bg-transparent text-white drop-shadow-[0_1px_2px_rgba(0,0,0,0.7)]";
5254
5380
  const maskRegions = createMaskRegions(overlay.selectionBounds, overlay.cropBounds);
5255
- return /* @__PURE__ */ jsxs14(Fragment5, { children: [
5381
+ return /* @__PURE__ */ jsxs14(Fragment6, { children: [
5256
5382
  /* @__PURE__ */ jsx21("div", { className: "pointer-events-none absolute inset-0 z-[5]", "data-testid": "image-crop-mask", children: maskRegions.map((region) => /* @__PURE__ */ jsx21(
5257
5383
  "div",
5258
5384
  {
@@ -5648,7 +5774,10 @@ function EditorWorkspace({
5648
5774
  slides,
5649
5775
  activeSlide,
5650
5776
  deckTitle,
5777
+ decks,
5778
+ currentDeckId,
5651
5779
  isSaving,
5780
+ isSwitchingDeck,
5652
5781
  isPresenting,
5653
5782
  thumbnails,
5654
5783
  slideWidth,
@@ -5677,6 +5806,8 @@ function EditorWorkspace({
5677
5806
  isManipulating,
5678
5807
  isToolbarSuppressed,
5679
5808
  onDeckTitleChange,
5809
+ onDeckSwitch,
5810
+ onDeckImport,
5680
5811
  onExportPdf,
5681
5812
  onExportHtml,
5682
5813
  onExportSourceFiles,
@@ -5727,6 +5858,11 @@ function EditorWorkspace({
5727
5858
  {
5728
5859
  title: deckTitle,
5729
5860
  onTitleChange: onDeckTitleChange,
5861
+ decks,
5862
+ currentDeckId,
5863
+ isSwitchingDeck,
5864
+ onDeckSwitch,
5865
+ onDeckImport,
5730
5866
  isSaving,
5731
5867
  onPresent,
5732
5868
  onExportHtml,
@@ -11206,9 +11342,14 @@ var EMPTY_LOCKED_ELEMENT_IDS = [];
11206
11342
  function SlidesEditor({
11207
11343
  slides: loadedSlides,
11208
11344
  deckTitle,
11345
+ decks,
11346
+ currentDeckId,
11209
11347
  isSaving = false,
11348
+ isSwitchingDeck = false,
11210
11349
  onSlidesChange,
11211
11350
  onDeckTitleChange,
11351
+ onDeckSwitch,
11352
+ onDeckImport,
11212
11353
  onExportPdf,
11213
11354
  onExportHtml,
11214
11355
  onExportSourceFiles
@@ -11471,7 +11612,10 @@ function SlidesEditor({
11471
11612
  slides,
11472
11613
  activeSlide,
11473
11614
  deckTitle: resolvedDeckTitle,
11615
+ decks,
11616
+ currentDeckId,
11474
11617
  isSaving,
11618
+ isSwitchingDeck,
11475
11619
  isPresenting,
11476
11620
  thumbnails,
11477
11621
  slideWidth,
@@ -11499,6 +11643,8 @@ function SlidesEditor({
11499
11643
  isManipulating: isManipulating || isMarqueeSelecting,
11500
11644
  isToolbarSuppressed,
11501
11645
  onDeckTitleChange,
11646
+ onDeckSwitch,
11647
+ onDeckImport,
11502
11648
  onExportHtml,
11503
11649
  onExportSourceFiles,
11504
11650
  onExportPdf,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starrykit/slides-editor",
3
- "version": "0.1.30",
3
+ "version": "0.1.31",
4
4
  "description": "Embeddable React editor UI for Starry Slides — host-driven through props and callbacks.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/StarryKit/starry-slides#readme",
@@ -30,7 +30,7 @@
30
30
  "build": "tsc -p tsconfig.json && tsup src/index.tsx --format esm --external react --external react-dom"
31
31
  },
32
32
  "dependencies": {
33
- "@starrykit/slides-core": "0.1.30",
33
+ "@starrykit/slides-core": "0.1.31",
34
34
  "class-variance-authority": "^0.7.1",
35
35
  "clsx": "^2.1.1",
36
36
  "html-to-image": "^1.11.11",