rita-workspace 0.3.1 → 0.4.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.
- package/README.md +89 -40
- package/dist/index.d.mts +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +123 -12
- package/dist/index.mjs +121 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -7,7 +7,9 @@ Multi-drawing workspace feature for Rita (Excalidraw fork based on B310-digital/
|
|
|
7
7
|
- **Multiple drawings** - Create and manage multiple drawings in one workspace
|
|
8
8
|
- **Menu integration** - Seamlessly integrates with Excalidraw's hamburger menu
|
|
9
9
|
- **Auto-save** - All drawings saved locally in IndexedDB
|
|
10
|
+
- **Auto-sync** - Automatic sync between workspace and Excalidraw canvas
|
|
10
11
|
- **Rename & delete** - Full drawing management via dialog
|
|
12
|
+
- **i18n support** - Swedish and English with automatic Excalidraw language sync
|
|
11
13
|
|
|
12
14
|
## Installation
|
|
13
15
|
|
|
@@ -19,99 +21,127 @@ yarn add rita-workspace
|
|
|
19
21
|
|
|
20
22
|
## Integration Guide
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
Three files need to be modified in the B310/Excalidraw fork:
|
|
23
25
|
|
|
24
|
-
### 1. `excalidraw-app/App.tsx`
|
|
26
|
+
### 1. `excalidraw-app/App.tsx` - Add Provider and Bridge
|
|
25
27
|
|
|
26
|
-
**Add
|
|
28
|
+
**Add imports:**
|
|
27
29
|
|
|
28
30
|
```tsx
|
|
29
|
-
import { WorkspaceProvider } from "rita-workspace";
|
|
31
|
+
import { WorkspaceProvider, WorkspaceBridge } from "rita-workspace";
|
|
30
32
|
```
|
|
31
33
|
|
|
32
|
-
**Wrap with WorkspaceProvider
|
|
34
|
+
**Wrap ExcalidrawApp with WorkspaceProvider:**
|
|
33
35
|
|
|
34
36
|
```tsx
|
|
35
37
|
const ExcalidrawApp = () => {
|
|
36
|
-
// ...
|
|
37
38
|
return (
|
|
38
39
|
<TopErrorBoundary>
|
|
39
40
|
<Provider store={appJotaiStore}>
|
|
40
|
-
<WorkspaceProvider>
|
|
41
|
+
<WorkspaceProvider lang="sv"> {/* <-- Add this */}
|
|
41
42
|
<ExcalidrawWrapper />
|
|
42
|
-
</WorkspaceProvider>
|
|
43
|
+
</WorkspaceProvider> {/* <-- And this */}
|
|
43
44
|
</Provider>
|
|
44
45
|
</TopErrorBoundary>
|
|
45
46
|
);
|
|
46
47
|
};
|
|
47
48
|
```
|
|
48
49
|
|
|
49
|
-
|
|
50
|
+
**Add WorkspaceBridge inside ExcalidrawWrapper** (this syncs the canvas automatically):
|
|
50
51
|
|
|
51
|
-
|
|
52
|
+
```tsx
|
|
53
|
+
const ExcalidrawWrapper = () => {
|
|
54
|
+
const [excalidrawAPI, excalidrawRefCallback] =
|
|
55
|
+
useCallbackRefState<ExcalidrawImperativeAPI>();
|
|
56
|
+
|
|
57
|
+
// ... existing code ...
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<div style={{ height: "100%" }}>
|
|
61
|
+
{/* === ADD THIS - Auto-syncs workspace with Excalidraw === */}
|
|
62
|
+
<WorkspaceBridge excalidrawAPI={excalidrawAPI} />
|
|
63
|
+
|
|
64
|
+
<Excalidraw
|
|
65
|
+
excalidrawAPI={excalidrawRefCallback}
|
|
66
|
+
// ... rest of props ...
|
|
67
|
+
/>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 2. `excalidraw-app/components/AppMainMenu.tsx` - Add Menu Items
|
|
74
|
+
|
|
75
|
+
**Add imports:**
|
|
52
76
|
|
|
53
77
|
```tsx
|
|
54
|
-
import React, { useState } from "react";
|
|
78
|
+
import React, { useState } from "react";
|
|
55
79
|
|
|
56
|
-
// Add after other imports:
|
|
57
80
|
import { WorkspaceMenuItems, DrawingsDialog } from "rita-workspace";
|
|
81
|
+
import { LoadIcon } from "../components/icons"; // Excalidraw's folder icon
|
|
58
82
|
```
|
|
59
83
|
|
|
60
|
-
**Add state and menu items
|
|
84
|
+
**Add state and menu items:**
|
|
61
85
|
|
|
62
86
|
```tsx
|
|
63
87
|
export const AppMainMenu: React.FC<{...}> = React.memo((props) => {
|
|
64
|
-
const [showDrawingsDialog, setShowDrawingsDialog] = useState(false);
|
|
88
|
+
const [showDrawingsDialog, setShowDrawingsDialog] = useState(false);
|
|
65
89
|
|
|
66
90
|
return (
|
|
67
|
-
<>
|
|
91
|
+
<>
|
|
68
92
|
<MainMenu>
|
|
69
93
|
<MainMenu.DefaultItems.LoadScene />
|
|
70
94
|
<MainMenu.DefaultItems.SaveToActiveFile />
|
|
71
95
|
|
|
72
|
-
{/* === RITA WORKSPACE
|
|
96
|
+
{/* === RITA WORKSPACE === */}
|
|
73
97
|
<MainMenu.Sub>
|
|
74
|
-
<MainMenu.Sub.Trigger
|
|
98
|
+
<MainMenu.Sub.Trigger>{LoadIcon} Arbetsyta</MainMenu.Sub.Trigger>
|
|
75
99
|
<MainMenu.Sub.Content>
|
|
76
100
|
<WorkspaceMenuItems
|
|
77
101
|
onManageDrawings={() => setShowDrawingsDialog(true)}
|
|
78
102
|
/>
|
|
79
103
|
</MainMenu.Sub.Content>
|
|
80
104
|
</MainMenu.Sub>
|
|
81
|
-
{/* === END RITA WORKSPACE === */}
|
|
82
105
|
|
|
83
106
|
<MainMenu.DefaultItems.Export />
|
|
84
107
|
{/* ... rest of menu items ... */}
|
|
85
108
|
</MainMenu>
|
|
86
109
|
|
|
87
|
-
{/* === RITA WORKSPACE: Add dialog === */}
|
|
88
110
|
<DrawingsDialog
|
|
89
111
|
open={showDrawingsDialog}
|
|
90
112
|
onClose={() => setShowDrawingsDialog(false)}
|
|
91
113
|
/>
|
|
92
|
-
</>
|
|
114
|
+
</>
|
|
93
115
|
);
|
|
94
116
|
});
|
|
95
117
|
```
|
|
96
118
|
|
|
97
|
-
##
|
|
119
|
+
## How It Works
|
|
98
120
|
|
|
99
|
-
|
|
121
|
+
1. **WorkspaceProvider** - Manages workspace state (drawings list, active drawing)
|
|
122
|
+
2. **WorkspaceBridge** - Automatically syncs between workspace and Excalidraw:
|
|
123
|
+
- Loads drawing into canvas when you switch drawings
|
|
124
|
+
- Auto-saves canvas changes back to workspace
|
|
125
|
+
- Saves current drawing before switching to another
|
|
126
|
+
3. **WorkspaceMenuItems** - Provides the menu UI for switching drawings
|
|
127
|
+
4. **DrawingsDialog** - Full management UI (rename, delete, create)
|
|
100
128
|
|
|
129
|
+
## Language Support (i18n)
|
|
130
|
+
|
|
131
|
+
Pass Excalidraw's `langCode` to `WorkspaceProvider`:
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
const [langCode] = useAppLangCode();
|
|
135
|
+
|
|
136
|
+
<WorkspaceProvider lang={langCode}>
|
|
137
|
+
{/* All components automatically use the same language */}
|
|
138
|
+
</WorkspaceProvider>
|
|
101
139
|
```
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
│ ... │ └──│ ✓ Current drawing │
|
|
108
|
-
└─────────────────────────┘ │ Sketch 2 │
|
|
109
|
-
│ Project X │
|
|
110
|
-
│ ─────────────────── │
|
|
111
|
-
│ + Ny ritning │
|
|
112
|
-
│ 📄 Hantera... │
|
|
113
|
-
└─────────────────────┘
|
|
114
|
-
```
|
|
140
|
+
|
|
141
|
+
| Code | Language |
|
|
142
|
+
|------|----------|
|
|
143
|
+
| `sv`, `sv-SE` | 🇸🇪 Swedish |
|
|
144
|
+
| `en`, `en-US` | 🇬🇧 English (default) |
|
|
115
145
|
|
|
116
146
|
## API Reference
|
|
117
147
|
|
|
@@ -119,24 +149,32 @@ After integration, a "📄 Ritningar" submenu appears in the hamburger menu:
|
|
|
119
149
|
|
|
120
150
|
| Component | Description |
|
|
121
151
|
|-----------|-------------|
|
|
122
|
-
| `WorkspaceProvider` | React context provider
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
152
|
+
| `WorkspaceProvider` | React context provider. Props: `lang` |
|
|
153
|
+
| `WorkspaceBridge` | Auto-syncs workspace ↔ Excalidraw. Props: `excalidrawAPI`, `autoSaveInterval` |
|
|
154
|
+
| `WorkspaceMenuItems` | Menu items for MainMenu. Props: `onManageDrawings`, `lang` |
|
|
155
|
+
| `DrawingsDialog` | Management dialog. Props: `open`, `onClose`, `lang` |
|
|
125
156
|
|
|
126
157
|
### Hooks
|
|
127
158
|
|
|
128
159
|
| Hook | Description |
|
|
129
160
|
|------|-------------|
|
|
130
161
|
| `useWorkspace()` | Access workspace state and actions |
|
|
162
|
+
| `useWorkspaceLang()` | Get current language and translations |
|
|
131
163
|
|
|
132
164
|
### useWorkspace() returns
|
|
133
165
|
|
|
134
166
|
```tsx
|
|
135
167
|
const {
|
|
168
|
+
// State
|
|
136
169
|
drawings, // Drawing[] - all drawings
|
|
137
170
|
activeDrawing, // Drawing | null - currently active
|
|
138
171
|
isLoading, // boolean
|
|
139
|
-
|
|
172
|
+
error, // string | null
|
|
173
|
+
lang, // string - current language code
|
|
174
|
+
t, // Translations object
|
|
175
|
+
|
|
176
|
+
// Actions
|
|
177
|
+
createNewDrawing, // (name?: string) => Promise<Drawing>
|
|
140
178
|
switchDrawing, // (id: string) => Promise<void>
|
|
141
179
|
renameDrawing, // (id: string, name: string) => Promise<void>
|
|
142
180
|
removeDrawing, // (id: string) => Promise<void>
|
|
@@ -144,9 +182,20 @@ const {
|
|
|
144
182
|
} = useWorkspace();
|
|
145
183
|
```
|
|
146
184
|
|
|
185
|
+
### WorkspaceBridge Props
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
<WorkspaceBridge
|
|
189
|
+
excalidrawAPI={excalidrawAPI} // Required - from Excalidraw
|
|
190
|
+
autoSaveInterval={2000} // Optional - ms between saves (default: 2000)
|
|
191
|
+
onDrawingLoad={(id) => {}} // Optional - called when drawing loads
|
|
192
|
+
onDrawingSave={(id) => {}} // Optional - called when drawing saves
|
|
193
|
+
/>
|
|
194
|
+
```
|
|
195
|
+
|
|
147
196
|
## Data Storage
|
|
148
197
|
|
|
149
|
-
Drawings are stored in IndexedDB
|
|
198
|
+
Drawings are stored in IndexedDB:
|
|
150
199
|
|
|
151
200
|
```typescript
|
|
152
201
|
interface Drawing {
|
package/dist/index.d.mts
CHANGED
|
@@ -99,7 +99,7 @@ interface WorkspaceContextValue {
|
|
|
99
99
|
renameDrawing: (id: string, name: string) => Promise<void>;
|
|
100
100
|
removeDrawing: (id: string) => Promise<void>;
|
|
101
101
|
duplicateCurrentDrawing: () => Promise<Drawing | null>;
|
|
102
|
-
saveCurrentDrawing: (elements: unknown[], appState: Record<string, unknown>) => Promise<void>;
|
|
102
|
+
saveCurrentDrawing: (elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
103
103
|
}
|
|
104
104
|
declare function useWorkspace(): WorkspaceContextValue;
|
|
105
105
|
/**
|
|
@@ -262,6 +262,64 @@ declare function useExcalidrawBridge({ excalidrawAPI, autoSaveInterval, }: UseEx
|
|
|
262
262
|
scheduleSave: () => void;
|
|
263
263
|
};
|
|
264
264
|
|
|
265
|
+
/**
|
|
266
|
+
* WorkspaceBridge - Automatic sync between Workspace and Excalidraw
|
|
267
|
+
*
|
|
268
|
+
* This component handles:
|
|
269
|
+
* 1. Loading drawings into Excalidraw when activeDrawing changes
|
|
270
|
+
* 2. Auto-saving Excalidraw changes back to the workspace
|
|
271
|
+
* 3. Saving current drawing before switching to another
|
|
272
|
+
*/
|
|
273
|
+
interface ExcalidrawImperativeAPI {
|
|
274
|
+
getSceneElements: () => unknown[];
|
|
275
|
+
getAppState: () => Record<string, unknown>;
|
|
276
|
+
getFiles: () => Record<string, unknown>;
|
|
277
|
+
updateScene: (scene: {
|
|
278
|
+
elements?: unknown[];
|
|
279
|
+
appState?: Record<string, unknown>;
|
|
280
|
+
commitToStore?: boolean;
|
|
281
|
+
}) => void;
|
|
282
|
+
resetScene: () => void;
|
|
283
|
+
scrollToContent: () => void;
|
|
284
|
+
}
|
|
285
|
+
interface WorkspaceBridgeProps {
|
|
286
|
+
/**
|
|
287
|
+
* The Excalidraw imperative API
|
|
288
|
+
*/
|
|
289
|
+
excalidrawAPI: ExcalidrawImperativeAPI | null;
|
|
290
|
+
/**
|
|
291
|
+
* Auto-save interval in milliseconds (default: 2000)
|
|
292
|
+
* Set to 0 to disable auto-save
|
|
293
|
+
*/
|
|
294
|
+
autoSaveInterval?: number;
|
|
295
|
+
/**
|
|
296
|
+
* Called when a drawing is loaded into Excalidraw
|
|
297
|
+
*/
|
|
298
|
+
onDrawingLoad?: (drawingId: string) => void;
|
|
299
|
+
/**
|
|
300
|
+
* Called when a drawing is saved
|
|
301
|
+
*/
|
|
302
|
+
onDrawingSave?: (drawingId: string) => void;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* WorkspaceBridge component - place this inside your Excalidraw wrapper
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```tsx
|
|
309
|
+
* const ExcalidrawWrapper = () => {
|
|
310
|
+
* const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
|
311
|
+
*
|
|
312
|
+
* return (
|
|
313
|
+
* <>
|
|
314
|
+
* <WorkspaceBridge excalidrawAPI={excalidrawAPI} />
|
|
315
|
+
* <Excalidraw excalidrawAPI={setExcalidrawAPI} />
|
|
316
|
+
* </>
|
|
317
|
+
* );
|
|
318
|
+
* };
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function WorkspaceBridge({ excalidrawAPI, autoSaveInterval, onDrawingLoad, onDrawingSave, }: WorkspaceBridgeProps): null;
|
|
322
|
+
|
|
265
323
|
interface WorkspacePluginProps {
|
|
266
324
|
children: ReactNode;
|
|
267
325
|
defaultSidebarOpen?: boolean;
|
|
@@ -285,4 +343,4 @@ interface WorkspacePluginProps {
|
|
|
285
343
|
*/
|
|
286
344
|
declare function WorkspacePlugin(props: WorkspacePluginProps): react_jsx_runtime.JSX.Element;
|
|
287
345
|
|
|
288
|
-
export { type Drawing, DrawingList, DrawingListItem, DrawingsDialog, type DrawingsDialogProps, Sidebar, type SupportedLanguage, type Translations, type Workspace, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, deleteDrawing, duplicateDrawing, getAllDrawings, getDB, getDrawing, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isLanguageSupported, removeDrawingFromWorkspace, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang };
|
|
346
|
+
export { type Drawing, DrawingList, DrawingListItem, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, deleteDrawing, duplicateDrawing, getAllDrawings, getDB, getDrawing, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isLanguageSupported, removeDrawingFromWorkspace, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang };
|
package/dist/index.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ interface WorkspaceContextValue {
|
|
|
99
99
|
renameDrawing: (id: string, name: string) => Promise<void>;
|
|
100
100
|
removeDrawing: (id: string) => Promise<void>;
|
|
101
101
|
duplicateCurrentDrawing: () => Promise<Drawing | null>;
|
|
102
|
-
saveCurrentDrawing: (elements: unknown[], appState: Record<string, unknown>) => Promise<void>;
|
|
102
|
+
saveCurrentDrawing: (elements: unknown[], appState: Record<string, unknown>, files?: Record<string, unknown>) => Promise<void>;
|
|
103
103
|
}
|
|
104
104
|
declare function useWorkspace(): WorkspaceContextValue;
|
|
105
105
|
/**
|
|
@@ -262,6 +262,64 @@ declare function useExcalidrawBridge({ excalidrawAPI, autoSaveInterval, }: UseEx
|
|
|
262
262
|
scheduleSave: () => void;
|
|
263
263
|
};
|
|
264
264
|
|
|
265
|
+
/**
|
|
266
|
+
* WorkspaceBridge - Automatic sync between Workspace and Excalidraw
|
|
267
|
+
*
|
|
268
|
+
* This component handles:
|
|
269
|
+
* 1. Loading drawings into Excalidraw when activeDrawing changes
|
|
270
|
+
* 2. Auto-saving Excalidraw changes back to the workspace
|
|
271
|
+
* 3. Saving current drawing before switching to another
|
|
272
|
+
*/
|
|
273
|
+
interface ExcalidrawImperativeAPI {
|
|
274
|
+
getSceneElements: () => unknown[];
|
|
275
|
+
getAppState: () => Record<string, unknown>;
|
|
276
|
+
getFiles: () => Record<string, unknown>;
|
|
277
|
+
updateScene: (scene: {
|
|
278
|
+
elements?: unknown[];
|
|
279
|
+
appState?: Record<string, unknown>;
|
|
280
|
+
commitToStore?: boolean;
|
|
281
|
+
}) => void;
|
|
282
|
+
resetScene: () => void;
|
|
283
|
+
scrollToContent: () => void;
|
|
284
|
+
}
|
|
285
|
+
interface WorkspaceBridgeProps {
|
|
286
|
+
/**
|
|
287
|
+
* The Excalidraw imperative API
|
|
288
|
+
*/
|
|
289
|
+
excalidrawAPI: ExcalidrawImperativeAPI | null;
|
|
290
|
+
/**
|
|
291
|
+
* Auto-save interval in milliseconds (default: 2000)
|
|
292
|
+
* Set to 0 to disable auto-save
|
|
293
|
+
*/
|
|
294
|
+
autoSaveInterval?: number;
|
|
295
|
+
/**
|
|
296
|
+
* Called when a drawing is loaded into Excalidraw
|
|
297
|
+
*/
|
|
298
|
+
onDrawingLoad?: (drawingId: string) => void;
|
|
299
|
+
/**
|
|
300
|
+
* Called when a drawing is saved
|
|
301
|
+
*/
|
|
302
|
+
onDrawingSave?: (drawingId: string) => void;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* WorkspaceBridge component - place this inside your Excalidraw wrapper
|
|
306
|
+
*
|
|
307
|
+
* @example
|
|
308
|
+
* ```tsx
|
|
309
|
+
* const ExcalidrawWrapper = () => {
|
|
310
|
+
* const [excalidrawAPI, setExcalidrawAPI] = useState(null);
|
|
311
|
+
*
|
|
312
|
+
* return (
|
|
313
|
+
* <>
|
|
314
|
+
* <WorkspaceBridge excalidrawAPI={excalidrawAPI} />
|
|
315
|
+
* <Excalidraw excalidrawAPI={setExcalidrawAPI} />
|
|
316
|
+
* </>
|
|
317
|
+
* );
|
|
318
|
+
* };
|
|
319
|
+
* ```
|
|
320
|
+
*/
|
|
321
|
+
declare function WorkspaceBridge({ excalidrawAPI, autoSaveInterval, onDrawingLoad, onDrawingSave, }: WorkspaceBridgeProps): null;
|
|
322
|
+
|
|
265
323
|
interface WorkspacePluginProps {
|
|
266
324
|
children: ReactNode;
|
|
267
325
|
defaultSidebarOpen?: boolean;
|
|
@@ -285,4 +343,4 @@ interface WorkspacePluginProps {
|
|
|
285
343
|
*/
|
|
286
344
|
declare function WorkspacePlugin(props: WorkspacePluginProps): react_jsx_runtime.JSX.Element;
|
|
287
345
|
|
|
288
|
-
export { type Drawing, DrawingList, DrawingListItem, DrawingsDialog, type DrawingsDialogProps, Sidebar, type SupportedLanguage, type Translations, type Workspace, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, deleteDrawing, duplicateDrawing, getAllDrawings, getDB, getDrawing, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isLanguageSupported, removeDrawingFromWorkspace, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang };
|
|
346
|
+
export { type Drawing, DrawingList, DrawingListItem, DrawingsDialog, type DrawingsDialogProps, type ExcalidrawImperativeAPI, Sidebar, type SupportedLanguage, type Translations, type Workspace, WorkspaceBridge, type WorkspaceBridgeProps, type WorkspaceContextValue, WorkspaceMenuItems, type WorkspaceMenuItemsProps, WorkspacePlugin, WorkspaceProvider, addDrawingToWorkspace, closeDB, createDrawing, deleteDrawing, duplicateDrawing, getAllDrawings, getDB, getDrawing, getOrCreateDefaultWorkspace, getTranslations, getWorkspace, isLanguageSupported, removeDrawingFromWorkspace, setActiveDrawing, updateDrawing, updateWorkspace, useExcalidrawBridge, useWorkspace, useWorkspaceLang };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(index_exports, {
|
|
|
34
34
|
DrawingListItem: () => DrawingListItem,
|
|
35
35
|
DrawingsDialog: () => DrawingsDialog,
|
|
36
36
|
Sidebar: () => Sidebar,
|
|
37
|
+
WorkspaceBridge: () => WorkspaceBridge,
|
|
37
38
|
WorkspaceMenuItems: () => WorkspaceMenuItems,
|
|
38
39
|
WorkspacePlugin: () => WorkspacePlugin,
|
|
39
40
|
WorkspaceProvider: () => WorkspaceProvider,
|
|
@@ -211,11 +212,11 @@ var import_react = require("react");
|
|
|
211
212
|
// src/i18n/translations.ts
|
|
212
213
|
var sv = {
|
|
213
214
|
// Menu
|
|
214
|
-
drawings: "
|
|
215
|
+
drawings: "Arbetsyta",
|
|
215
216
|
newDrawing: "Ny ritning",
|
|
216
|
-
manageDrawings: "Hantera
|
|
217
|
+
manageDrawings: "Hantera arbetsyta...",
|
|
217
218
|
// Dialog
|
|
218
|
-
dialogTitle: "
|
|
219
|
+
dialogTitle: "Arbetsyta",
|
|
219
220
|
close: "St\xE4ng",
|
|
220
221
|
open: "\xD6ppna",
|
|
221
222
|
rename: "Byt namn",
|
|
@@ -233,11 +234,11 @@ var sv = {
|
|
|
233
234
|
};
|
|
234
235
|
var en = {
|
|
235
236
|
// Menu
|
|
236
|
-
drawings: "
|
|
237
|
+
drawings: "Workspace",
|
|
237
238
|
newDrawing: "New drawing",
|
|
238
|
-
manageDrawings: "Manage
|
|
239
|
+
manageDrawings: "Manage workspace...",
|
|
239
240
|
// Dialog
|
|
240
|
-
dialogTitle: "
|
|
241
|
+
dialogTitle: "Workspace",
|
|
241
242
|
close: "Close",
|
|
242
243
|
open: "Open",
|
|
243
244
|
rename: "Rename",
|
|
@@ -398,10 +399,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
398
399
|
return null;
|
|
399
400
|
}
|
|
400
401
|
}, [activeDrawing, workspace]);
|
|
401
|
-
const saveCurrentDrawing = (0, import_react.useCallback)(async (elements, appState) => {
|
|
402
|
+
const saveCurrentDrawing = (0, import_react.useCallback)(async (elements, appState, files) => {
|
|
402
403
|
if (!activeDrawing) return;
|
|
403
404
|
try {
|
|
404
|
-
const
|
|
405
|
+
const updateData = {
|
|
406
|
+
elements,
|
|
407
|
+
appState
|
|
408
|
+
};
|
|
409
|
+
if (files) {
|
|
410
|
+
updateData.files = files;
|
|
411
|
+
}
|
|
412
|
+
const updated = await updateDrawing(activeDrawing.id, updateData);
|
|
405
413
|
if (updated) {
|
|
406
414
|
setActiveDrawing2(updated);
|
|
407
415
|
setDrawings((prev) => prev.map((d) => d.id === updated.id ? updated : d));
|
|
@@ -1185,17 +1193,119 @@ function useExcalidrawBridge({
|
|
|
1185
1193
|
};
|
|
1186
1194
|
}
|
|
1187
1195
|
|
|
1188
|
-
// src/
|
|
1196
|
+
// src/integration/WorkspaceBridge.tsx
|
|
1189
1197
|
var import_react7 = require("react");
|
|
1198
|
+
function WorkspaceBridge({
|
|
1199
|
+
excalidrawAPI,
|
|
1200
|
+
autoSaveInterval = 2e3,
|
|
1201
|
+
onDrawingLoad,
|
|
1202
|
+
onDrawingSave
|
|
1203
|
+
}) {
|
|
1204
|
+
const { activeDrawing, saveCurrentDrawing } = useWorkspace();
|
|
1205
|
+
const lastDrawingIdRef = (0, import_react7.useRef)(null);
|
|
1206
|
+
const saveTimeoutRef = (0, import_react7.useRef)(null);
|
|
1207
|
+
const isLoadingRef = (0, import_react7.useRef)(false);
|
|
1208
|
+
const saveDrawing = (0, import_react7.useCallback)(async () => {
|
|
1209
|
+
if (!excalidrawAPI || !activeDrawing || isLoadingRef.current) return;
|
|
1210
|
+
try {
|
|
1211
|
+
const elements = excalidrawAPI.getSceneElements();
|
|
1212
|
+
const appState = excalidrawAPI.getAppState();
|
|
1213
|
+
const files = excalidrawAPI.getFiles();
|
|
1214
|
+
const persistentAppState = {
|
|
1215
|
+
viewBackgroundColor: appState.viewBackgroundColor,
|
|
1216
|
+
zoom: appState.zoom,
|
|
1217
|
+
scrollX: appState.scrollX,
|
|
1218
|
+
scrollY: appState.scrollY
|
|
1219
|
+
// Add other persistent properties as needed
|
|
1220
|
+
};
|
|
1221
|
+
await saveCurrentDrawing(elements, persistentAppState, files);
|
|
1222
|
+
onDrawingSave?.(activeDrawing.id);
|
|
1223
|
+
} catch (error) {
|
|
1224
|
+
console.error("[WorkspaceBridge] Failed to save drawing:", error);
|
|
1225
|
+
}
|
|
1226
|
+
}, [excalidrawAPI, activeDrawing, saveCurrentDrawing, onDrawingSave]);
|
|
1227
|
+
const scheduleSave = (0, import_react7.useCallback)(() => {
|
|
1228
|
+
if (autoSaveInterval <= 0) return;
|
|
1229
|
+
if (saveTimeoutRef.current) {
|
|
1230
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1231
|
+
}
|
|
1232
|
+
saveTimeoutRef.current = setTimeout(saveDrawing, autoSaveInterval);
|
|
1233
|
+
}, [saveDrawing, autoSaveInterval]);
|
|
1234
|
+
(0, import_react7.useEffect)(() => {
|
|
1235
|
+
if (!excalidrawAPI || !activeDrawing) return;
|
|
1236
|
+
if (lastDrawingIdRef.current === activeDrawing.id) return;
|
|
1237
|
+
if (lastDrawingIdRef.current !== null) {
|
|
1238
|
+
if (saveTimeoutRef.current) {
|
|
1239
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1240
|
+
saveTimeoutRef.current = null;
|
|
1241
|
+
}
|
|
1242
|
+
saveDrawing();
|
|
1243
|
+
}
|
|
1244
|
+
isLoadingRef.current = true;
|
|
1245
|
+
lastDrawingIdRef.current = activeDrawing.id;
|
|
1246
|
+
try {
|
|
1247
|
+
excalidrawAPI.updateScene({
|
|
1248
|
+
elements: activeDrawing.elements || [],
|
|
1249
|
+
appState: activeDrawing.appState || {},
|
|
1250
|
+
commitToStore: true
|
|
1251
|
+
});
|
|
1252
|
+
onDrawingLoad?.(activeDrawing.id);
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
console.error("[WorkspaceBridge] Failed to load drawing:", error);
|
|
1255
|
+
} finally {
|
|
1256
|
+
setTimeout(() => {
|
|
1257
|
+
isLoadingRef.current = false;
|
|
1258
|
+
}, 100);
|
|
1259
|
+
}
|
|
1260
|
+
}, [excalidrawAPI, activeDrawing, saveDrawing, onDrawingLoad]);
|
|
1261
|
+
(0, import_react7.useEffect)(() => {
|
|
1262
|
+
if (!excalidrawAPI || autoSaveInterval <= 0) return;
|
|
1263
|
+
let lastElementCount = 0;
|
|
1264
|
+
let lastChecksum = "";
|
|
1265
|
+
const checkForChanges = () => {
|
|
1266
|
+
if (isLoadingRef.current || !excalidrawAPI) return;
|
|
1267
|
+
try {
|
|
1268
|
+
const elements = excalidrawAPI.getSceneElements();
|
|
1269
|
+
const currentCount = elements.length;
|
|
1270
|
+
const currentChecksum = JSON.stringify(elements.slice(-1));
|
|
1271
|
+
if (currentCount !== lastElementCount || currentChecksum !== lastChecksum) {
|
|
1272
|
+
lastElementCount = currentCount;
|
|
1273
|
+
lastChecksum = currentChecksum;
|
|
1274
|
+
scheduleSave();
|
|
1275
|
+
}
|
|
1276
|
+
} catch {
|
|
1277
|
+
}
|
|
1278
|
+
};
|
|
1279
|
+
const intervalId = setInterval(checkForChanges, 1e3);
|
|
1280
|
+
return () => {
|
|
1281
|
+
clearInterval(intervalId);
|
|
1282
|
+
if (saveTimeoutRef.current) {
|
|
1283
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1284
|
+
}
|
|
1285
|
+
};
|
|
1286
|
+
}, [excalidrawAPI, autoSaveInterval, scheduleSave]);
|
|
1287
|
+
(0, import_react7.useEffect)(() => {
|
|
1288
|
+
return () => {
|
|
1289
|
+
if (saveTimeoutRef.current) {
|
|
1290
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1291
|
+
}
|
|
1292
|
+
saveDrawing();
|
|
1293
|
+
};
|
|
1294
|
+
}, [saveDrawing]);
|
|
1295
|
+
return null;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// src/WorkspacePlugin.tsx
|
|
1299
|
+
var import_react8 = require("react");
|
|
1190
1300
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1191
1301
|
function WorkspacePluginInner({
|
|
1192
1302
|
children,
|
|
1193
1303
|
defaultSidebarOpen = true,
|
|
1194
1304
|
sidebarWidth = 250
|
|
1195
1305
|
}) {
|
|
1196
|
-
const [sidebarOpen, setSidebarOpen] = (0,
|
|
1306
|
+
const [sidebarOpen, setSidebarOpen] = (0, import_react8.useState)(defaultSidebarOpen);
|
|
1197
1307
|
const { activeDrawing } = useWorkspace();
|
|
1198
|
-
(0,
|
|
1308
|
+
(0, import_react8.useEffect)(() => {
|
|
1199
1309
|
const handleKeyDown = (e) => {
|
|
1200
1310
|
if (e.ctrlKey && e.key === "b") {
|
|
1201
1311
|
e.preventDefault();
|
|
@@ -1208,7 +1318,7 @@ function WorkspacePluginInner({
|
|
|
1208
1318
|
window.addEventListener("keydown", handleKeyDown);
|
|
1209
1319
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1210
1320
|
}, []);
|
|
1211
|
-
const handleToggleSidebar = (0,
|
|
1321
|
+
const handleToggleSidebar = (0, import_react8.useCallback)(() => {
|
|
1212
1322
|
setSidebarOpen((prev) => !prev);
|
|
1213
1323
|
}, []);
|
|
1214
1324
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
@@ -1252,6 +1362,7 @@ function WorkspacePlugin(props) {
|
|
|
1252
1362
|
DrawingListItem,
|
|
1253
1363
|
DrawingsDialog,
|
|
1254
1364
|
Sidebar,
|
|
1365
|
+
WorkspaceBridge,
|
|
1255
1366
|
WorkspaceMenuItems,
|
|
1256
1367
|
WorkspacePlugin,
|
|
1257
1368
|
WorkspaceProvider,
|
package/dist/index.mjs
CHANGED
|
@@ -150,11 +150,11 @@ import { createContext, useContext, useEffect, useState, useCallback } from "rea
|
|
|
150
150
|
// src/i18n/translations.ts
|
|
151
151
|
var sv = {
|
|
152
152
|
// Menu
|
|
153
|
-
drawings: "
|
|
153
|
+
drawings: "Arbetsyta",
|
|
154
154
|
newDrawing: "Ny ritning",
|
|
155
|
-
manageDrawings: "Hantera
|
|
155
|
+
manageDrawings: "Hantera arbetsyta...",
|
|
156
156
|
// Dialog
|
|
157
|
-
dialogTitle: "
|
|
157
|
+
dialogTitle: "Arbetsyta",
|
|
158
158
|
close: "St\xE4ng",
|
|
159
159
|
open: "\xD6ppna",
|
|
160
160
|
rename: "Byt namn",
|
|
@@ -172,11 +172,11 @@ var sv = {
|
|
|
172
172
|
};
|
|
173
173
|
var en = {
|
|
174
174
|
// Menu
|
|
175
|
-
drawings: "
|
|
175
|
+
drawings: "Workspace",
|
|
176
176
|
newDrawing: "New drawing",
|
|
177
|
-
manageDrawings: "Manage
|
|
177
|
+
manageDrawings: "Manage workspace...",
|
|
178
178
|
// Dialog
|
|
179
|
-
dialogTitle: "
|
|
179
|
+
dialogTitle: "Workspace",
|
|
180
180
|
close: "Close",
|
|
181
181
|
open: "Open",
|
|
182
182
|
rename: "Rename",
|
|
@@ -337,10 +337,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
337
337
|
return null;
|
|
338
338
|
}
|
|
339
339
|
}, [activeDrawing, workspace]);
|
|
340
|
-
const saveCurrentDrawing = useCallback(async (elements, appState) => {
|
|
340
|
+
const saveCurrentDrawing = useCallback(async (elements, appState, files) => {
|
|
341
341
|
if (!activeDrawing) return;
|
|
342
342
|
try {
|
|
343
|
-
const
|
|
343
|
+
const updateData = {
|
|
344
|
+
elements,
|
|
345
|
+
appState
|
|
346
|
+
};
|
|
347
|
+
if (files) {
|
|
348
|
+
updateData.files = files;
|
|
349
|
+
}
|
|
350
|
+
const updated = await updateDrawing(activeDrawing.id, updateData);
|
|
344
351
|
if (updated) {
|
|
345
352
|
setActiveDrawing2(updated);
|
|
346
353
|
setDrawings((prev) => prev.map((d) => d.id === updated.id ? updated : d));
|
|
@@ -1124,8 +1131,110 @@ function useExcalidrawBridge({
|
|
|
1124
1131
|
};
|
|
1125
1132
|
}
|
|
1126
1133
|
|
|
1134
|
+
// src/integration/WorkspaceBridge.tsx
|
|
1135
|
+
import { useEffect as useEffect4, useRef as useRef3, useCallback as useCallback4 } from "react";
|
|
1136
|
+
function WorkspaceBridge({
|
|
1137
|
+
excalidrawAPI,
|
|
1138
|
+
autoSaveInterval = 2e3,
|
|
1139
|
+
onDrawingLoad,
|
|
1140
|
+
onDrawingSave
|
|
1141
|
+
}) {
|
|
1142
|
+
const { activeDrawing, saveCurrentDrawing } = useWorkspace();
|
|
1143
|
+
const lastDrawingIdRef = useRef3(null);
|
|
1144
|
+
const saveTimeoutRef = useRef3(null);
|
|
1145
|
+
const isLoadingRef = useRef3(false);
|
|
1146
|
+
const saveDrawing = useCallback4(async () => {
|
|
1147
|
+
if (!excalidrawAPI || !activeDrawing || isLoadingRef.current) return;
|
|
1148
|
+
try {
|
|
1149
|
+
const elements = excalidrawAPI.getSceneElements();
|
|
1150
|
+
const appState = excalidrawAPI.getAppState();
|
|
1151
|
+
const files = excalidrawAPI.getFiles();
|
|
1152
|
+
const persistentAppState = {
|
|
1153
|
+
viewBackgroundColor: appState.viewBackgroundColor,
|
|
1154
|
+
zoom: appState.zoom,
|
|
1155
|
+
scrollX: appState.scrollX,
|
|
1156
|
+
scrollY: appState.scrollY
|
|
1157
|
+
// Add other persistent properties as needed
|
|
1158
|
+
};
|
|
1159
|
+
await saveCurrentDrawing(elements, persistentAppState, files);
|
|
1160
|
+
onDrawingSave?.(activeDrawing.id);
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
console.error("[WorkspaceBridge] Failed to save drawing:", error);
|
|
1163
|
+
}
|
|
1164
|
+
}, [excalidrawAPI, activeDrawing, saveCurrentDrawing, onDrawingSave]);
|
|
1165
|
+
const scheduleSave = useCallback4(() => {
|
|
1166
|
+
if (autoSaveInterval <= 0) return;
|
|
1167
|
+
if (saveTimeoutRef.current) {
|
|
1168
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1169
|
+
}
|
|
1170
|
+
saveTimeoutRef.current = setTimeout(saveDrawing, autoSaveInterval);
|
|
1171
|
+
}, [saveDrawing, autoSaveInterval]);
|
|
1172
|
+
useEffect4(() => {
|
|
1173
|
+
if (!excalidrawAPI || !activeDrawing) return;
|
|
1174
|
+
if (lastDrawingIdRef.current === activeDrawing.id) return;
|
|
1175
|
+
if (lastDrawingIdRef.current !== null) {
|
|
1176
|
+
if (saveTimeoutRef.current) {
|
|
1177
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1178
|
+
saveTimeoutRef.current = null;
|
|
1179
|
+
}
|
|
1180
|
+
saveDrawing();
|
|
1181
|
+
}
|
|
1182
|
+
isLoadingRef.current = true;
|
|
1183
|
+
lastDrawingIdRef.current = activeDrawing.id;
|
|
1184
|
+
try {
|
|
1185
|
+
excalidrawAPI.updateScene({
|
|
1186
|
+
elements: activeDrawing.elements || [],
|
|
1187
|
+
appState: activeDrawing.appState || {},
|
|
1188
|
+
commitToStore: true
|
|
1189
|
+
});
|
|
1190
|
+
onDrawingLoad?.(activeDrawing.id);
|
|
1191
|
+
} catch (error) {
|
|
1192
|
+
console.error("[WorkspaceBridge] Failed to load drawing:", error);
|
|
1193
|
+
} finally {
|
|
1194
|
+
setTimeout(() => {
|
|
1195
|
+
isLoadingRef.current = false;
|
|
1196
|
+
}, 100);
|
|
1197
|
+
}
|
|
1198
|
+
}, [excalidrawAPI, activeDrawing, saveDrawing, onDrawingLoad]);
|
|
1199
|
+
useEffect4(() => {
|
|
1200
|
+
if (!excalidrawAPI || autoSaveInterval <= 0) return;
|
|
1201
|
+
let lastElementCount = 0;
|
|
1202
|
+
let lastChecksum = "";
|
|
1203
|
+
const checkForChanges = () => {
|
|
1204
|
+
if (isLoadingRef.current || !excalidrawAPI) return;
|
|
1205
|
+
try {
|
|
1206
|
+
const elements = excalidrawAPI.getSceneElements();
|
|
1207
|
+
const currentCount = elements.length;
|
|
1208
|
+
const currentChecksum = JSON.stringify(elements.slice(-1));
|
|
1209
|
+
if (currentCount !== lastElementCount || currentChecksum !== lastChecksum) {
|
|
1210
|
+
lastElementCount = currentCount;
|
|
1211
|
+
lastChecksum = currentChecksum;
|
|
1212
|
+
scheduleSave();
|
|
1213
|
+
}
|
|
1214
|
+
} catch {
|
|
1215
|
+
}
|
|
1216
|
+
};
|
|
1217
|
+
const intervalId = setInterval(checkForChanges, 1e3);
|
|
1218
|
+
return () => {
|
|
1219
|
+
clearInterval(intervalId);
|
|
1220
|
+
if (saveTimeoutRef.current) {
|
|
1221
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
}, [excalidrawAPI, autoSaveInterval, scheduleSave]);
|
|
1225
|
+
useEffect4(() => {
|
|
1226
|
+
return () => {
|
|
1227
|
+
if (saveTimeoutRef.current) {
|
|
1228
|
+
clearTimeout(saveTimeoutRef.current);
|
|
1229
|
+
}
|
|
1230
|
+
saveDrawing();
|
|
1231
|
+
};
|
|
1232
|
+
}, [saveDrawing]);
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1127
1236
|
// src/WorkspacePlugin.tsx
|
|
1128
|
-
import { useState as useState5, useEffect as
|
|
1237
|
+
import { useState as useState5, useEffect as useEffect5, useCallback as useCallback5 } from "react";
|
|
1129
1238
|
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1130
1239
|
function WorkspacePluginInner({
|
|
1131
1240
|
children,
|
|
@@ -1134,7 +1243,7 @@ function WorkspacePluginInner({
|
|
|
1134
1243
|
}) {
|
|
1135
1244
|
const [sidebarOpen, setSidebarOpen] = useState5(defaultSidebarOpen);
|
|
1136
1245
|
const { activeDrawing } = useWorkspace();
|
|
1137
|
-
|
|
1246
|
+
useEffect5(() => {
|
|
1138
1247
|
const handleKeyDown = (e) => {
|
|
1139
1248
|
if (e.ctrlKey && e.key === "b") {
|
|
1140
1249
|
e.preventDefault();
|
|
@@ -1147,7 +1256,7 @@ function WorkspacePluginInner({
|
|
|
1147
1256
|
window.addEventListener("keydown", handleKeyDown);
|
|
1148
1257
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1149
1258
|
}, []);
|
|
1150
|
-
const handleToggleSidebar =
|
|
1259
|
+
const handleToggleSidebar = useCallback5(() => {
|
|
1151
1260
|
setSidebarOpen((prev) => !prev);
|
|
1152
1261
|
}, []);
|
|
1153
1262
|
return /* @__PURE__ */ jsxs5(
|
|
@@ -1190,6 +1299,7 @@ export {
|
|
|
1190
1299
|
DrawingListItem,
|
|
1191
1300
|
DrawingsDialog,
|
|
1192
1301
|
Sidebar,
|
|
1302
|
+
WorkspaceBridge,
|
|
1193
1303
|
WorkspaceMenuItems,
|
|
1194
1304
|
WorkspacePlugin,
|
|
1195
1305
|
WorkspaceProvider,
|