@textcortex/slidewise 1.0.1 → 1.2.0

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 (53) hide show
  1. package/dist/index.mjs +8085 -7881
  2. package/dist/index.mjs.map +1 -1
  3. package/dist/slidewise.css +1 -1
  4. package/package.json +4 -19
  5. package/src/SlidewiseEditor.css +121 -4
  6. package/src/SlidewiseEditor.tsx +93 -165
  7. package/src/SlidewiseFileEditor.tsx +109 -11
  8. package/src/components/editor/TopBar.tsx +37 -24
  9. package/src/compound/HostContext.tsx +29 -0
  10. package/src/compound/IconContext.tsx +42 -0
  11. package/src/compound/ReadOnlyContext.tsx +23 -0
  12. package/src/compound/SlidewiseRoot.tsx +325 -0
  13. package/src/compound/index.ts +51 -0
  14. package/src/compound/parts.tsx +160 -0
  15. package/src/css.d.ts +4 -0
  16. package/src/index.ts +43 -0
  17. package/src/lib/__tests__/history.test.ts +164 -0
  18. package/src/lib/store.ts +81 -4
  19. package/README.md +0 -112
  20. package/dist/file.svg +0 -1
  21. package/dist/globe.svg +0 -1
  22. package/dist/types/SlidewiseEditor.d.ts +0 -47
  23. package/dist/types/SlidewiseFileEditor.d.ts +0 -54
  24. package/dist/types/components/editor/BottomToolbar.d.ts +0 -1
  25. package/dist/types/components/editor/Canvas.d.ts +0 -1
  26. package/dist/types/components/editor/Editor.d.ts +0 -8
  27. package/dist/types/components/editor/ElementView.d.ts +0 -6
  28. package/dist/types/components/editor/FloatingToolbar.d.ts +0 -6
  29. package/dist/types/components/editor/GridView.d.ts +0 -1
  30. package/dist/types/components/editor/PlayMode.d.ts +0 -1
  31. package/dist/types/components/editor/SelectionFrame.d.ts +0 -8
  32. package/dist/types/components/editor/SlideRail.d.ts +0 -1
  33. package/dist/types/components/editor/SlideView.d.ts +0 -5
  34. package/dist/types/components/editor/TopBar.d.ts +0 -7
  35. package/dist/types/index.d.ts +0 -7
  36. package/dist/types/lib/StoreProvider.d.ts +0 -8
  37. package/dist/types/lib/fonts.d.ts +0 -9
  38. package/dist/types/lib/pptx/deckToPptx.d.ts +0 -9
  39. package/dist/types/lib/pptx/index.d.ts +0 -3
  40. package/dist/types/lib/pptx/pptxToDeck.d.ts +0 -18
  41. package/dist/types/lib/pptx/types.d.ts +0 -15
  42. package/dist/types/lib/pptx/units.d.ts +0 -25
  43. package/dist/types/lib/schema/migrate.d.ts +0 -25
  44. package/dist/types/lib/seed.d.ts +0 -2
  45. package/dist/types/lib/store.d.ts +0 -55
  46. package/dist/types/lib/types.d.ts +0 -141
  47. package/dist/window.svg +0 -1
  48. package/src/App.tsx +0 -261
  49. package/src/components/editor/Editor.tsx +0 -53
  50. package/src/index.css +0 -13
  51. package/src/lib/seed.ts +0 -777
  52. package/src/main.tsx +0 -10
  53. package/src/vite-env.d.ts +0 -3
@@ -0,0 +1,325 @@
1
+ import {
2
+ forwardRef,
3
+ useEffect,
4
+ useId,
5
+ useImperativeHandle,
6
+ useRef,
7
+ type CSSProperties,
8
+ type PropsWithChildren,
9
+ type Ref,
10
+ } from "react";
11
+ import {
12
+ EditorStoreProvider,
13
+ useEditor,
14
+ useEditorStore,
15
+ } from "@/lib/StoreProvider";
16
+ import { collectFontFamilies, ensureGoogleFontsLoaded } from "@/lib/fonts";
17
+ import type { Deck } from "@/lib/types";
18
+ import { GridView } from "@/components/editor/GridView";
19
+ import { PlayMode } from "@/components/editor/PlayMode";
20
+ import { HostProvider } from "./HostContext";
21
+ import { IconProvider, type SlidewiseIcons } from "./IconContext";
22
+ import { ReadOnlyProvider } from "./ReadOnlyContext";
23
+
24
+ export interface SlidewiseRootProps {
25
+ /**
26
+ * Deck to load on mount. Pass a new reference only when you intend to
27
+ * reset the editor's state (e.g. discard changes, load a different file)
28
+ * — passing a new reference on every `onChange` would loop.
29
+ */
30
+ deck: Deck;
31
+ /** Fires after every committed mutation. */
32
+ onChange?: (deck: Deck) => void;
33
+ /** Fires when the user invokes save (top bar button or imperative API). */
34
+ onSave?: (deck: Deck) => void | Promise<void>;
35
+ /** Override the default `.slidewise.json` export. */
36
+ onExport?: (deck: Deck) => void;
37
+ /** Fires when the dirty flag flips. */
38
+ onDirtyChange?: (dirty: boolean) => void;
39
+ /**
40
+ * Fires whenever the undo/redo stacks change depth. Use this to update
41
+ * "Undo"/"Redo" button enabled state without polling `canUndo()`/`canRedo()`.
42
+ */
43
+ onHistoryChange?: (state: HistoryState) => void;
44
+ /**
45
+ * Hide editing affordances (save / undo / redo) and disable canvas
46
+ * mutations. Use this when the host viewer doesn't have write access.
47
+ */
48
+ readOnly?: boolean;
49
+ /** "light" | "dark"; first-render only. */
50
+ theme?: "light" | "dark";
51
+ /** Slide id to land on; falls back to the first. */
52
+ initialSlideId?: string;
53
+ /** Override the default Geist font; sets `--slidewise-font-sans`. */
54
+ fontFamily?: string;
55
+ /**
56
+ * Per-action icon overrides for the chrome. Hosts pass any subset to
57
+ * skin Slidewise with their own icon set; missing slots fall back to
58
+ * the bundled lucide icons.
59
+ */
60
+ icons?: SlidewiseIcons;
61
+ /** Extra class names appended to the root. */
62
+ className?: string;
63
+ /** Inline style applied to the root. */
64
+ style?: CSSProperties;
65
+ }
66
+
67
+ export interface HistoryState {
68
+ canUndo: boolean;
69
+ canRedo: boolean;
70
+ /** Snapshot counts. Useful for "X steps to redo" indicators. */
71
+ undoSize: number;
72
+ redoSize: number;
73
+ }
74
+
75
+ export interface SlidewiseRootHandle {
76
+ play(): void;
77
+ stop(): void;
78
+ undo(): void;
79
+ redo(): void;
80
+ /** True iff there's at least one snapshot to undo back to. */
81
+ canUndo(): boolean;
82
+ /** True iff there's at least one snapshot to redo forward to. */
83
+ canRedo(): boolean;
84
+ /** Current undo/redo stack depths. */
85
+ getHistorySize(): { undo: number; redo: number };
86
+ /**
87
+ * End the in-flight coalesce burst. Call on natural commit boundaries
88
+ * (mouseup after drag, blur on a text input) so the next mutation starts
89
+ * a fresh history step. Most hosts won't need this — the 500ms idle
90
+ * window handles typical typing/drag bursts.
91
+ */
92
+ endCoalesce(): void;
93
+ getDeck(): Deck;
94
+ isDirty(): boolean;
95
+ resetDirty(): void;
96
+ }
97
+
98
+ /**
99
+ * Top-level compound part. Provides the editor's store via context to all
100
+ * descendants and renders the themed root container. Compose any subset of
101
+ * `<Slidewise.TopBar />`, `<Slidewise.SlideRail />`, `<Slidewise.Canvas />`,
102
+ * `<Slidewise.RightPanel />`, `<Slidewise.BottomToolbar />` as children — or
103
+ * mix them with host UI to wrap, replace, or omit any region.
104
+ *
105
+ * Hosts that want the unopinionated default tree can use `<SlidewiseEditor>`
106
+ * which is just `<Slidewise.Root>` rendering the standard layout.
107
+ */
108
+ export const Root = forwardRef<SlidewiseRootHandle, PropsWithChildren<SlidewiseRootProps>>(
109
+ function SlidewiseRoot(props, ref) {
110
+ return (
111
+ <EditorStoreProvider initialDeck={props.deck}>
112
+ <RootInner {...props} forwardedRef={ref} />
113
+ </EditorStoreProvider>
114
+ );
115
+ }
116
+ );
117
+
118
+ function RootInner({
119
+ deck,
120
+ onChange,
121
+ onSave,
122
+ onExport,
123
+ onDirtyChange,
124
+ onHistoryChange: props_onHistoryChange,
125
+ readOnly = false,
126
+ theme,
127
+ initialSlideId,
128
+ fontFamily,
129
+ icons,
130
+ className,
131
+ style,
132
+ children,
133
+ forwardedRef,
134
+ }: PropsWithChildren<SlidewiseRootProps> & {
135
+ forwardedRef: Ref<SlidewiseRootHandle>;
136
+ }) {
137
+ const store = useEditorStore();
138
+ const savedDeckRef = useRef<Deck>(deck);
139
+ const dirtyRef = useRef(false);
140
+ const onChangeRef = useRef(onChange);
141
+ const onDirtyChangeRef = useRef(onDirtyChange);
142
+ const onSaveRef = useRef(onSave);
143
+ const onExportRef = useRef(onExport);
144
+ const onHistoryChangeRef = useRef(props_onHistoryChange);
145
+
146
+ useEffect(() => {
147
+ onChangeRef.current = onChange;
148
+ onDirtyChangeRef.current = onDirtyChange;
149
+ onSaveRef.current = onSave;
150
+ onExportRef.current = onExport;
151
+ onHistoryChangeRef.current = props_onHistoryChange;
152
+ }, [onChange, onDirtyChange, onSave, onExport, props_onHistoryChange]);
153
+
154
+ useEffect(() => {
155
+ if (theme) {
156
+ store.getState().setTheme(theme);
157
+ }
158
+ }, [theme, store]);
159
+
160
+ useEffect(() => {
161
+ if (initialSlideId) {
162
+ const exists = store
163
+ .getState()
164
+ .deck.slides.some((s) => s.id === initialSlideId);
165
+ if (exists) {
166
+ store.getState().selectSlide(initialSlideId);
167
+ }
168
+ }
169
+ // run once on mount
170
+ // eslint-disable-next-line react-hooks/exhaustive-deps
171
+ }, []);
172
+
173
+ useEffect(() => {
174
+ if (deck !== savedDeckRef.current) {
175
+ store.getState().setDeck(deck);
176
+ savedDeckRef.current = deck;
177
+ if (dirtyRef.current) {
178
+ dirtyRef.current = false;
179
+ onDirtyChangeRef.current?.(false);
180
+ }
181
+ }
182
+ }, [deck, store]);
183
+
184
+ const instanceId = useId().replace(/[^a-z0-9]/gi, "");
185
+ useEffect(() => {
186
+ ensureGoogleFontsLoaded(
187
+ instanceId,
188
+ collectFontFamilies(store.getState().deck)
189
+ );
190
+ return store.subscribe((state, prev) => {
191
+ // Fire onHistoryChange whenever stack depths change. Independent of
192
+ // deck identity so undo/redo always emit, even if the resulting deck
193
+ // happens to be reference-equal (shouldn't, but defensive).
194
+ const prevHist = prev.history.length;
195
+ const prevFut = prev.future.length;
196
+ const nextHist = state.history.length;
197
+ const nextFut = state.future.length;
198
+ if (prevHist !== nextHist || prevFut !== nextFut) {
199
+ onHistoryChangeRef.current?.({
200
+ canUndo: nextHist > 0,
201
+ canRedo: nextFut > 0,
202
+ undoSize: nextHist,
203
+ redoSize: nextFut,
204
+ });
205
+ }
206
+ if (state.deck === prev.deck) return;
207
+ onChangeRef.current?.(state.deck);
208
+ const nextDirty = state.deck !== savedDeckRef.current;
209
+ if (nextDirty !== dirtyRef.current) {
210
+ dirtyRef.current = nextDirty;
211
+ onDirtyChangeRef.current?.(nextDirty);
212
+ }
213
+ ensureGoogleFontsLoaded(instanceId, collectFontFamilies(state.deck));
214
+ });
215
+ }, [store, instanceId]);
216
+
217
+ useEffect(() => {
218
+ return () => {
219
+ ensureGoogleFontsLoaded(instanceId, []);
220
+ };
221
+ }, [instanceId]);
222
+
223
+ useImperativeHandle(
224
+ forwardedRef,
225
+ () => ({
226
+ play: () => store.getState().play(),
227
+ stop: () => store.getState().stop(),
228
+ undo: () => store.getState().undo(),
229
+ redo: () => store.getState().redo(),
230
+ canUndo: () => store.getState().canUndo(),
231
+ canRedo: () => store.getState().canRedo(),
232
+ getHistorySize: () => {
233
+ const s = store.getState();
234
+ return { undo: s.history.length, redo: s.future.length };
235
+ },
236
+ endCoalesce: () => store.getState().endCoalesce(),
237
+ getDeck: () => store.getState().deck,
238
+ isDirty: () => dirtyRef.current,
239
+ resetDirty: () => {
240
+ savedDeckRef.current = store.getState().deck;
241
+ if (dirtyRef.current) {
242
+ dirtyRef.current = false;
243
+ onDirtyChangeRef.current?.(false);
244
+ }
245
+ },
246
+ }),
247
+ [store]
248
+ );
249
+
250
+ // Wrap host save with dirty-flag reset so any TopBar.Save / imperative save
251
+ // path that funnels through here clears the dirty state on success.
252
+ const wrappedSave = onSave
253
+ ? async (d: Deck) => {
254
+ await onSaveRef.current!(d);
255
+ savedDeckRef.current = d;
256
+ if (dirtyRef.current) {
257
+ dirtyRef.current = false;
258
+ onDirtyChangeRef.current?.(false);
259
+ }
260
+ }
261
+ : undefined;
262
+
263
+ return (
264
+ <ReadOnlyProvider readOnly={readOnly}>
265
+ <IconProvider icons={icons ?? {}}>
266
+ <HostProvider callbacks={{ onSave: wrappedSave, onExport }}>
267
+ <RootShell
268
+ fontFamily={fontFamily}
269
+ className={className}
270
+ style={style}
271
+ >
272
+ {children}
273
+ </RootShell>
274
+ </HostProvider>
275
+ </IconProvider>
276
+ </ReadOnlyProvider>
277
+ );
278
+ }
279
+
280
+ /**
281
+ * Renders the themed container. Split out so the children read the theme
282
+ * via the store (not props), letting them re-render when the theme flips.
283
+ */
284
+ function RootShell({
285
+ fontFamily,
286
+ className,
287
+ style,
288
+ children,
289
+ }: PropsWithChildren<{
290
+ fontFamily?: string;
291
+ className?: string;
292
+ style?: CSSProperties;
293
+ }>) {
294
+ const theme = useEditor((s) => s.theme);
295
+ const playing = useEditor((s) => s.playing);
296
+ const view = useEditor((s) => s.view);
297
+
298
+ const rootStyle: CSSProperties = {
299
+ width: "100%",
300
+ height: "100%",
301
+ display: "flex",
302
+ flexDirection: "column",
303
+ background: "var(--app-bg)",
304
+ color: "var(--ink)",
305
+ overflow: "hidden",
306
+ ...(fontFamily ? { ["--font-geist-sans" as string]: fontFamily } : null),
307
+ ...style,
308
+ };
309
+
310
+ return (
311
+ <div
312
+ className={
313
+ className
314
+ ? `slidewise-editor theme-${theme} ${className}`
315
+ : `slidewise-editor theme-${theme}`
316
+ }
317
+ data-slidewise-theme={theme}
318
+ style={rootStyle}
319
+ >
320
+ {children}
321
+ {view === "grid" && <GridView />}
322
+ {playing && <PlayMode />}
323
+ </div>
324
+ );
325
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Compound API for Slidewise. Use these primitives when you want to compose
3
+ * the editor — replacing, wrapping, or omitting any region. Hosts that just
4
+ * want the default editor can keep using `<SlidewiseEditor>`, which is a
5
+ * thin wrapper rendering this same tree:
6
+ *
7
+ * ```tsx
8
+ * <Slidewise.Root deck={deck} onChange={…} onSave={…}>
9
+ * <Slidewise.TopBar />
10
+ * <Slidewise.Body>
11
+ * <Slidewise.SlideRail />
12
+ * <Slidewise.CanvasFrame>
13
+ * <Slidewise.Canvas />
14
+ * <Slidewise.BottomToolbar />
15
+ * </Slidewise.CanvasFrame>
16
+ * </Slidewise.Body>
17
+ * </Slidewise.Root>
18
+ * ```
19
+ *
20
+ * Use the namespace import to keep call sites tidy:
21
+ *
22
+ * ```tsx
23
+ * import * as Slidewise from "@textcortex/slidewise";
24
+ * ```
25
+ */
26
+ export {
27
+ Root,
28
+ type SlidewiseRootProps,
29
+ type SlidewiseRootHandle,
30
+ type HistoryState,
31
+ } from "./SlidewiseRoot";
32
+ export {
33
+ TopBar,
34
+ SlideRail,
35
+ Canvas,
36
+ BottomToolbar,
37
+ RightPanel,
38
+ Body,
39
+ CanvasFrame,
40
+ type RegionProps,
41
+ } from "./parts";
42
+ export {
43
+ useHostCallbacks,
44
+ type SlidewiseHostCallbacks,
45
+ } from "./HostContext";
46
+ export {
47
+ IconProvider,
48
+ useIcons,
49
+ type SlidewiseIcons,
50
+ } from "./IconContext";
51
+ export { ReadOnlyProvider, useReadOnly } from "./ReadOnlyContext";
@@ -0,0 +1,160 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import { TopBar as TopBarInternal } from "@/components/editor/TopBar";
3
+ import { SlideRail as SlideRailInternal } from "@/components/editor/SlideRail";
4
+ import { Canvas as CanvasInternal } from "@/components/editor/Canvas";
5
+ import { BottomToolbar as BottomToolbarInternal } from "@/components/editor/BottomToolbar";
6
+ import { useHostCallbacks } from "./HostContext";
7
+
8
+ /**
9
+ * Region-level compound parts. Each consumes the editor store via context,
10
+ * so any part can be omitted, wrapped, or replaced. None of these accept
11
+ * deck/onChange/onSave props — those live on `<Slidewise.Root>`.
12
+ */
13
+
14
+ export interface RegionProps {
15
+ className?: string;
16
+ style?: CSSProperties;
17
+ }
18
+
19
+ /**
20
+ * The default top bar (title input, undo/redo, save, play, theme toggle,
21
+ * export). Reads host callbacks from context, so the Save and Export
22
+ * buttons fire the host's `onSave` / `onExport` from `<Slidewise.Root>`.
23
+ *
24
+ * Omit it from the tree to hide the whole bar; or render your own toolbar
25
+ * alongside `<Slidewise.Canvas>` for full control.
26
+ */
27
+ export function TopBar(_props: RegionProps = {}) {
28
+ const { onSave, onExport } = useHostCallbacks();
29
+ return <TopBarInternal onSave={onSave} onExport={onExport} />;
30
+ }
31
+
32
+ /**
33
+ * Left-side slide thumbnail rail with add/duplicate/delete.
34
+ */
35
+ export function SlideRail(_props: RegionProps = {}) {
36
+ return <SlideRailInternal />;
37
+ }
38
+
39
+ /**
40
+ * The main editing canvas. This is the only part that's effectively required
41
+ * — without it the editor renders nothing visible. Layout-wise it expects
42
+ * a flex container that gives it `flex: 1`; the default layout takes care of
43
+ * this when you also render `<Slidewise.Body>`.
44
+ */
45
+ export function Canvas(_props: RegionProps = {}) {
46
+ return <CanvasInternal />;
47
+ }
48
+
49
+ /**
50
+ * Floating bottom toolbar with the active-tool selector (select / text /
51
+ * shape / etc.). Optional — omit it if your host has its own tool surface.
52
+ */
53
+ export function BottomToolbar(_props: RegionProps = {}) {
54
+ return <BottomToolbarInternal />;
55
+ }
56
+
57
+ /**
58
+ * Right-side properties panel. The default editor doesn't ship a built-in
59
+ * inspector yet — this slot is rendered for hosts that want to inject their
60
+ * own (AI suggestions, comments, element properties, etc.). Pass `children`
61
+ * to fill the slot; the part handles the layout (fixed width column, themed
62
+ * surface) so injected content blends with the rest of the editor.
63
+ */
64
+ export function RightPanel({
65
+ className,
66
+ style,
67
+ children,
68
+ width = 320,
69
+ }: RegionProps & { children?: ReactNode; width?: number | string }) {
70
+ if (!children) return null;
71
+ return (
72
+ <aside
73
+ className={
74
+ className
75
+ ? `slidewise-right-panel ${className}`
76
+ : "slidewise-right-panel"
77
+ }
78
+ style={{
79
+ width,
80
+ flexShrink: 0,
81
+ height: "100%",
82
+ background: "var(--rail-bg)",
83
+ borderLeft: "1px solid var(--border)",
84
+ boxShadow: "var(--rail-shadow)",
85
+ overflow: "auto",
86
+ ...style,
87
+ }}
88
+ >
89
+ {children}
90
+ </aside>
91
+ );
92
+ }
93
+
94
+ /**
95
+ * Default body layout — slide rail + canvas + (optional) right panel side
96
+ * by side. Most hosts compose this manually:
97
+ *
98
+ * ```tsx
99
+ * <Slidewise.Root deck={deck}>
100
+ * <Slidewise.TopBar />
101
+ * <Slidewise.Body>
102
+ * <Slidewise.SlideRail />
103
+ * <Slidewise.Canvas />
104
+ * <Slidewise.BottomToolbar />
105
+ * </Slidewise.Body>
106
+ * </Slidewise.Root>
107
+ * ```
108
+ *
109
+ * Provided as a convenience so most hosts don't have to repeat the flex
110
+ * row + relative positioning that BottomToolbar's anchor expects.
111
+ */
112
+ export function Body({ className, style, children }: RegionProps & { children?: ReactNode }) {
113
+ return (
114
+ <div
115
+ className={
116
+ className ? `slidewise-body ${className}` : "slidewise-body"
117
+ }
118
+ style={{
119
+ flex: 1,
120
+ display: "flex",
121
+ overflow: "hidden",
122
+ position: "relative",
123
+ ...style,
124
+ }}
125
+ >
126
+ {children}
127
+ </div>
128
+ );
129
+ }
130
+
131
+ /**
132
+ * Wraps `<Slidewise.Canvas>` with the relative positioning that
133
+ * `<Slidewise.BottomToolbar>` anchors to. Use it when you want the toolbar
134
+ * to float over the canvas — which is what the default editor does:
135
+ *
136
+ * ```tsx
137
+ * <Slidewise.CanvasFrame>
138
+ * <Slidewise.Canvas />
139
+ * <Slidewise.BottomToolbar />
140
+ * </Slidewise.CanvasFrame>
141
+ * ```
142
+ */
143
+ export function CanvasFrame({
144
+ className,
145
+ style,
146
+ children,
147
+ }: RegionProps & { children?: ReactNode }) {
148
+ return (
149
+ <div
150
+ className={
151
+ className
152
+ ? `slidewise-canvas-frame ${className}`
153
+ : "slidewise-canvas-frame"
154
+ }
155
+ style={{ flex: 1, display: "flex", position: "relative", ...style }}
156
+ >
157
+ {children}
158
+ </div>
159
+ );
160
+ }
package/src/css.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ // Allow side-effect imports of plain CSS files from TypeScript.
2
+ // The library bundles its CSS into dist/slidewise.css; consumers import
3
+ // "@textcortex/slidewise/style.css" separately (see package.json exports).
4
+ declare module "*.css";
package/src/index.ts CHANGED
@@ -10,6 +10,49 @@ export {
10
10
  type SlidewiseFileEditorApi,
11
11
  } from "./SlidewiseFileEditor";
12
12
 
13
+ /**
14
+ * Compound API. Use the namespace import idiom for the full editor:
15
+ *
16
+ * ```tsx
17
+ * import * as Slidewise from "@textcortex/slidewise";
18
+ *
19
+ * <Slidewise.Root deck={deck} onChange={...}>
20
+ * <Slidewise.TopBar />
21
+ * <Slidewise.Body>
22
+ * <Slidewise.SlideRail />
23
+ * <Slidewise.CanvasFrame>
24
+ * <Slidewise.Canvas />
25
+ * <Slidewise.BottomToolbar />
26
+ * </Slidewise.CanvasFrame>
27
+ * </Slidewise.Body>
28
+ * </Slidewise.Root>
29
+ * ```
30
+ *
31
+ * Each region reads the editor store via context, so you can replace, wrap,
32
+ * or omit any one. `<Slidewise.RightPanel>` is provided so hosts can inject
33
+ * their own panel content (AI suggestions, comments, etc.) inside the same
34
+ * themed surface.
35
+ */
36
+ export {
37
+ Root,
38
+ TopBar,
39
+ SlideRail,
40
+ Canvas,
41
+ BottomToolbar,
42
+ RightPanel,
43
+ Body,
44
+ CanvasFrame,
45
+ useHostCallbacks,
46
+ useIcons,
47
+ useReadOnly,
48
+ type SlidewiseRootProps,
49
+ type SlidewiseRootHandle,
50
+ type HistoryState,
51
+ type SlidewiseHostCallbacks,
52
+ type SlidewiseIcons,
53
+ type RegionProps,
54
+ } from "./compound";
55
+
13
56
  export { parsePptx, serializeDeck } from "./lib/pptx";
14
57
  export type { ParseDiagnostics, ParseResult } from "./lib/pptx/types";
15
58