@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
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@textcortex/slidewise",
3
- "version": "1.0.1",
3
+ "version": "1.2.0",
4
4
  "description": "Embeddable React PPTX editor.",
5
5
  "license": "MIT",
6
- "private": false,
7
6
  "type": "module",
8
7
  "main": "./dist/index.mjs",
9
8
  "module": "./dist/index.mjs",
@@ -25,7 +24,8 @@
25
24
  ],
26
25
  "repository": {
27
26
  "type": "git",
28
- "url": "git+https://github.com/textcortex/SlideWise.git"
27
+ "url": "git+https://github.com/textcortex/SlideWise.git",
28
+ "directory": "packages/slidewise"
29
29
  },
30
30
  "homepage": "https://github.com/textcortex/SlideWise#readme",
31
31
  "bugs": {
@@ -51,36 +51,21 @@
51
51
  "zustand": "^5.0.12"
52
52
  },
53
53
  "devDependencies": {
54
- "@changesets/cli": "^2.27.10",
55
- "@tailwindcss/vite": "^4",
56
54
  "@testing-library/jest-dom": "^6.9.1",
57
55
  "@testing-library/react": "^16.3.2",
58
56
  "@types/react": "^19",
59
57
  "@types/react-dom": "^19",
60
58
  "@vitejs/plugin-react": "^5.0.0",
61
- "eslint": "^9",
62
- "eslint-plugin-react-hooks": "^5.2.0",
63
- "eslint-plugin-react-refresh": "^0.4.20",
64
- "globals": "^16.2.0",
65
59
  "jsdom": "^29.1.1",
66
60
  "react": "19.2.4",
67
61
  "react-dom": "19.2.4",
68
- "tailwindcss": "^4",
69
- "typescript": "^5",
70
- "typescript-eslint": "^8.42.0",
71
62
  "vite": "^7.0.0",
72
63
  "vitest": "^4.1.5"
73
64
  },
74
65
  "scripts": {
75
- "dev": "vite",
76
- "build": "tsc -b && vite build",
77
66
  "build:lib": "tsc -p tsconfig.lib.json && vite build --mode lib",
78
- "preview": "vite preview",
79
67
  "test": "vitest run",
80
68
  "test:watch": "vitest",
81
- "lint": "eslint .",
82
- "changeset": "changeset",
83
- "version-packages": "changeset version",
84
- "release": "pnpm run build:lib && changeset publish"
69
+ "lint": "eslint ."
85
70
  }
86
71
  }
@@ -4,24 +4,75 @@
4
4
  * to avoid colliding with host styles. At-rules (@font-face, @media, …) are
5
5
  * fine at the top level. The `library CSS scope` test in
6
6
  * src/lib/__tests__/css-scope.test.ts enforces this.
7
+ *
8
+ * THEMING
9
+ * -------
10
+ * Every chrome surface (panels, cards, popovers, the canvas frame) reads
11
+ * from a small set of CSS custom properties exposed below. Hosts override
12
+ * any of them by either:
13
+ *
14
+ * 1. Passing them via the `style` prop on `<Slidewise.Root>`:
15
+ * <Slidewise.Root style={{ "--surface-bg": "#0f0f12" }}>
16
+ *
17
+ * 2. Setting them in a stylesheet that targets a wrapping class:
18
+ * .my-host .slidewise-editor { --surface-bg: #0f0f12; }
19
+ *
20
+ * The light defaults track the rest of TextCortex's web app; the dark
21
+ * defaults adopt the kit from textcortex/platform#7428 (charcoal-purple
22
+ * surface with a faint inset highlight + layered shadow on hover).
7
23
  */
8
24
  @import "./fonts.css";
9
25
 
10
26
  .slidewise-editor {
27
+ /* PUBLIC THEMING TOKENS
28
+ These are namespaced with `--slidewise-*` and intended for hosts to
29
+ override directly via the `style` prop on `<Slidewise.Root>` or via a
30
+ wrapping stylesheet. Internal variables (below) cascade from these
31
+ where the connection is structural. Hosts that want to override every
32
+ surface still have access to the unprefixed internal vars too. */
33
+ --slidewise-radius: 10px;
34
+ --slidewise-bar-bg: var(--app-bg);
35
+ --slidewise-accent: var(--accent);
36
+
37
+ /* Layout backgrounds */
11
38
  --app-bg: #ffffff;
12
39
  --rail-bg: #fafafb;
13
40
  --canvas-bg-from: #f4f6fb;
14
41
  --canvas-bg-to: #e7eaf2;
42
+
43
+ /* Generic chrome surface (cards, popovers, dialogs). Mirrors the kit
44
+ introduced for the Browse Agents redesign — neutral surface with a
45
+ hairline inset ring and a layered shadow that lifts on hover. */
46
+ --surface-bg: #ffffff;
47
+ --surface-ring: rgba(0, 0, 0, 0.06);
48
+ --surface-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.6) inset,
49
+ 0 1px 2px rgba(16, 24, 40, 0.06),
50
+ 0 1px 3px rgba(16, 24, 40, 0.08);
51
+ --surface-hover-bg: #f6f3ff;
52
+ --surface-hover-ring: rgba(213, 192, 253, 0.6);
53
+ --surface-hover-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.6) inset,
54
+ 0 2px 4px rgba(128, 67, 249, 0.1),
55
+ 0 8px 24px -4px rgba(128, 67, 249, 0.22);
56
+
57
+ /* Borders and dividers */
15
58
  --border: rgba(15, 23, 42, 0.08);
16
59
  --border-strong: rgba(15, 23, 42, 0.1);
17
60
  --border-dashed: rgba(15, 23, 42, 0.18);
61
+
62
+ /* Text */
18
63
  --ink: #0e1330;
19
64
  --ink-muted: #5b6178;
65
+
66
+ /* Hover/active states for interactive elements (buttons, list rows) */
20
67
  --hover: rgba(15, 23, 42, 0.05);
21
68
  --hover-strong: rgba(15, 23, 42, 0.06);
22
69
  --active: rgba(15, 23, 42, 0.08);
70
+
71
+ /* Brand accent */
23
72
  --accent: #4f5bd5;
24
73
  --accent-soft: rgba(79, 91, 213, 0.14);
74
+
75
+ /* Floating UI shadows */
25
76
  --toolbar-bg: rgba(255, 255, 255, 0.94);
26
77
  --toolbar-shadow: 0 1px 2px rgba(15, 23, 42, 0.04),
27
78
  0 12px 32px rgba(15, 23, 42, 0.1);
@@ -32,20 +83,33 @@
32
83
  0 24px 60px rgba(15, 23, 42, 0.16);
33
84
  --thumb-shadow: 0 1px 2px rgba(15, 23, 42, 0.04),
34
85
  0 6px 20px rgba(15, 23, 42, 0.08);
86
+
87
+ /* Tool selector active pill */
35
88
  --tool-active-bg: #0e1330;
36
89
  --tool-active-fg: #ffffff;
90
+
91
+ /* Primary button (e.g. Save) */
37
92
  --primary-bg: #0e1330;
38
93
  --primary-bg-hover: #1a2147;
39
94
  --primary-fg: #ffffff;
95
+
96
+ /* Form inputs */
40
97
  --input-bg: rgba(15, 23, 42, 0.04);
98
+
99
+ /* Popover surfaces */
41
100
  --menu-bg: #ffffff;
42
101
  --menu-shadow: 0 12px 24px rgba(15, 23, 42, 0.12);
102
+
103
+ /* Misc */
43
104
  --gap-icon-bg: #ffffff;
44
105
  --scroll-thumb: rgba(15, 23, 42, 0.14);
45
106
  --scroll-thumb-hover: rgba(15, 23, 42, 0.24);
46
107
  --smart-grad: linear-gradient(135deg, #e7e9ff, #fce7df);
47
108
  --smart-fg: #4f5bd5;
48
109
  --grid-overlay-bg: rgba(245, 246, 250, 0.96);
110
+ --loading-overlay-bg: rgba(255, 255, 255, 0.6);
111
+
112
+ /* Typography */
49
113
  --font-geist-sans: "Geist Variable";
50
114
  --font-geist-mono: "Geist Mono Variable";
51
115
  color-scheme: light;
@@ -58,20 +122,38 @@
58
122
 
59
123
  .slidewise-editor.theme-dark {
60
124
  --app-bg: #0a0d1c;
61
- --rail-bg: #0d1124;
125
+ --rail-bg: #11131c;
62
126
  --canvas-bg-from: #181c38;
63
127
  --canvas-bg-to: #0c1027;
128
+
129
+ /* Charcoal-purple surface kit from textcortex/platform#7428 — neutral
130
+ dark base, faint inset highlight, layered shadow that lifts to a
131
+ subtle plum tint on hover. */
132
+ --surface-bg: #1c1c22;
133
+ --surface-ring: rgba(255, 255, 255, 0.05);
134
+ --surface-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.06) inset,
135
+ 0 1px 2px rgba(0, 0, 0, 0.5),
136
+ 0 4px 10px -2px rgba(0, 0, 0, 0.4);
137
+ --surface-hover-bg: #241834;
138
+ --surface-hover-ring: rgba(125, 91, 223, 0.2);
139
+ --surface-hover-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.04) inset,
140
+ 0 4px 16px -2px rgba(125, 91, 223, 0.4);
141
+
64
142
  --border: rgba(255, 255, 255, 0.08);
65
143
  --border-strong: rgba(255, 255, 255, 0.14);
66
144
  --border-dashed: rgba(255, 255, 255, 0.18);
145
+
67
146
  --ink: #ecedfa;
68
147
  --ink-muted: #969ab8;
148
+
69
149
  --hover: rgba(255, 255, 255, 0.06);
70
150
  --hover-strong: rgba(255, 255, 255, 0.09);
71
151
  --active: rgba(255, 255, 255, 0.12);
152
+
72
153
  --accent: #8a96f0;
73
154
  --accent-soft: rgba(138, 150, 240, 0.22);
74
- --toolbar-bg: rgba(22, 26, 56, 0.88);
155
+
156
+ --toolbar-bg: rgba(28, 28, 34, 0.92);
75
157
  --toolbar-shadow: 0 1px 2px rgba(0, 0, 0, 0.5),
76
158
  0 12px 32px rgba(0, 0, 0, 0.5);
77
159
  --rail-shadow: 4px 0 14px rgba(0, 0, 0, 0.35);
@@ -81,15 +163,20 @@
81
163
  0 30px 80px rgba(0, 0, 0, 0.55);
82
164
  --thumb-shadow: 0 1px 2px rgba(0, 0, 0, 0.5),
83
165
  0 8px 24px rgba(0, 0, 0, 0.45);
166
+
84
167
  --tool-active-bg: #ecedfa;
85
168
  --tool-active-fg: #0a0d1c;
169
+
86
170
  --primary-bg: #ecedfa;
87
171
  --primary-bg-hover: #ffffff;
88
172
  --primary-fg: #0a0d1c;
173
+
89
174
  --input-bg: rgba(255, 255, 255, 0.07);
90
- --menu-bg: #181c3a;
175
+
176
+ --menu-bg: #1c1c22;
91
177
  --menu-shadow: 0 12px 28px rgba(0, 0, 0, 0.55);
92
- --gap-icon-bg: #181c3a;
178
+
179
+ --gap-icon-bg: #1c1c22;
93
180
  --scroll-thumb: rgba(255, 255, 255, 0.16);
94
181
  --scroll-thumb-hover: rgba(255, 255, 255, 0.3);
95
182
  --smart-grad: linear-gradient(
@@ -99,6 +186,7 @@
99
186
  );
100
187
  --smart-fg: #c8cefb;
101
188
  --grid-overlay-bg: rgba(10, 13, 28, 0.96);
189
+ --loading-overlay-bg: rgba(0, 0, 0, 0.5);
102
190
  color-scheme: dark;
103
191
  }
104
192
 
@@ -144,3 +232,32 @@
144
232
  .slidewise-editor button {
145
233
  font-family: inherit;
146
234
  }
235
+
236
+ /*
237
+ * Ambient surface utility — a card / panel base that uses the surface-*
238
+ * tokens. Region parts (e.g. RightPanel) and host injections under it
239
+ * automatically pick up the kit's hairline ring + layered shadow with no
240
+ * extra styling. Hosts can apply this class manually or override the
241
+ * token values to retheme every surface in one place.
242
+ */
243
+ .slidewise-editor .slidewise-surface {
244
+ background: var(--surface-bg);
245
+ box-shadow: var(--surface-shadow);
246
+ border: 1px solid transparent;
247
+ outline: 1px solid var(--surface-ring);
248
+ outline-offset: -1px;
249
+ border-radius: 12px;
250
+ transition: background-color 200ms, box-shadow 200ms, outline-color 200ms;
251
+ }
252
+
253
+ .slidewise-editor .slidewise-surface[data-interactive="true"]:hover {
254
+ background: var(--surface-hover-bg);
255
+ box-shadow: var(--surface-hover-shadow);
256
+ outline-color: var(--surface-hover-ring);
257
+ }
258
+
259
+ @media (prefers-reduced-motion: reduce) {
260
+ .slidewise-editor .slidewise-surface {
261
+ transition: none;
262
+ }
263
+ }
@@ -1,18 +1,19 @@
1
+ import { forwardRef, type CSSProperties } from "react";
1
2
  import {
2
- forwardRef,
3
- useEffect,
4
- useId,
5
- useImperativeHandle,
6
- useRef,
7
- type CSSProperties,
8
- type Ref,
9
- } from "react";
10
- import { Editor } from "@/components/editor/Editor";
3
+ Root,
4
+ type SlidewiseRootHandle,
5
+ type SlidewiseRootProps,
6
+ type HistoryState,
7
+ } from "./compound/SlidewiseRoot";
11
8
  import {
12
- EditorStoreProvider,
13
- useEditorStore,
14
- } from "@/lib/StoreProvider";
15
- import { collectFontFamilies, ensureGoogleFontsLoaded } from "@/lib/fonts";
9
+ TopBar,
10
+ SlideRail,
11
+ Canvas,
12
+ BottomToolbar,
13
+ Body,
14
+ CanvasFrame,
15
+ } from "./compound/parts";
16
+ import type { SlidewiseIcons } from "./compound/IconContext";
16
17
  import type { Deck } from "@/lib/types";
17
18
  import "./SlidewiseEditor.css";
18
19
 
@@ -34,6 +35,11 @@ export interface SlidewiseEditorProps {
34
35
  onExport?: (deck: Deck) => void;
35
36
  /** Fires when the dirty flag flips. Useful for "unsaved changes" banners. */
36
37
  onDirtyChange?: (dirty: boolean) => void;
38
+ /**
39
+ * Fires whenever the undo/redo stack depths change. Use this to enable/disable
40
+ * Undo and Redo buttons reactively without polling `api.canUndo()`.
41
+ */
42
+ onHistoryChange?: (state: HistoryState) => void;
37
43
  /** Reserved for future use; not enforced yet. */
38
44
  readOnly?: boolean;
39
45
  /** "light" or "dark"; defaults to "light". Ignored after first render. */
@@ -42,173 +48,95 @@ export interface SlidewiseEditorProps {
42
48
  initialSlideId?: string;
43
49
  /** Render the built-in top bar (title, undo/redo, save, play). Default true. */
44
50
  showTopBar?: boolean;
51
+ /** Render the floating bottom toolbar (tool selector). Default true. */
52
+ showBottomToolbar?: boolean;
45
53
  /** Override the bundled Geist font; sets `--font-geist-sans` on the root. */
46
54
  fontFamily?: string;
55
+ /**
56
+ * Per-action icon overrides. Pass a ReactNode for any of `undo`, `redo`,
57
+ * `save`, `play`, `themeLight`, `themeDark`, `export`, `smart` to skin the
58
+ * editor's chrome with your own icon set; missing slots fall back to the
59
+ * bundled lucide-react icons.
60
+ */
61
+ icons?: SlidewiseIcons;
47
62
  /** Extra class names appended to the editor root. */
48
63
  className?: string;
49
64
  /** Inline style applied to the editor root. */
50
65
  style?: CSSProperties;
51
66
  }
52
67
 
53
- export interface SlidewiseEditorHandle {
54
- play(): void;
55
- stop(): void;
56
- undo(): void;
57
- redo(): void;
58
- getDeck(): Deck;
59
- isDirty(): boolean;
60
- resetDirty(): void;
61
- }
68
+ export type SlidewiseEditorHandle = SlidewiseRootHandle;
62
69
 
70
+ /**
71
+ * Convenience wrapper that renders the default editor layout. Equivalent to:
72
+ *
73
+ * ```tsx
74
+ * <Slidewise.Root deck={deck} onChange={...}>
75
+ * <Slidewise.TopBar />
76
+ * <Slidewise.Body>
77
+ * <Slidewise.SlideRail />
78
+ * <Slidewise.CanvasFrame>
79
+ * <Slidewise.Canvas />
80
+ * <Slidewise.BottomToolbar />
81
+ * </Slidewise.CanvasFrame>
82
+ * </Slidewise.Body>
83
+ * </Slidewise.Root>
84
+ * ```
85
+ *
86
+ * Use `<Slidewise.Root>` directly when you need to wrap, replace, or omit
87
+ * any region.
88
+ */
63
89
  export const SlidewiseEditor = forwardRef<
64
90
  SlidewiseEditorHandle,
65
91
  SlidewiseEditorProps
66
- >(function SlidewiseEditor(props, ref) {
67
- return (
68
- <EditorStoreProvider initialDeck={props.deck}>
69
- <SlidewiseEditorInner {...props} forwardedRef={ref} />
70
- </EditorStoreProvider>
71
- );
72
- });
73
-
74
- function SlidewiseEditorInner({
75
- deck,
76
- onChange,
77
- onSave,
78
- onExport,
79
- onDirtyChange,
80
- theme,
81
- initialSlideId,
82
- showTopBar,
83
- fontFamily,
84
- className,
85
- style,
86
- forwardedRef,
87
- }: SlidewiseEditorProps & { forwardedRef: Ref<SlidewiseEditorHandle> }) {
88
- const store = useEditorStore();
89
- const savedDeckRef = useRef<Deck>(deck);
90
- const dirtyRef = useRef(false);
91
- const onChangeRef = useRef(onChange);
92
- const onDirtyChangeRef = useRef(onDirtyChange);
93
-
94
- // Keep callback refs current without re-subscribing.
95
- useEffect(() => {
96
- onChangeRef.current = onChange;
97
- onDirtyChangeRef.current = onDirtyChange;
98
- }, [onChange, onDirtyChange]);
99
-
100
- // Apply theme on first render and whenever it changes.
101
- useEffect(() => {
102
- if (theme) {
103
- store.getState().setTheme(theme);
104
- }
105
- }, [theme, store]);
106
-
107
- // Land on the requested slide.
108
- useEffect(() => {
109
- if (initialSlideId) {
110
- const exists = store
111
- .getState()
112
- .deck.slides.some((s) => s.id === initialSlideId);
113
- if (exists) {
114
- store.getState().selectSlide(initialSlideId);
115
- }
116
- }
117
- // run once on mount
118
- // eslint-disable-next-line react-hooks/exhaustive-deps
119
- }, []);
120
-
121
- // External deck reset: if a new Deck reference comes in, replace the store's
122
- // deck and clear dirty. The first run is a no-op (savedDeckRef === deck).
123
- useEffect(() => {
124
- if (deck !== savedDeckRef.current) {
125
- store.getState().setDeck(deck);
126
- savedDeckRef.current = deck;
127
- if (dirtyRef.current) {
128
- dirtyRef.current = false;
129
- onDirtyChangeRef.current?.(false);
130
- }
131
- }
132
- }, [deck, store]);
133
-
134
- // Subscribe once: emit onChange, recompute dirty, and refresh the Google
135
- // Fonts <link> whenever the deck changes.
136
- const instanceId = useId().replace(/[^a-z0-9]/gi, "");
137
- useEffect(() => {
138
- ensureGoogleFontsLoaded(
139
- instanceId,
140
- collectFontFamilies(store.getState().deck)
141
- );
142
- return store.subscribe((state, prev) => {
143
- if (state.deck === prev.deck) return;
144
- onChangeRef.current?.(state.deck);
145
- const nextDirty = state.deck !== savedDeckRef.current;
146
- if (nextDirty !== dirtyRef.current) {
147
- dirtyRef.current = nextDirty;
148
- onDirtyChangeRef.current?.(nextDirty);
149
- }
150
- ensureGoogleFontsLoaded(instanceId, collectFontFamilies(state.deck));
151
- });
152
- }, [store, instanceId]);
153
-
154
- // Remove our font <link> when the editor unmounts.
155
- useEffect(() => {
156
- return () => {
157
- ensureGoogleFontsLoaded(instanceId, []);
158
- };
159
- }, [instanceId]);
160
-
161
- useImperativeHandle(
162
- forwardedRef,
163
- () => ({
164
- play: () => store.getState().play(),
165
- stop: () => store.getState().stop(),
166
- undo: () => store.getState().undo(),
167
- redo: () => store.getState().redo(),
168
- getDeck: () => store.getState().deck,
169
- isDirty: () => dirtyRef.current,
170
- resetDirty: () => {
171
- savedDeckRef.current = store.getState().deck;
172
- if (dirtyRef.current) {
173
- dirtyRef.current = false;
174
- onDirtyChangeRef.current?.(false);
175
- }
176
- },
177
- }),
178
- [store]
179
- );
180
-
181
- // Wrap the host save callback so a successful save resets the dirty flag.
182
- const handleSave = onSave
183
- ? async (d: Deck) => {
184
- await onSave(d);
185
- savedDeckRef.current = d;
186
- if (dirtyRef.current) {
187
- dirtyRef.current = false;
188
- onDirtyChangeRef.current?.(false);
189
- }
190
- }
191
- : undefined;
192
-
193
- const rootStyle: CSSProperties = {
194
- width: "100%",
195
- height: "100%",
196
- ...(fontFamily ? { ["--font-geist-sans" as string]: fontFamily } : null),
197
- ...style,
92
+ >(function SlidewiseEditor(
93
+ {
94
+ deck,
95
+ onChange,
96
+ onSave,
97
+ onExport,
98
+ onDirtyChange,
99
+ onHistoryChange,
100
+ readOnly,
101
+ theme,
102
+ initialSlideId,
103
+ showTopBar = true,
104
+ showBottomToolbar = true,
105
+ fontFamily,
106
+ icons,
107
+ className,
108
+ style,
109
+ },
110
+ ref
111
+ ) {
112
+ const rootProps: SlidewiseRootProps = {
113
+ deck,
114
+ onChange,
115
+ onSave,
116
+ onExport,
117
+ onDirtyChange,
118
+ onHistoryChange,
119
+ readOnly,
120
+ theme,
121
+ initialSlideId,
122
+ fontFamily,
123
+ icons,
124
+ className,
125
+ style,
198
126
  };
199
127
 
200
128
  return (
201
- <div
202
- className={className ? `slidewise-editor-host ${className}` : "slidewise-editor-host"}
203
- style={rootStyle}
204
- >
205
- <Editor
206
- showTopBar={showTopBar !== false}
207
- onSave={handleSave}
208
- onExport={onExport}
209
- />
210
- </div>
129
+ <Root {...rootProps} ref={ref}>
130
+ {showTopBar && <TopBar />}
131
+ <Body>
132
+ <SlideRail />
133
+ <CanvasFrame>
134
+ <Canvas />
135
+ {showBottomToolbar && <BottomToolbar />}
136
+ </CanvasFrame>
137
+ </Body>
138
+ </Root>
211
139
  );
212
- }
140
+ });
213
141
 
214
142
  export default SlidewiseEditor;