@turnix-co/konva-editor 2.0.24 → 2.0.27

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.
package/README.md CHANGED
@@ -1,31 +1,66 @@
1
1
  # @turnix-co/konva-editor
2
2
 
3
- A powerful multi-slide canvas editor built with React and Konva. Create interactive presentations, whiteboards, and educational content with drawing, media, and recording capabilities.
3
+ A powerful, private multi-slide canvas editor built with React, Konva, and Redux. Features drawing tools, media support, recording capabilities, and more.
4
4
 
5
- [![npm version](https://badge.fury.io/js/%40turnix-co%2Fkonva-editor.svg)](https://www.npmjs.com/package/@turnix-co/konva-editor)
5
+ ## 🔒 Private Package
6
6
 
7
- ## Installation
7
+ This package is **private** and only available to authorized Turnix team members via GitHub Packages.
8
8
 
9
- ```bash
10
- npm install @turnix-co/konva-editor
11
- ```
9
+ ## 📦 Installation
12
10
 
13
- ### Peer Dependencies
11
+ ### For Team Members
14
12
 
15
- Install the required peer dependencies:
13
+ **Step 1:** Install the package from GitHub Packages:
14
+ ```bash
15
+ npm install @turnix-co/konva-editor --registry=https://npm.pkg.github.com --legacy-peer-deps
16
+ ```
16
17
 
18
+ **Step 2:** Install peer dependencies:
17
19
  ```bash
18
20
  npm install konva react-konva react-konva-utils
19
21
  ```
20
22
 
21
- > **Note:** Konva has platform-specific code (browser vs Node.js). Installing it separately ensures Next.js correctly resolves the browser version and avoids SSR errors.
23
+ **Why separate?** Konva has platform-specific code (browser vs Node.js). Installing it separately ensures Next.js correctly resolves the browser version and avoids SSR errors.
24
+
25
+ ### Setup Authentication
26
+
27
+ Contact your team lead for authentication credentials (.npmrc file).
28
+
29
+ ## ⚠️ CRITICAL SETUP REQUIREMENTS
30
+
31
+ **Before you start**, understand these requirements or features WON'T work:
32
+
33
+ 1. **Layout Container Required**: Components MUST be wrapped in a div with:
34
+ - `width: '100vw'` and `height: '100vh'`
35
+ - `display: 'flex'` and `flexDirection: 'column'`
36
+ - Canvas wrapper needs `flex: 1` and `position: 'relative'`
37
+
38
+ 2. **stageRef Required**: For video recording, you MUST:
39
+ - Create a `stageRef` with `useRef<Konva.Stage | null>(null)`
40
+ - Pass it to both `<Toolbar stageRef={stageRef} />` and `<Canvas onStageReady={...} />`
41
+
42
+ 3. **CSS Import Required**: Import the package's CSS in your root layout
43
+
44
+ ❌ **Common Mistakes** (these will break video recording):
45
+ ```tsx
46
+ // ❌ WRONG - No layout wrapper, no stageRef
47
+ <ReduxProvider store={store}>
48
+ <Toolbar />
49
+ <Canvas />
50
+ <SlideNavigation />
51
+ </ReduxProvider>
52
+ ```
53
+
54
+ ✅ **Correct Setup** (see Quick Start below)
22
55
 
23
- ## Quick Start
56
+ ## 🚀 Quick Start
24
57
 
25
58
  ### 1. Import the CSS styles
26
59
 
27
60
  **IMPORTANT:** You must import the package's CSS file for components to display properly.
28
61
 
62
+ In your root layout or main CSS file (e.g., `app/layout.tsx` or `app/globals.css`):
63
+
29
64
  ```tsx
30
65
  // In your layout.tsx or _app.tsx
31
66
  import '@turnix-co/konva-editor/styles.css';
@@ -36,7 +71,81 @@ Or in your CSS file:
36
71
  @import '@turnix-co/konva-editor/styles.css';
37
72
  ```
38
73
 
39
- ### 2. Create your editor page
74
+ ### 2. Create your editor page with auto-save
75
+
76
+ **IMPORTANT:** To enable IndexedDB persistence (auto-save), you must call the `useSlidesPersistence()` hook.
77
+
78
+ **CRITICAL FOR VIDEO RECORDING:** You MUST wrap components in a properly styled container for video recording to work correctly!
79
+
80
+ ```tsx
81
+ 'use client';
82
+
83
+ import { useRef } from 'react';
84
+ import {
85
+ Canvas,
86
+ Toolbar,
87
+ SlideNavigation,
88
+ ReduxProvider,
89
+ store,
90
+ useSlidesPersistence
91
+ } from '@turnix-co/konva-editor';
92
+ import '@turnix-co/konva-editor/styles.css';
93
+ import type Konva from 'konva';
94
+
95
+ // Wrapper component to load persisted slides
96
+ function PersistenceLoader({ children }: { children: React.ReactNode }) {
97
+ useSlidesPersistence(); // ← This loads slides from IndexedDB on mount
98
+ return <>{children}</>;
99
+ }
100
+
101
+ export default function EditorPage() {
102
+ const stageRef = useRef<Konva.Stage | null>(null);
103
+
104
+ return (
105
+ <ReduxProvider store={store}>
106
+ <PersistenceLoader>
107
+ {/* CRITICAL: This wrapper is REQUIRED for video recording to work */}
108
+ <div style={{
109
+ width: '100vw',
110
+ height: '100vh',
111
+ display: 'flex',
112
+ flexDirection: 'column',
113
+ overflow: 'hidden' // Prevents scrollbars
114
+ }}>
115
+ <Toolbar stageRef={stageRef} />
116
+
117
+ {/* CRITICAL: Canvas wrapper needs flex: 1 and position: relative */}
118
+ <div style={{ flex: 1, position: 'relative' }}>
119
+ <Canvas onStageReady={(ref) => { stageRef.current = ref.current; }} />
120
+ </div>
121
+
122
+ <SlideNavigation />
123
+ </div>
124
+ </PersistenceLoader>
125
+ </ReduxProvider>
126
+ );
127
+ }
128
+ ```
129
+
130
+ **Important:**
131
+ - Always add `'use client'` at the top!
132
+ - Call `useSlidesPersistence()` inside `ReduxProvider` to enable auto-save
133
+ - **MUST** have the wrapper div with `width: '100vw'` and `height: '100vh'`
134
+ - **MUST** pass `stageRef` to both Toolbar and Canvas for recording to work
135
+
136
+ ### 3. Add Publishing (Optional)
137
+
138
+ The package includes **automatic data persistence via IndexedDB + Redux**:
139
+ - ✅ **Auto-save**: Slides are automatically saved to browser storage on every change
140
+ - ✅ **Auto-restore**: Slides are automatically loaded when the page refreshes
141
+ - ✅ **No backend needed**: Data persists in the user's browser
142
+
143
+ **How it works:**
144
+ 1. `useSlidesPersistence()` hook loads slides from IndexedDB on mount
145
+ 2. Redux middleware automatically saves changes to IndexedDB (500ms debounce)
146
+ 3. Works completely offline - no server required!
147
+
148
+ If you want to publish slides to your backend:
40
149
 
41
150
  ```tsx
42
151
  'use client';
@@ -46,144 +155,125 @@ import {
46
155
  Canvas,
47
156
  Toolbar,
48
157
  SlideNavigation,
49
- TopNavBar,
50
- BottomToolbar,
51
158
  PublishButton,
52
- EditorRoot,
53
159
  ReduxProvider,
54
160
  store,
55
161
  useSlidesPersistence,
56
- type Slide,
57
162
  type PublishProgress,
58
163
  type PublishResponse,
164
+ type Slide
59
165
  } from '@turnix-co/konva-editor';
60
166
  import '@turnix-co/konva-editor/styles.css';
61
167
  import type Konva from 'konva';
62
168
 
63
- // Wrapper component to load persisted slides
169
+ // Wrapper component for persistence
64
170
  function PersistenceLoader({ children }: { children: React.ReactNode }) {
65
- useSlidesPersistence(); // Loads slides from IndexedDB on mount
171
+ useSlidesPersistence();
66
172
  return <>{children}</>;
67
173
  }
68
174
 
69
175
  export default function EditorPage() {
70
176
  const stageRef = useRef<Konva.Stage | null>(null);
71
177
 
178
+ // Your custom publish logic
72
179
  const handlePublish = async (
73
180
  slides: Slide[],
74
181
  onProgress?: (progress: PublishProgress) => void
75
182
  ): Promise<PublishResponse> => {
76
- // Your publish logic here
77
- return { success: true, message: 'Published!' };
183
+ // Update progress
184
+ onProgress?.({
185
+ current: 0,
186
+ total: slides.length,
187
+ status: 'Preparing slides...'
188
+ });
189
+
190
+ // Call your API endpoint
191
+ const response = await fetch('/api/publish', {
192
+ method: 'POST',
193
+ headers: { 'Content-Type': 'application/json' },
194
+ body: JSON.stringify({ slides })
195
+ });
196
+
197
+ const data = await response.json();
198
+
199
+ onProgress?.({
200
+ current: slides.length,
201
+ total: slides.length,
202
+ status: 'Published successfully!'
203
+ });
204
+
205
+ return {
206
+ success: true,
207
+ message: 'Slides published successfully!',
208
+ projectId: data.id,
209
+ url: data.url
210
+ };
78
211
  };
79
212
 
80
213
  return (
81
214
  <ReduxProvider store={store}>
82
215
  <PersistenceLoader>
83
- <EditorRoot
84
- style={{
85
- width: '100vw',
86
- height: '100vh',
87
- display: 'flex',
88
- flexDirection: 'column',
89
- overflow: 'hidden',
90
- }}
91
- >
92
- {/* Header with title and publish button */}
93
- <TopNavBar
94
- title="My Lesson"
95
- autoSaveMessage="Auto-saved just now"
96
- onBack={() => window.history.back()}
97
- onPublish={handlePublish}
98
- />
99
-
100
- {/* Main toolbar */}
216
+ {/* CRITICAL: This wrapper is REQUIRED */}
217
+ <div style={{
218
+ width: '100vw',
219
+ height: '100vh',
220
+ display: 'flex',
221
+ flexDirection: 'column',
222
+ overflow: 'hidden'
223
+ }}>
101
224
  <Toolbar stageRef={stageRef} />
102
225
 
103
- {/* Canvas area */}
104
226
  <div style={{ flex: 1, position: 'relative' }}>
105
227
  <Canvas onStageReady={(ref) => { stageRef.current = ref.current; }} />
106
-
107
- {/* Bottom toolbar for pen size and colors (shows when drawing) */}
108
- <BottomToolbar />
109
228
  </div>
110
229
 
111
- {/* Slide navigation */}
112
230
  <SlideNavigation />
113
- </EditorRoot>
231
+
232
+ {/* Add PublishButton with your custom publish function */}
233
+ <PublishButton
234
+ onPublish={handlePublish}
235
+ label="Save to Cloud"
236
+ />
237
+ </div>
114
238
  </PersistenceLoader>
115
239
  </ReduxProvider>
116
240
  );
117
241
  }
118
242
  ```
119
243
 
120
- ## Features
121
-
122
- - **Multi-slide Support** - Create presentations with up to 20 slides
123
- - **Freehand Drawing** - Smooth pen and brush tools with customizable colors and sizes
124
- - **Shapes** - Rectangle, circle, triangle, arrow, and line shapes
125
- - **Text** - Rich text with formatting options
126
- - **Images** - Drag & drop, resize, rotate with context menu
127
- - **Videos** - Upload and playback with thumbnail previews
128
- - **Flashcards** - Interactive flashcard elements
129
- - **Photo Frames** - Decorative frames with annotation support
130
- - **Quiz Elements** - Multiple choice, true/false, short answer, long answer, fill-in-the-blanks
131
- - **Audio Recording** - Record voice notes for individual objects
132
- - **Screen Recording** - Record entire canvas with audio
133
- - **Undo/Redo** - Full history management
134
- - **Auto-save** - Automatic persistence with IndexedDB + Redux
135
- - **Export** - Export slides as images
136
-
137
- ## Components
244
+ ## 📚 Components
138
245
 
139
246
  ### Core Components
140
247
 
141
- | Component | Description |
142
- |-----------|-------------|
143
- | `Canvas` | Main canvas component with drawing, images, videos, and text |
144
- | `Toolbar` | Toolbar with drawing tools, colors, and actions |
145
- | `SlideNavigation` | Multi-slide navigation and management |
146
- | `TopNavBar` | Header with title, auto-save status, and action buttons |
147
- | `BottomToolbar` | Pen size and color picker toolbar |
148
- | `LayersPanel` | Layer management panel for reordering elements |
149
- | `ScreenRecorder` | Screen recording functionality |
150
- | `PublishButton` | Customizable publish button with progress tracking |
151
- | `EditorRoot` | Root wrapper that scopes styles (required for styling) |
248
+ - **`<Canvas />`** - Main canvas component with drawing, images, videos, and text
249
+ - **`<Toolbar />`** - Toolbar with drawing tools, colors, and actions
250
+ - **`<SlideNavigation />`** - Multi-slide navigation and management
251
+ - **`<ScreenRecorder />`** - Screen recording functionality
252
+ - **`<PublishButton />`** - Customizable publish button with progress tracking
152
253
 
153
254
  ### Redux Setup
154
255
 
155
- | Export | Description |
156
- |--------|-------------|
157
- | `ReduxProvider` | Redux provider (from react-redux) |
158
- | `store` | Pre-configured Redux store |
159
- | `useDispatch` | Typed dispatch hook |
160
- | `useSelector` | Typed selector hook |
256
+ - **`ReduxProvider`** - Redux provider (from react-redux)
257
+ - **`store`** - Pre-configured Redux store
161
258
 
162
- ## Critical Setup Requirements
259
+ ## 🎥 Using the Recording Feature
163
260
 
164
- For all features to work correctly, ensure:
165
-
166
- 1. **Layout Container**: Components MUST be wrapped in a div with:
167
- - `width: '100vw'` and `height: '100vh'`
168
- - `display: 'flex'` and `flexDirection: 'column'`
169
- - Canvas wrapper needs `flex: 1` and `position: 'relative'`
170
-
171
- 2. **stageRef Required for Recording**: You MUST:
172
- - Create a `stageRef` with `useRef<Konva.Stage | null>(null)`
173
- - Pass it to both `<Toolbar stageRef={stageRef} />` and `<Canvas onStageReady={...} />`
174
-
175
- 3. **CSS Import Required**: Import the package's CSS in your root layout
176
-
177
- ## Recording Feature
178
-
179
- The Toolbar includes a built-in recording button. To enable it:
261
+ The Toolbar component includes a built-in recording button. To enable it, you need to pass the stage reference from the Canvas component to the Toolbar:
180
262
 
181
263
  ```tsx
182
264
  'use client';
183
265
 
184
- import { useRef } from 'react';
185
- import { Canvas, Toolbar, SlideNavigation, ReduxProvider, store } from '@turnix-co/konva-editor';
186
- import type Konva from 'konva';
266
+ import { useState, useRef } from 'react';
267
+ import {
268
+ Canvas,
269
+ Toolbar,
270
+ SlideNavigation,
271
+ ReduxProvider,
272
+ store,
273
+ type CanvasProps,
274
+ type ToolbarProps
275
+ } from '@turnix-co/konva-editor';
276
+ import Konva from 'konva';
187
277
 
188
278
  export default function EditorPage() {
189
279
  const stageRef = useRef<Konva.Stage | null>(null);
@@ -191,10 +281,18 @@ export default function EditorPage() {
191
281
  return (
192
282
  <ReduxProvider store={store}>
193
283
  <div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'column' }}>
284
+ {/* Pass stageRef to Toolbar to enable recording */}
194
285
  <Toolbar stageRef={stageRef} />
286
+
195
287
  <div style={{ flex: 1, position: 'relative' }}>
196
- <Canvas onStageReady={(ref) => { stageRef.current = ref.current; }} />
288
+ {/* Canvas provides the stageRef via onStageReady callback */}
289
+ <Canvas
290
+ onStageReady={(ref) => {
291
+ stageRef.current = ref.current;
292
+ }}
293
+ />
197
294
  </div>
295
+
198
296
  <SlideNavigation />
199
297
  </div>
200
298
  </ReduxProvider>
@@ -202,213 +300,259 @@ export default function EditorPage() {
202
300
  }
203
301
  ```
204
302
 
205
- ## Publishing
206
-
207
- ### Auto-save (Built-in)
208
-
209
- The package includes automatic data persistence via IndexedDB + Redux:
210
- - Slides are automatically saved to browser storage on every change
211
- - Slides are automatically loaded when the page refreshes
212
- - Works completely offline - no server required
213
-
214
- ### Custom Publishing
303
+ **Key points:**
304
+ 1. Create a `stageRef` using `useRef<Konva.Stage | null>(null)`
305
+ 2. Pass the `stageRef` to the `<Toolbar>` component
306
+ 3. Use the `onStageReady` callback on `<Canvas>` to populate the stageRef
307
+ 4. **IMPORTANT**: Wrap Canvas in a container with `flex: 1` and `position: 'relative'`
308
+ 5. **IMPORTANT**: The parent container MUST have `width: '100vw'` and `height: '100vh'` for recorded videos to display in full screen
309
+ 6. The recording button in the Toolbar will now work correctly
215
310
 
216
- Add a publish button to save to your backend:
311
+ Alternatively, you can handle recording externally:
217
312
 
218
313
  ```tsx
314
+ 'use client';
315
+
316
+ import { useState, useRef } from 'react';
219
317
  import {
220
- PublishButton,
221
- type PublishProgress,
222
- type PublishResponse,
223
- type Slide
318
+ Canvas,
319
+ Toolbar,
320
+ ScreenRecorder,
321
+ ReduxProvider,
322
+ store
224
323
  } from '@turnix-co/konva-editor';
324
+ import Konva from 'konva';
325
+
326
+ export default function EditorPage() {
327
+ const stageRef = useRef<Konva.Stage | null>(null);
328
+ const [showRecorder, setShowRecorder] = useState(false);
225
329
 
226
- const handlePublish = async (
227
- slides: Slide[],
228
- onProgress?: (progress: PublishProgress) => void
229
- ): Promise<PublishResponse> => {
230
- onProgress?.({ current: 0, total: slides.length, status: 'Preparing...' });
330
+ const handleRecordingComplete = (videoBlob: Blob, thumbnailDataUrl: string) => {
331
+ // Handle the recorded video
332
+ console.log('Recording completed', videoBlob);
333
+ };
231
334
 
232
- const response = await fetch('/api/publish', {
233
- method: 'POST',
234
- headers: { 'Content-Type': 'application/json' },
235
- body: JSON.stringify({ slides })
236
- });
335
+ return (
336
+ <ReduxProvider store={store}>
337
+ <div style={{ width: '100vw', height: '100vh' }}>
338
+ {/* Toolbar with custom recording handler */}
339
+ <Toolbar
340
+ onScreenRecord={() => setShowRecorder(true)}
341
+ />
342
+
343
+ <Canvas
344
+ onStageReady={(ref) => {
345
+ stageRef.current = ref.current;
346
+ }}
347
+ />
348
+
349
+ {/* External ScreenRecorder component */}
350
+ {showRecorder && (
351
+ <ScreenRecorder
352
+ onClose={() => setShowRecorder(false)}
353
+ stageRef={stageRef}
354
+ onRecordingComplete={handleRecordingComplete}
355
+ />
356
+ )}
357
+ </div>
358
+ </ReduxProvider>
359
+ );
360
+ }
361
+ ```
237
362
 
238
- const data = await response.json();
363
+ ## 🎨 Features
239
364
 
240
- return {
241
- success: true,
242
- message: 'Published successfully!',
243
- projectId: data.id,
244
- url: data.url
365
+ - ✏️ **Drawing tools** - Pen and eraser with customizable colors and sizes
366
+ - 🖼️ **Image support** - Drag & drop, resize, rotate with context menu
367
+ - 🎥 **Video support** - Upload and playback with thumbnail previews
368
+ - 📝 **Text elements** - Add and edit text on canvas
369
+ - 🎙️ **Audio recording** - Record voice notes for individual objects
370
+ - 📹 **Screen recording** - Record entire canvas with audio
371
+ - 📊 **Multi-slide presentations** - Create up to 20 slides
372
+ - ↩️ **Undo/Redo** - Full history management
373
+ - 💾 **Auto-save** - Automatic persistence with IndexedDB + Redux
374
+ - 📤 **Export** - Export slides as images
375
+ - 🚀 **Publishing** - Customizable publish button with progress tracking
376
+
377
+ ## 🔧 Advanced Usage
378
+
379
+ ### Custom Actions
380
+
381
+ ```tsx
382
+ import { useDispatch, addImage, setTool } from '@turnix/konva-editor';
383
+
384
+ function CustomControls() {
385
+ const dispatch = useDispatch();
386
+
387
+ const handleAddImage = () => {
388
+ dispatch(addImage({
389
+ id: 'custom-id',
390
+ src: 'https://example.com/image.jpg',
391
+ x: 100,
392
+ y: 100,
393
+ width: 200,
394
+ height: 200,
395
+ }));
245
396
  };
246
- };
247
397
 
248
- // In your component:
249
- <PublishButton onPublish={handlePublish} label="Save to Cloud" />
398
+ return <button onClick={handleAddImage}>Add Image</button>;
399
+ }
250
400
  ```
251
401
 
252
- ## Redux Actions
402
+ ### TypeScript Support
253
403
 
254
- ### Slide Management
255
- - `addSlide()` - Create new slide
256
- - `deleteSlide(id)` - Delete slide
257
- - `setCurrentSlide(id)` - Switch to slide
258
- - `duplicateSlide(id)` - Duplicate slide
259
- - `reorderSlides(payload)` - Reorder slides
260
- - `setBackgroundColor(color)` - Set slide background
261
- - `loadSlides(slides)` - Load slides from data
262
-
263
- ### Elements
264
- - `addText`, `updateText`, `deleteText`, `duplicateText`
265
- - `addShape`, `updateShape`, `deleteShape`, `duplicateShape`
266
- - `addImage`, `updateImage`, `deleteImage`, `duplicateImage`
267
- - `addVideo`, `updateVideo`, `deleteVideo`, `duplicateVideo`
268
- - `addFlashcard`, `updateFlashcard`, `deleteFlashcard`
269
- - `addPhotoFrame`, `updatePhotoFrame`, `deletePhotoFrame`
270
- - `addMultipleChoice`, `addTrueFalse`, `addShortAnswer`, `addLongAnswer`, `addFillInTheBlanks`
271
-
272
- ### Drawing
273
- - `addLine`, `removeLine`, `updateLastLine`
274
- - `setLines`, `deleteLineById`, `duplicateLine`
275
-
276
- ### Canvas Operations
277
- - `clearCanvas()` - Clear current slide
278
- - `undo()` - Undo last action
279
- - `redo()` - Redo undone action
280
- - `bringToFront(id)`, `sendToBack(id)` - Layer ordering
404
+ Full TypeScript support with exported types:
281
405
 
282
- ### Toolbar Actions
283
- - `setTool(tool)` - Set active tool ('pen' | 'eraser' | 'text')
284
- - `setPenColor(color)` - Set pen color
285
- - `setStrokeWidth(width)` - Set stroke width
406
+ ```tsx
407
+ import type { RootState, ImageElement, CanvasState } from '@turnix/konva-editor';
408
+ ```
409
+
410
+ ## 📖 API Reference
286
411
 
287
- ### Selectors
288
- - `selectCurrentSlide` - Get current slide
289
- - `selectAllSlides` - Get all slides
290
- - `selectCurrentSlideId` - Get current slide ID
291
- - `selectCanAddSlide` - Check if can add more slides
292
- - `selectSlideById(id)` - Get slide by ID
412
+ ### PublishButton Props
293
413
 
294
- ## Utilities
414
+ ```tsx
415
+ interface PublishButtonProps {
416
+ // Custom publish function (required)
417
+ onPublish?: (
418
+ slides: Slide[],
419
+ onProgress?: (progress: PublishProgress) => void
420
+ ) => Promise<PublishResponse>;
295
421
 
296
- ### Export Slides
422
+ // Custom button label (optional, default: "Publish Slides")
423
+ label?: string;
297
424
 
425
+ // Custom className for positioning/styling (optional)
426
+ className?: string;
427
+ }
428
+
429
+ interface PublishProgress {
430
+ current: number; // Current progress (e.g., 5)
431
+ total: number; // Total items (e.g., 10)
432
+ status: string; // Status message (e.g., "Uploading...")
433
+ }
434
+
435
+ interface PublishResponse {
436
+ success: boolean; // Whether publish succeeded
437
+ message: string; // Success/error message
438
+ projectId?: string; // Optional: ID from your backend
439
+ url?: string; // Optional: URL to published project
440
+ }
441
+ ```
442
+
443
+ **Example API Endpoint** (`app/api/publish/route.ts`):
298
444
  ```tsx
299
- import { exportSlideAsImage, exportSlideAsBlob, getSlideDataURL } from '@turnix-co/konva-editor';
445
+ import { NextRequest, NextResponse } from 'next/server';
300
446
 
301
- // Export as downloadable image
302
- await exportSlideAsImage(stageRef, { pixelRatio: 2 });
447
+ export async function POST(request: NextRequest) {
448
+ const { slides } = await request.json();
303
449
 
304
- // Get as Blob for upload
305
- const blob = await exportSlideAsBlob(stageRef, { format: 'png' });
450
+ // Save to your database
451
+ const project = await db.project.create({
452
+ data: {
453
+ slides: JSON.stringify(slides),
454
+ createdAt: new Date(),
455
+ }
456
+ });
306
457
 
307
- // Get as data URL
308
- const dataUrl = await getSlideDataURL(stageRef);
458
+ return NextResponse.json({
459
+ id: project.id,
460
+ url: `/projects/${project.id}`,
461
+ });
462
+ }
309
463
  ```
310
464
 
311
- ### Line Simplification
465
+ ### Redux Actions
466
+
467
+ **Canvas Actions:**
468
+ - `addImage(payload)` - Add image to canvas
469
+ - `addVideo(payload)` - Add video to canvas
470
+ - `addText(payload)` - Add text to canvas
471
+ - `clearCanvas()` - Clear current slide
472
+ - `undo()` - Undo last action
473
+ - `redo()` - Redo undone action
474
+ - `deleteElement(id)` - Delete element by ID
475
+
476
+ **Slide Actions:**
477
+ - `addSlide()` - Create new slide
478
+ - `deleteSlide(id)` - Delete slide
479
+ - `setCurrentSlide(id)` - Switch to slide
480
+ - `duplicateSlide(id)` - Duplicate slide
312
481
 
313
- Reduce payload size by simplifying freehand lines:
482
+ **Toolbar Actions:**
483
+ - `setTool(tool)` - Set active tool ('pen' | 'eraser' | 'text')
484
+ - `setPenColor(color)` - Set pen color
485
+ - `setStrokeWidth(width)` - Set stroke width
314
486
 
315
- ```tsx
316
- import { simplifyLines, getCompressionStats } from '@turnix-co/konva-editor';
487
+ ### Utilities
317
488
 
318
- // Simplify all lines with tolerance (higher = more compression)
319
- const simplified = simplifyLines(lines, 3.0);
489
+ - `exportSlideAsImage(slideId)` - Export slide as PNG
490
+ - `exportSlideAsPDF(slideId)` - Export slide as PDF
320
491
 
321
- // Get compression statistics
322
- const stats = getCompressionStats(originalPoints, simplifiedPoints);
323
- console.log(`Reduced by ${stats.reduction}`);
324
- ```
492
+ ## 🏗️ Architecture
325
493
 
326
- ## TypeScript Support
494
+ The package uses:
495
+ - **React 19** for UI
496
+ - **Konva** for canvas rendering
497
+ - **Redux Toolkit** for state management
498
+ - **IndexedDB** for persistence
499
+ - **TypeScript** for type safety
327
500
 
328
- Full TypeScript support with exported types:
501
+ ## 🔐 Security
329
502
 
330
- ```tsx
331
- import type {
332
- Slide,
333
- Line,
334
- TextElement,
335
- Shape,
336
- ImageElement,
337
- VideoElement,
338
- FlashcardElement,
339
- PhotoFrameElement,
340
- MultipleChoice,
341
- TrueFalse,
342
- ShortAnswer,
343
- LongAnswer,
344
- FillInTheBlanks,
345
- CanvasProps,
346
- ToolbarProps,
347
- EditorRootProps,
348
- TopNavBarProps,
349
- BottomToolbarProps,
350
- ExportOptions,
351
- PublishProgress,
352
- PublishResponse,
353
- RootState,
354
- AppDispatch,
355
- } from '@turnix-co/konva-editor';
356
- ```
503
+ ⚠️ **This is a private package.** Do not:
504
+ - Share authentication credentials
505
+ - Publish package code publicly
506
+ - Include in public repositories
507
+
508
+ ## 📝 License
509
+
510
+ UNLICENSED - Private and proprietary software for Turnix team use only.
357
511
 
358
- ## Component Props
359
-
360
- ### TopNavBar
361
-
362
- | Prop | Type | Default | Description |
363
- |------|------|---------|-------------|
364
- | `title` | `string` | `'Untitled'` | Title displayed in the header |
365
- | `autoSaveMessage` | `string` | `'Auto-saved just now'` | Auto-save status text |
366
- | `onBack` | `() => void` | - | Callback when back button clicked |
367
- | `onSettings` | `() => void` | - | Callback when settings button clicked |
368
- | `onProfile` | `() => void` | - | Callback when profile button clicked |
369
- | `onPublish` | `PublishButtonProps['onPublish']` | - | Publish function (shows publish button if provided) |
370
- | `showBackButton` | `boolean` | `true` | Show/hide back button |
371
- | `showSettingsButton` | `boolean` | `true` | Show/hide settings button |
372
- | `showProfileButton` | `boolean` | `true` | Show/hide profile button |
373
- | `showPublishButton` | `boolean` | `true` | Show/hide publish button |
374
- | `leftContent` | `React.ReactNode` | - | Custom left side content |
375
- | `rightContent` | `React.ReactNode` | - | Custom right side content |
376
- | `className` | `string` | - | Additional CSS classes |
377
-
378
- ### BottomToolbar
379
-
380
- | Prop | Type | Default | Description |
381
- |------|------|---------|-------------|
382
- | `colors` | `Array<{name: string, value: string}>` | Default colors | Custom color palette |
383
- | `showSizeSlider` | `boolean` | `true` | Show/hide pen size slider |
384
- | `showColorPicker` | `boolean` | `true` | Show/hide color picker |
385
- | `showBackgroundPicker` | `boolean` | `true` | Show/hide background color picker |
386
- | `className` | `string` | - | Additional CSS classes |
387
-
388
- ## Troubleshooting
512
+ ## 🆘 Support
513
+
514
+ For issues or questions, contact the Turnix development team.
515
+
516
+ ## 🐛 Troubleshooting
389
517
 
390
518
  ### Recorded Video Not Displaying Full Screen
391
519
 
392
- 1. **Check Container Styling**: Ensure Canvas is wrapped in a container with `width: '100vw'` and `height: '100vh'`
393
- 2. **Verify stageRef**: Make sure `stageRef` is passed to both Toolbar and Canvas
394
- 3. **Use flex: 1**: Canvas container should use `flex: 1` instead of fixed dimensions
520
+ If your recorded video doesn't display in full screen when played:
521
+
522
+ 1. **Check Container Styling**: Ensure the Canvas is wrapped in a container that fills the viewport:
523
+ ```tsx
524
+ <div style={{ width: '100vw', height: '100vh', display: 'flex', flexDirection: 'column' }}>
525
+ <Toolbar stageRef={stageRef} />
526
+ <div style={{ flex: 1, position: 'relative' }}>
527
+ <Canvas onStageReady={(ref) => stageRef.current = ref.current} />
528
+ </div>
529
+ </div>
530
+ ```
395
531
 
396
- ### Styles Not Applied
532
+ 2. **Verify stageRef is Passed**: Make sure you're passing the `stageRef` to the Toolbar component:
533
+ ```tsx
534
+ <Toolbar stageRef={stageRef} />
535
+ ```
397
536
 
398
- 1. Make sure you imported `@turnix-co/konva-editor/styles.css`
399
- 2. Wrap components in `EditorRoot` if you have CSS conflicts
537
+ 3. **Check onStageReady Callback**: Ensure the Canvas component's `onStageReady` callback is properly setting the stageRef:
538
+ ```tsx
539
+ <Canvas onStageReady={(ref) => { stageRef.current = ref.current; }} />
540
+ ```
400
541
 
401
- ### SSR Errors
542
+ 4. **No Fixed Dimensions**: Avoid setting fixed width/height on the Canvas container. Use `flex: 1` instead to allow it to fill available space.
402
543
 
403
- If you see SSR errors with Konva:
404
- 1. Make sure your component has `'use client'` directive
405
- 2. Install Konva peer dependencies separately: `npm install konva react-konva react-konva-utils`
544
+ ### Download and Re-record Buttons Not Visible
406
545
 
407
- ## License
546
+ After recording stops, you should see a preview dialog with three buttons:
547
+ - **Add to Canvas** - Adds the video to the canvas (clears existing content)
548
+ - **Download** - Downloads the video file locally
549
+ - **Re-record** - Discards the recording and starts over
408
550
 
409
- UNLICENSED - Proprietary software by Turnix
551
+ If you don't see these buttons, ensure you're using version `1.3.3` or later:
552
+ ```bash
553
+ npm install @turnix-co/konva-editor@latest --registry=https://npm.pkg.github.com
554
+ ```
410
555
 
411
- ## Support
556
+ ---
412
557
 
413
- For issues and feature requests, please visit:
414
- https://github.com/turnix-co/schoopla-konva-editor/issues
558
+ **Built with ❤️ by the Turnix team**