rita-workspace 0.5.0 → 0.5.2
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/dist/index.d.mts +36 -3
- package/dist/index.d.ts +36 -3
- package/dist/index.js +720 -198
- package/dist/index.mjs +715 -199
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
// src/storage/db.ts
|
|
2
2
|
import { openDB } from "idb";
|
|
3
3
|
var DB_NAME = "rita-workspace";
|
|
4
|
-
var DB_VERSION =
|
|
4
|
+
var DB_VERSION = 2;
|
|
5
5
|
var dbInstance = null;
|
|
6
6
|
async function getDB() {
|
|
7
7
|
if (dbInstance) return dbInstance;
|
|
8
8
|
dbInstance = await openDB(DB_NAME, DB_VERSION, {
|
|
9
|
-
upgrade(db) {
|
|
9
|
+
upgrade(db, oldVersion) {
|
|
10
10
|
if (!db.objectStoreNames.contains("workspaces")) {
|
|
11
11
|
const workspaceStore = db.createObjectStore("workspaces", { keyPath: "id" });
|
|
12
12
|
workspaceStore.createIndex("by-updated", "updatedAt");
|
|
@@ -15,6 +15,10 @@ async function getDB() {
|
|
|
15
15
|
const drawingStore = db.createObjectStore("drawings", { keyPath: "id" });
|
|
16
16
|
drawingStore.createIndex("by-updated", "updatedAt");
|
|
17
17
|
}
|
|
18
|
+
if (oldVersion < 2) {
|
|
19
|
+
const folderStore = db.createObjectStore("folders", { keyPath: "id" });
|
|
20
|
+
folderStore.createIndex("by-name", "name");
|
|
21
|
+
}
|
|
18
22
|
}
|
|
19
23
|
});
|
|
20
24
|
return dbInstance;
|
|
@@ -28,12 +32,13 @@ async function closeDB() {
|
|
|
28
32
|
|
|
29
33
|
// src/storage/drawingStore.ts
|
|
30
34
|
import { nanoid } from "nanoid";
|
|
31
|
-
async function createDrawing(name = "Untitled", elements = [], appState = {}) {
|
|
35
|
+
async function createDrawing(name = "Untitled", elements = [], appState = {}, folderId) {
|
|
32
36
|
const db = await getDB();
|
|
33
37
|
const now = Date.now();
|
|
34
38
|
const drawing = {
|
|
35
39
|
id: nanoid(),
|
|
36
40
|
name,
|
|
41
|
+
folderId: folderId || null,
|
|
37
42
|
elements,
|
|
38
43
|
appState,
|
|
39
44
|
files: {},
|
|
@@ -76,9 +81,62 @@ async function duplicateDrawing(id, newName) {
|
|
|
76
81
|
return createDrawing(
|
|
77
82
|
newName || `${existing.name} (copy)`,
|
|
78
83
|
existing.elements,
|
|
79
|
-
existing.appState
|
|
84
|
+
existing.appState,
|
|
85
|
+
existing.folderId
|
|
80
86
|
);
|
|
81
87
|
}
|
|
88
|
+
async function moveDrawingToFolder(drawingId, folderId) {
|
|
89
|
+
return updateDrawing(drawingId, { folderId });
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// src/storage/folderStore.ts
|
|
93
|
+
import { nanoid as nanoid2 } from "nanoid";
|
|
94
|
+
async function createFolder(name) {
|
|
95
|
+
const db = await getDB();
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
const folder = {
|
|
98
|
+
id: nanoid2(),
|
|
99
|
+
name,
|
|
100
|
+
createdAt: now,
|
|
101
|
+
updatedAt: now
|
|
102
|
+
};
|
|
103
|
+
await db.put("folders", folder);
|
|
104
|
+
return folder;
|
|
105
|
+
}
|
|
106
|
+
async function getFolder(id) {
|
|
107
|
+
const db = await getDB();
|
|
108
|
+
return db.get("folders", id);
|
|
109
|
+
}
|
|
110
|
+
async function getAllFolders() {
|
|
111
|
+
const db = await getDB();
|
|
112
|
+
return db.getAllFromIndex("folders", "by-name");
|
|
113
|
+
}
|
|
114
|
+
async function renameFolder(id, name) {
|
|
115
|
+
const db = await getDB();
|
|
116
|
+
const existing = await db.get("folders", id);
|
|
117
|
+
if (!existing) return void 0;
|
|
118
|
+
const updated = {
|
|
119
|
+
...existing,
|
|
120
|
+
name,
|
|
121
|
+
updatedAt: Date.now()
|
|
122
|
+
};
|
|
123
|
+
await db.put("folders", updated);
|
|
124
|
+
return updated;
|
|
125
|
+
}
|
|
126
|
+
async function deleteFolder(id) {
|
|
127
|
+
const db = await getDB();
|
|
128
|
+
const allDrawings = await db.getAll("drawings");
|
|
129
|
+
const tx = db.transaction(["drawings", "folders"], "readwrite");
|
|
130
|
+
for (const drawing of allDrawings) {
|
|
131
|
+
if (drawing.folderId === id) {
|
|
132
|
+
drawing.folderId = null;
|
|
133
|
+
drawing.updatedAt = Date.now();
|
|
134
|
+
await tx.objectStore("drawings").put(drawing);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
await tx.objectStore("folders").delete(id);
|
|
138
|
+
await tx.done;
|
|
139
|
+
}
|
|
82
140
|
|
|
83
141
|
// src/storage/workspaceStore.ts
|
|
84
142
|
var DEFAULT_WORKSPACE_ID = "default";
|
|
@@ -184,6 +242,14 @@ var sv = {
|
|
|
184
242
|
importWorkspace: "Importera arbetsyta",
|
|
185
243
|
exportDrawing: "Spara som .excalidraw",
|
|
186
244
|
importDrawing: "\xD6ppna .excalidraw",
|
|
245
|
+
// Folders
|
|
246
|
+
createFolder: "Skapa mapp",
|
|
247
|
+
renameFolder: "Byt namn p\xE5 mapp",
|
|
248
|
+
deleteFolder: "Ta bort mapp",
|
|
249
|
+
deleteFolderConfirm: "Vill du ta bort denna mapp? Ritningarna flyttas till rotniv\xE5n.",
|
|
250
|
+
moveToFolder: "Flytta till mapp",
|
|
251
|
+
moveToRoot: "Ingen mapp",
|
|
252
|
+
newFolderName: "Ny mapp",
|
|
187
253
|
// Shortcuts
|
|
188
254
|
shortcutNewDrawing: "Ctrl+Alt+N"
|
|
189
255
|
};
|
|
@@ -223,6 +289,14 @@ var en = {
|
|
|
223
289
|
importWorkspace: "Import workspace",
|
|
224
290
|
exportDrawing: "Save as .excalidraw",
|
|
225
291
|
importDrawing: "Open .excalidraw",
|
|
292
|
+
// Folders
|
|
293
|
+
createFolder: "Create folder",
|
|
294
|
+
renameFolder: "Rename folder",
|
|
295
|
+
deleteFolder: "Delete folder",
|
|
296
|
+
deleteFolderConfirm: "Delete this folder? Drawings will be moved to root.",
|
|
297
|
+
moveToFolder: "Move to folder",
|
|
298
|
+
moveToRoot: "No folder",
|
|
299
|
+
newFolderName: "New folder",
|
|
226
300
|
// Shortcuts
|
|
227
301
|
shortcutNewDrawing: "Ctrl+Alt+N"
|
|
228
302
|
};
|
|
@@ -264,17 +338,23 @@ function useWorkspaceLang() {
|
|
|
264
338
|
var TAB_ID = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
265
339
|
var TABS_KEY = "rita-workspace-tabs";
|
|
266
340
|
var TAB_CHANNEL = "rita-workspace-tabs";
|
|
341
|
+
var tabsMapCache = null;
|
|
342
|
+
var tabsMapRaw = null;
|
|
267
343
|
function getTabsMap() {
|
|
268
344
|
try {
|
|
269
|
-
const raw =
|
|
345
|
+
const raw = localStorage.getItem(TABS_KEY) || "{}";
|
|
346
|
+
if (raw === tabsMapRaw && tabsMapCache) return tabsMapCache;
|
|
347
|
+
tabsMapRaw = raw;
|
|
348
|
+
const parsed = JSON.parse(raw);
|
|
270
349
|
const result = {};
|
|
271
|
-
for (const [tabId, value] of Object.entries(
|
|
350
|
+
for (const [tabId, value] of Object.entries(parsed)) {
|
|
272
351
|
if (typeof value === "string") {
|
|
273
352
|
result[tabId] = { drawingId: value, openedAt: 0 };
|
|
274
353
|
} else if (value && typeof value === "object" && "drawingId" in value) {
|
|
275
354
|
result[tabId] = value;
|
|
276
355
|
}
|
|
277
356
|
}
|
|
357
|
+
tabsMapCache = result;
|
|
278
358
|
return result;
|
|
279
359
|
} catch {
|
|
280
360
|
return {};
|
|
@@ -291,7 +371,10 @@ function setTabDrawing(drawingId) {
|
|
|
291
371
|
} else {
|
|
292
372
|
delete tabs[TAB_ID];
|
|
293
373
|
}
|
|
294
|
-
|
|
374
|
+
const json = JSON.stringify(tabs);
|
|
375
|
+
localStorage.setItem(TABS_KEY, json);
|
|
376
|
+
tabsMapCache = tabs;
|
|
377
|
+
tabsMapRaw = json;
|
|
295
378
|
}
|
|
296
379
|
function isDrawingOpenedEarlierInOtherTab(drawingId) {
|
|
297
380
|
const tabs = getTabsMap();
|
|
@@ -305,6 +388,7 @@ function isDrawingOpenedEarlierInOtherTab(drawingId) {
|
|
|
305
388
|
function WorkspaceProvider({ children, lang = "en" }) {
|
|
306
389
|
const [workspace, setWorkspace] = useState(null);
|
|
307
390
|
const [drawings, setDrawings] = useState([]);
|
|
391
|
+
const [folders, setFolders] = useState([]);
|
|
308
392
|
const [activeDrawing, setActiveDrawing2] = useState(null);
|
|
309
393
|
const [isLoading, setIsLoading] = useState(true);
|
|
310
394
|
const [error, setError] = useState(null);
|
|
@@ -380,6 +464,8 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
380
464
|
const allDrawings = await getAllDrawings();
|
|
381
465
|
const wsDrawings = allDrawings.filter((d) => ws.drawingIds.includes(d.id));
|
|
382
466
|
setDrawings(wsDrawings);
|
|
467
|
+
const allFolders = await getAllFolders();
|
|
468
|
+
setFolders(allFolders);
|
|
383
469
|
const lastDrawingId = sessionStorage.getItem("rita-workspace-tab-drawing");
|
|
384
470
|
let active = null;
|
|
385
471
|
if (lastDrawingId) {
|
|
@@ -412,14 +498,20 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
412
498
|
const allDrawings = await getAllDrawings();
|
|
413
499
|
const wsDrawings = allDrawings.filter((d) => workspace.drawingIds.includes(d.id));
|
|
414
500
|
setDrawings(wsDrawings);
|
|
501
|
+
const allFolders = await getAllFolders();
|
|
502
|
+
setFolders(allFolders);
|
|
415
503
|
} catch (err) {
|
|
416
504
|
}
|
|
417
505
|
}, [workspace]);
|
|
418
|
-
const
|
|
506
|
+
const drawingsRef = useRef(drawings);
|
|
507
|
+
drawingsRef.current = drawings;
|
|
508
|
+
const activeDrawingIdRef = useRef(activeDrawing?.id ?? null);
|
|
509
|
+
activeDrawingIdRef.current = activeDrawing?.id ?? null;
|
|
510
|
+
const createNewDrawing = useCallback(async (name, folderId) => {
|
|
419
511
|
if (!workspace) return null;
|
|
420
512
|
try {
|
|
421
|
-
const defaultName = `${t.newDrawing} ${
|
|
422
|
-
const drawing = await createDrawing(name || defaultName);
|
|
513
|
+
const defaultName = `${t.newDrawing} ${drawingsRef.current.length + 1}`;
|
|
514
|
+
const drawing = await createDrawing(name || defaultName, [], {}, folderId);
|
|
423
515
|
await addDrawingToWorkspace(workspace.id, drawing.id);
|
|
424
516
|
setDrawings((prev) => [...prev, drawing]);
|
|
425
517
|
setActiveDrawing2(drawing);
|
|
@@ -434,7 +526,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
434
526
|
setError(err instanceof Error ? err.message : "Failed to create drawing");
|
|
435
527
|
return null;
|
|
436
528
|
}
|
|
437
|
-
}, [workspace,
|
|
529
|
+
}, [workspace, t]);
|
|
438
530
|
const switchDrawing = useCallback(async (id) => {
|
|
439
531
|
if (!workspace) return;
|
|
440
532
|
try {
|
|
@@ -453,23 +545,23 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
453
545
|
const updated = await updateDrawing(id, { name });
|
|
454
546
|
if (updated) {
|
|
455
547
|
setDrawings((prev) => prev.map((d) => d.id === id ? updated : d));
|
|
456
|
-
if (
|
|
548
|
+
if (activeDrawingIdRef.current === id) {
|
|
457
549
|
setActiveDrawing2(updated);
|
|
458
550
|
}
|
|
459
551
|
}
|
|
460
552
|
} catch (err) {
|
|
461
553
|
setError(err instanceof Error ? err.message : "Failed to rename drawing");
|
|
462
554
|
}
|
|
463
|
-
}, [
|
|
555
|
+
}, []);
|
|
464
556
|
const removeDrawing = useCallback(async (id) => {
|
|
465
|
-
if (!workspace ||
|
|
557
|
+
if (!workspace || drawingsRef.current.length <= 1) return;
|
|
466
558
|
try {
|
|
467
559
|
await deleteDrawing(id);
|
|
468
560
|
const updatedWorkspace = await removeDrawingFromWorkspace(workspace.id, id);
|
|
469
561
|
setDrawings((prev) => prev.filter((d) => d.id !== id));
|
|
470
562
|
if (updatedWorkspace) {
|
|
471
563
|
setWorkspace(updatedWorkspace);
|
|
472
|
-
if (
|
|
564
|
+
if (activeDrawingIdRef.current === id && updatedWorkspace.activeDrawingId) {
|
|
473
565
|
const newActive = await getDrawing(updatedWorkspace.activeDrawingId);
|
|
474
566
|
setActiveDrawing2(newActive || null);
|
|
475
567
|
}
|
|
@@ -477,11 +569,11 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
477
569
|
} catch (err) {
|
|
478
570
|
setError(err instanceof Error ? err.message : "Failed to delete drawing");
|
|
479
571
|
}
|
|
480
|
-
}, [workspace
|
|
572
|
+
}, [workspace]);
|
|
481
573
|
const duplicateCurrentDrawing = useCallback(async () => {
|
|
482
|
-
if (!
|
|
574
|
+
if (!activeDrawingIdRef.current || !workspace) return null;
|
|
483
575
|
try {
|
|
484
|
-
const duplicate = await duplicateDrawing(
|
|
576
|
+
const duplicate = await duplicateDrawing(activeDrawingIdRef.current);
|
|
485
577
|
if (duplicate) {
|
|
486
578
|
await addDrawingToWorkspace(workspace.id, duplicate.id);
|
|
487
579
|
setDrawings((prev) => [...prev, duplicate]);
|
|
@@ -496,7 +588,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
496
588
|
setError(err instanceof Error ? err.message : "Failed to duplicate drawing");
|
|
497
589
|
return null;
|
|
498
590
|
}
|
|
499
|
-
}, [
|
|
591
|
+
}, [workspace]);
|
|
500
592
|
const saveCurrentDrawing = useCallback(async (elements, appState, files) => {
|
|
501
593
|
if (!activeDrawing) return;
|
|
502
594
|
try {
|
|
@@ -652,9 +744,52 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
652
744
|
setError(err instanceof Error ? err.message : "Failed to import drawing");
|
|
653
745
|
}
|
|
654
746
|
}, [workspace, t]);
|
|
747
|
+
const createFolder2 = useCallback(async (name) => {
|
|
748
|
+
try {
|
|
749
|
+
const folder = await createFolder(name);
|
|
750
|
+
setFolders((prev) => [...prev, folder]);
|
|
751
|
+
return folder;
|
|
752
|
+
} catch (err) {
|
|
753
|
+
setError(err instanceof Error ? err.message : "Failed to create folder");
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
}, []);
|
|
757
|
+
const renameFolder2 = useCallback(async (id, name) => {
|
|
758
|
+
try {
|
|
759
|
+
const updated = await renameFolder(id, name);
|
|
760
|
+
if (updated) {
|
|
761
|
+
setFolders((prev) => prev.map((f) => f.id === id ? updated : f));
|
|
762
|
+
}
|
|
763
|
+
} catch (err) {
|
|
764
|
+
setError(err instanceof Error ? err.message : "Failed to rename folder");
|
|
765
|
+
}
|
|
766
|
+
}, []);
|
|
767
|
+
const deleteFolder2 = useCallback(async (id) => {
|
|
768
|
+
try {
|
|
769
|
+
await deleteFolder(id);
|
|
770
|
+
setFolders((prev) => prev.filter((f) => f.id !== id));
|
|
771
|
+
setDrawings((prev) => prev.map((d) => d.folderId === id ? { ...d, folderId: null } : d));
|
|
772
|
+
} catch (err) {
|
|
773
|
+
setError(err instanceof Error ? err.message : "Failed to delete folder");
|
|
774
|
+
}
|
|
775
|
+
}, []);
|
|
776
|
+
const moveDrawingToFolder2 = useCallback(async (drawingId, folderId) => {
|
|
777
|
+
try {
|
|
778
|
+
const updated = await moveDrawingToFolder(drawingId, folderId);
|
|
779
|
+
if (updated) {
|
|
780
|
+
setDrawings((prev) => prev.map((d) => d.id === drawingId ? updated : d));
|
|
781
|
+
if (activeDrawingIdRef.current === drawingId) {
|
|
782
|
+
setActiveDrawing2(updated);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
} catch (err) {
|
|
786
|
+
setError(err instanceof Error ? err.message : "Failed to move drawing");
|
|
787
|
+
}
|
|
788
|
+
}, []);
|
|
655
789
|
const value = {
|
|
656
790
|
workspace,
|
|
657
791
|
drawings,
|
|
792
|
+
folders,
|
|
658
793
|
activeDrawing,
|
|
659
794
|
isLoading,
|
|
660
795
|
error,
|
|
@@ -666,6 +801,10 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
666
801
|
renameDrawing,
|
|
667
802
|
removeDrawing,
|
|
668
803
|
duplicateCurrentDrawing,
|
|
804
|
+
createFolder: createFolder2,
|
|
805
|
+
renameFolder: renameFolder2,
|
|
806
|
+
deleteFolder: deleteFolder2,
|
|
807
|
+
moveDrawingToFolder: moveDrawingToFolder2,
|
|
669
808
|
saveCurrentDrawing,
|
|
670
809
|
saveDrawingById,
|
|
671
810
|
refreshDrawings,
|
|
@@ -981,7 +1120,7 @@ var WorkspaceMenuItems = ({
|
|
|
981
1120
|
};
|
|
982
1121
|
|
|
983
1122
|
// src/ui/Dialog/DrawingsDialog.tsx
|
|
984
|
-
import { useState as useState4, useCallback as useCallback2, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
1123
|
+
import { useState as useState4, useCallback as useCallback2, useMemo, useRef as useRef3, useEffect as useEffect3 } from "react";
|
|
985
1124
|
import { Fragment as Fragment3, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
986
1125
|
var ActionButton = ({ icon, label, description, onClick, primary }) => /* @__PURE__ */ jsxs4(
|
|
987
1126
|
"button",
|
|
@@ -1019,11 +1158,16 @@ var DrawingsDialog = ({
|
|
|
1019
1158
|
}) => {
|
|
1020
1159
|
const {
|
|
1021
1160
|
drawings,
|
|
1161
|
+
folders,
|
|
1022
1162
|
activeDrawing,
|
|
1023
1163
|
switchDrawing,
|
|
1024
1164
|
createNewDrawing,
|
|
1025
1165
|
renameDrawing,
|
|
1026
1166
|
removeDrawing,
|
|
1167
|
+
createFolder: createFolder2,
|
|
1168
|
+
renameFolder: renameFolder2,
|
|
1169
|
+
deleteFolder: deleteFolder2,
|
|
1170
|
+
moveDrawingToFolder: moveDrawingToFolder2,
|
|
1027
1171
|
exportWorkspace,
|
|
1028
1172
|
importWorkspace,
|
|
1029
1173
|
exportDrawingAsExcalidraw,
|
|
@@ -1037,21 +1181,44 @@ var DrawingsDialog = ({
|
|
|
1037
1181
|
const [editingId, setEditingId] = useState4(null);
|
|
1038
1182
|
const [editName, setEditName] = useState4("");
|
|
1039
1183
|
const [confirmDeleteId, setConfirmDeleteId] = useState4(null);
|
|
1184
|
+
const [switchingId, setSwitchingId] = useState4(null);
|
|
1185
|
+
const [expandedFolders, setExpandedFolders] = useState4(/* @__PURE__ */ new Set());
|
|
1186
|
+
const [creatingFolder, setCreatingFolder] = useState4(false);
|
|
1187
|
+
const [newFolderName, setNewFolderName] = useState4("");
|
|
1188
|
+
const [editingFolderId, setEditingFolderId] = useState4(null);
|
|
1189
|
+
const [editFolderName, setEditFolderName] = useState4("");
|
|
1190
|
+
const [confirmDeleteFolderId, setConfirmDeleteFolderId] = useState4(null);
|
|
1191
|
+
const [movingDrawingId, setMovingDrawingId] = useState4(null);
|
|
1192
|
+
const [searchQuery, setSearchQuery] = useState4("");
|
|
1193
|
+
const [busyFolderId, setBusyFolderId] = useState4(null);
|
|
1194
|
+
const [isRefreshing, setIsRefreshing] = useState4(false);
|
|
1195
|
+
const [draggingDrawingId, setDraggingDrawingId] = useState4(null);
|
|
1196
|
+
const [dropTargetFolderId, setDropTargetFolderId] = useState4(null);
|
|
1040
1197
|
const [position, setPosition] = useState4(null);
|
|
1041
1198
|
const dragRef = useRef3(null);
|
|
1042
1199
|
const dialogRef = useRef3(null);
|
|
1200
|
+
const newFolderInputRef = useRef3(null);
|
|
1043
1201
|
const prevOpenRef = useRef3(false);
|
|
1044
1202
|
useEffect3(() => {
|
|
1045
1203
|
if (open) {
|
|
1046
|
-
|
|
1204
|
+
setIsRefreshing(true);
|
|
1205
|
+
refreshDrawings().finally(() => setIsRefreshing(false));
|
|
1047
1206
|
if (!prevOpenRef.current) {
|
|
1048
1207
|
setPosition(null);
|
|
1208
|
+
setSearchQuery("");
|
|
1209
|
+
setExpandedFolders(new Set(folders.map((f) => f.id)));
|
|
1049
1210
|
}
|
|
1050
1211
|
}
|
|
1051
1212
|
prevOpenRef.current = open;
|
|
1052
|
-
}, [open, refreshDrawings]);
|
|
1213
|
+
}, [open, refreshDrawings, folders]);
|
|
1214
|
+
useEffect3(() => {
|
|
1215
|
+
if (creatingFolder && newFolderInputRef.current) {
|
|
1216
|
+
newFolderInputRef.current.focus();
|
|
1217
|
+
}
|
|
1218
|
+
}, [creatingFolder]);
|
|
1053
1219
|
const handleMouseDown = useCallback2((e) => {
|
|
1054
1220
|
if (e.target.closest("button")) return;
|
|
1221
|
+
if (e.target.closest("input")) return;
|
|
1055
1222
|
if (!dialogRef.current) return;
|
|
1056
1223
|
const rect = dialogRef.current.getBoundingClientRect();
|
|
1057
1224
|
const currentX = position?.x ?? rect.left;
|
|
@@ -1072,11 +1239,13 @@ var DrawingsDialog = ({
|
|
|
1072
1239
|
document.addEventListener("mouseup", handleMouseUp);
|
|
1073
1240
|
}, [position]);
|
|
1074
1241
|
const handleSelect = useCallback2(async (drawing) => {
|
|
1242
|
+
setSwitchingId(drawing.id);
|
|
1075
1243
|
await switchDrawing(drawing.id);
|
|
1076
1244
|
onDrawingSelect?.(drawing);
|
|
1245
|
+
setSwitchingId(null);
|
|
1077
1246
|
}, [switchDrawing, onDrawingSelect]);
|
|
1078
|
-
const handleCreate = useCallback2(async () => {
|
|
1079
|
-
const newDrawing = await createNewDrawing();
|
|
1247
|
+
const handleCreate = useCallback2(async (folderId) => {
|
|
1248
|
+
const newDrawing = await createNewDrawing(void 0, folderId);
|
|
1080
1249
|
if (newDrawing) onDrawingSelect?.(newDrawing);
|
|
1081
1250
|
}, [createNewDrawing, onDrawingSelect]);
|
|
1082
1251
|
const handleStartEdit = useCallback2((drawing) => {
|
|
@@ -1098,6 +1267,48 @@ var DrawingsDialog = ({
|
|
|
1098
1267
|
await removeDrawing(id);
|
|
1099
1268
|
setConfirmDeleteId(null);
|
|
1100
1269
|
}, [removeDrawing]);
|
|
1270
|
+
const handleCreateFolder = useCallback2(async () => {
|
|
1271
|
+
if (newFolderName.trim()) {
|
|
1272
|
+
setBusyFolderId("__creating__");
|
|
1273
|
+
const folder = await createFolder2(newFolderName.trim());
|
|
1274
|
+
if (folder) {
|
|
1275
|
+
setExpandedFolders((prev) => /* @__PURE__ */ new Set([...prev, folder.id]));
|
|
1276
|
+
}
|
|
1277
|
+
setBusyFolderId(null);
|
|
1278
|
+
}
|
|
1279
|
+
setCreatingFolder(false);
|
|
1280
|
+
setNewFolderName("");
|
|
1281
|
+
}, [newFolderName, createFolder2]);
|
|
1282
|
+
const handleSaveFolderEdit = useCallback2(async () => {
|
|
1283
|
+
if (editingFolderId && editFolderName.trim()) {
|
|
1284
|
+
setBusyFolderId(editingFolderId);
|
|
1285
|
+
await renameFolder2(editingFolderId, editFolderName.trim());
|
|
1286
|
+
setBusyFolderId(null);
|
|
1287
|
+
}
|
|
1288
|
+
setEditingFolderId(null);
|
|
1289
|
+
setEditFolderName("");
|
|
1290
|
+
}, [editingFolderId, editFolderName, renameFolder2]);
|
|
1291
|
+
const handleDeleteFolder = useCallback2(async (id) => {
|
|
1292
|
+
setBusyFolderId(id);
|
|
1293
|
+
await deleteFolder2(id);
|
|
1294
|
+
setBusyFolderId(null);
|
|
1295
|
+
setConfirmDeleteFolderId(null);
|
|
1296
|
+
}, [deleteFolder2]);
|
|
1297
|
+
const handleMoveToFolder = useCallback2(async (drawingId, folderId) => {
|
|
1298
|
+
await moveDrawingToFolder2(drawingId, folderId);
|
|
1299
|
+
setMovingDrawingId(null);
|
|
1300
|
+
}, [moveDrawingToFolder2]);
|
|
1301
|
+
const toggleFolder = useCallback2((folderId) => {
|
|
1302
|
+
setExpandedFolders((prev) => {
|
|
1303
|
+
const next = new Set(prev);
|
|
1304
|
+
if (next.has(folderId)) {
|
|
1305
|
+
next.delete(folderId);
|
|
1306
|
+
} else {
|
|
1307
|
+
next.add(folderId);
|
|
1308
|
+
}
|
|
1309
|
+
return next;
|
|
1310
|
+
});
|
|
1311
|
+
}, []);
|
|
1101
1312
|
const getLocale = () => {
|
|
1102
1313
|
if (!effectiveLang) return "en-US";
|
|
1103
1314
|
const baseLang = effectiveLang.split("-")[0].toLowerCase();
|
|
@@ -1112,6 +1323,17 @@ var DrawingsDialog = ({
|
|
|
1112
1323
|
});
|
|
1113
1324
|
};
|
|
1114
1325
|
if (!open) return null;
|
|
1326
|
+
const { rootDrawings, drawingsByFolder, filteredFolders } = useMemo(() => {
|
|
1327
|
+
const query = searchQuery.toLowerCase().trim();
|
|
1328
|
+
const filtered = query ? drawings.filter((d) => d.name.toLowerCase().includes(query)) : drawings;
|
|
1329
|
+
const root = filtered.filter((d) => !d.folderId);
|
|
1330
|
+
const byFolder = {};
|
|
1331
|
+
for (const folder of folders) {
|
|
1332
|
+
byFolder[folder.id] = filtered.filter((d) => d.folderId === folder.id);
|
|
1333
|
+
}
|
|
1334
|
+
const foldersFiltered = query ? folders.filter((f) => f.name.toLowerCase().includes(query) || (byFolder[f.id] || []).length > 0) : folders;
|
|
1335
|
+
return { rootDrawings: root, drawingsByFolder: byFolder, filteredFolders: foldersFiltered };
|
|
1336
|
+
}, [drawings, folders, searchQuery]);
|
|
1115
1337
|
const dialogStyle = position ? {
|
|
1116
1338
|
position: "fixed",
|
|
1117
1339
|
left: position.x,
|
|
@@ -1145,6 +1367,355 @@ var DrawingsDialog = ({
|
|
|
1145
1367
|
color: "var(--text-secondary-color, #888)",
|
|
1146
1368
|
padding: "16px 20px 8px"
|
|
1147
1369
|
};
|
|
1370
|
+
const renderDrawingRow = (drawing) => /* @__PURE__ */ jsxs4(
|
|
1371
|
+
"div",
|
|
1372
|
+
{
|
|
1373
|
+
draggable: !editingId && !confirmDeleteId,
|
|
1374
|
+
onDragStart: (e) => {
|
|
1375
|
+
setDraggingDrawingId(drawing.id);
|
|
1376
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1377
|
+
e.dataTransfer.setData("text/plain", drawing.id);
|
|
1378
|
+
},
|
|
1379
|
+
onDragEnd: () => {
|
|
1380
|
+
setDraggingDrawingId(null);
|
|
1381
|
+
setDropTargetFolderId("__none__");
|
|
1382
|
+
setTimeout(() => setDropTargetFolderId(null), 0);
|
|
1383
|
+
},
|
|
1384
|
+
onClick: () => {
|
|
1385
|
+
if (editingId || confirmDeleteId || switchingId || movingDrawingId) return;
|
|
1386
|
+
if (activeDrawing?.id !== drawing.id) handleSelect(drawing);
|
|
1387
|
+
},
|
|
1388
|
+
style: {
|
|
1389
|
+
padding: "10px 12px",
|
|
1390
|
+
display: "flex",
|
|
1391
|
+
alignItems: "center",
|
|
1392
|
+
gap: "12px",
|
|
1393
|
+
borderRadius: "8px",
|
|
1394
|
+
marginBottom: "4px",
|
|
1395
|
+
cursor: draggingDrawingId ? "grabbing" : editingId || confirmDeleteId || switchingId ? "default" : "pointer",
|
|
1396
|
+
backgroundColor: activeDrawing?.id === drawing.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.1))" : "transparent",
|
|
1397
|
+
transition: "background-color 0.15s",
|
|
1398
|
+
opacity: draggingDrawingId === drawing.id ? 0.4 : switchingId && switchingId !== drawing.id ? 0.5 : 1
|
|
1399
|
+
},
|
|
1400
|
+
children: [
|
|
1401
|
+
renderThumbnail && /* @__PURE__ */ jsx6("div", { style: {
|
|
1402
|
+
width: "64px",
|
|
1403
|
+
height: "48px",
|
|
1404
|
+
flexShrink: 0,
|
|
1405
|
+
borderRadius: "4px",
|
|
1406
|
+
overflow: "hidden",
|
|
1407
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
1408
|
+
backgroundColor: "#fff",
|
|
1409
|
+
display: "flex",
|
|
1410
|
+
alignItems: "center",
|
|
1411
|
+
justifyContent: "center"
|
|
1412
|
+
}, children: renderThumbnail(drawing) }),
|
|
1413
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1, minWidth: 0 }, children: editingId === drawing.id ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "6px", alignItems: "center" }, children: [
|
|
1414
|
+
/* @__PURE__ */ jsx6(
|
|
1415
|
+
"input",
|
|
1416
|
+
{
|
|
1417
|
+
type: "text",
|
|
1418
|
+
value: editName,
|
|
1419
|
+
onChange: (e) => setEditName(e.target.value),
|
|
1420
|
+
onKeyDown: (e) => {
|
|
1421
|
+
if (e.key === "Enter") handleSaveEdit();
|
|
1422
|
+
if (e.key === "Escape") handleCancelEdit();
|
|
1423
|
+
},
|
|
1424
|
+
onClick: (e) => e.stopPropagation(),
|
|
1425
|
+
autoFocus: true,
|
|
1426
|
+
style: {
|
|
1427
|
+
flex: 1,
|
|
1428
|
+
padding: "4px 8px",
|
|
1429
|
+
fontSize: "14px",
|
|
1430
|
+
border: "1px solid var(--color-primary, #6c63ff)",
|
|
1431
|
+
borderRadius: "4px",
|
|
1432
|
+
outline: "none"
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
),
|
|
1436
|
+
/* @__PURE__ */ jsx6(
|
|
1437
|
+
"button",
|
|
1438
|
+
{
|
|
1439
|
+
onClick: (e) => {
|
|
1440
|
+
e.stopPropagation();
|
|
1441
|
+
handleSaveEdit();
|
|
1442
|
+
},
|
|
1443
|
+
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "var(--color-primary, #6c63ff)", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1444
|
+
children: t.save
|
|
1445
|
+
}
|
|
1446
|
+
),
|
|
1447
|
+
/* @__PURE__ */ jsx6(
|
|
1448
|
+
"button",
|
|
1449
|
+
{
|
|
1450
|
+
onClick: (e) => {
|
|
1451
|
+
e.stopPropagation();
|
|
1452
|
+
handleCancelEdit();
|
|
1453
|
+
},
|
|
1454
|
+
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", color: "inherit" },
|
|
1455
|
+
children: t.cancel
|
|
1456
|
+
}
|
|
1457
|
+
)
|
|
1458
|
+
] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1459
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1460
|
+
/* @__PURE__ */ jsxs4(
|
|
1461
|
+
"span",
|
|
1462
|
+
{
|
|
1463
|
+
onClick: (e) => {
|
|
1464
|
+
e.stopPropagation();
|
|
1465
|
+
handleStartEdit(drawing);
|
|
1466
|
+
},
|
|
1467
|
+
style: {
|
|
1468
|
+
fontWeight: activeDrawing?.id === drawing.id ? 600 : 400,
|
|
1469
|
+
fontSize: "14px",
|
|
1470
|
+
overflow: "hidden",
|
|
1471
|
+
textOverflow: "ellipsis",
|
|
1472
|
+
whiteSpace: "nowrap",
|
|
1473
|
+
cursor: "text"
|
|
1474
|
+
},
|
|
1475
|
+
title: t.rename,
|
|
1476
|
+
children: [
|
|
1477
|
+
switchingId === drawing.id ? /* @__PURE__ */ jsx6("span", { style: { display: "inline-block", animation: "spin 1s linear infinite", marginRight: "4px" }, children: "\u23F3" }) : activeDrawing?.id === drawing.id ? "\u2713 " : "",
|
|
1478
|
+
drawing.name
|
|
1479
|
+
]
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
/* @__PURE__ */ jsx6(
|
|
1483
|
+
"button",
|
|
1484
|
+
{
|
|
1485
|
+
onClick: (e) => {
|
|
1486
|
+
e.stopPropagation();
|
|
1487
|
+
handleStartEdit(drawing);
|
|
1488
|
+
},
|
|
1489
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "0 2px", fontSize: "12px", opacity: 0.4, flexShrink: 0 },
|
|
1490
|
+
title: t.rename,
|
|
1491
|
+
children: "\u270F\uFE0F"
|
|
1492
|
+
}
|
|
1493
|
+
)
|
|
1494
|
+
] }),
|
|
1495
|
+
/* @__PURE__ */ jsxs4("div", { style: { fontSize: "11px", color: "var(--text-secondary-color, #888)", marginTop: "1px" }, children: [
|
|
1496
|
+
t.modified,
|
|
1497
|
+
": ",
|
|
1498
|
+
formatDate(drawing.updatedAt)
|
|
1499
|
+
] })
|
|
1500
|
+
] }) }),
|
|
1501
|
+
/* @__PURE__ */ jsx6("div", { style: { display: "flex", gap: "2px", alignItems: "center" }, children: confirmDeleteId === drawing.id ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1502
|
+
/* @__PURE__ */ jsx6(
|
|
1503
|
+
"button",
|
|
1504
|
+
{
|
|
1505
|
+
onClick: (e) => {
|
|
1506
|
+
e.stopPropagation();
|
|
1507
|
+
handleDelete(drawing.id);
|
|
1508
|
+
},
|
|
1509
|
+
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "#dc3545", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1510
|
+
children: t.delete
|
|
1511
|
+
}
|
|
1512
|
+
),
|
|
1513
|
+
/* @__PURE__ */ jsx6(
|
|
1514
|
+
"button",
|
|
1515
|
+
{
|
|
1516
|
+
onClick: (e) => {
|
|
1517
|
+
e.stopPropagation();
|
|
1518
|
+
setConfirmDeleteId(null);
|
|
1519
|
+
},
|
|
1520
|
+
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", color: "inherit" },
|
|
1521
|
+
children: t.cancel
|
|
1522
|
+
}
|
|
1523
|
+
)
|
|
1524
|
+
] }) : movingDrawingId === drawing.id ? /* @__PURE__ */ jsxs4("div", { onClick: (e) => e.stopPropagation(), style: { display: "flex", flexDirection: "column", gap: "2px", fontSize: "12px" }, children: [
|
|
1525
|
+
/* @__PURE__ */ jsx6(
|
|
1526
|
+
"button",
|
|
1527
|
+
{
|
|
1528
|
+
onClick: () => handleMoveToFolder(drawing.id, null),
|
|
1529
|
+
style: { padding: "3px 8px", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", backgroundColor: !drawing.folderId ? "var(--color-primary-light, rgba(108, 99, 255, 0.1))" : "transparent", color: "inherit", textAlign: "left" },
|
|
1530
|
+
children: t.moveToRoot
|
|
1531
|
+
}
|
|
1532
|
+
),
|
|
1533
|
+
folders.map((folder) => /* @__PURE__ */ jsxs4(
|
|
1534
|
+
"button",
|
|
1535
|
+
{
|
|
1536
|
+
onClick: () => handleMoveToFolder(drawing.id, folder.id),
|
|
1537
|
+
style: { padding: "3px 8px", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", backgroundColor: drawing.folderId === folder.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.1))" : "transparent", color: "inherit", textAlign: "left" },
|
|
1538
|
+
children: [
|
|
1539
|
+
"\u{1F4C1} ",
|
|
1540
|
+
folder.name
|
|
1541
|
+
]
|
|
1542
|
+
},
|
|
1543
|
+
folder.id
|
|
1544
|
+
)),
|
|
1545
|
+
/* @__PURE__ */ jsx6(
|
|
1546
|
+
"button",
|
|
1547
|
+
{
|
|
1548
|
+
onClick: () => setMovingDrawingId(null),
|
|
1549
|
+
style: { padding: "3px 8px", border: "none", cursor: "pointer", backgroundColor: "transparent", color: "var(--text-secondary-color, #888)", textAlign: "left" },
|
|
1550
|
+
children: t.cancel
|
|
1551
|
+
}
|
|
1552
|
+
)
|
|
1553
|
+
] }) : editingId !== drawing.id && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1554
|
+
folders.length > 0 && /* @__PURE__ */ jsx6(
|
|
1555
|
+
"button",
|
|
1556
|
+
{
|
|
1557
|
+
onClick: (e) => {
|
|
1558
|
+
e.stopPropagation();
|
|
1559
|
+
setMovingDrawingId(drawing.id);
|
|
1560
|
+
},
|
|
1561
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "4px", fontSize: "14px", opacity: 0.5 },
|
|
1562
|
+
title: t.moveToFolder,
|
|
1563
|
+
children: "\u{1F4C1}"
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ jsx6(
|
|
1567
|
+
"button",
|
|
1568
|
+
{
|
|
1569
|
+
onClick: (e) => {
|
|
1570
|
+
e.stopPropagation();
|
|
1571
|
+
exportDrawingAsExcalidraw(drawing.id);
|
|
1572
|
+
},
|
|
1573
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "4px", fontSize: "14px", opacity: 0.5 },
|
|
1574
|
+
title: t.exportDrawing,
|
|
1575
|
+
children: "\u{1F4BE}"
|
|
1576
|
+
}
|
|
1577
|
+
),
|
|
1578
|
+
/* @__PURE__ */ jsx6(
|
|
1579
|
+
"button",
|
|
1580
|
+
{
|
|
1581
|
+
onClick: (e) => {
|
|
1582
|
+
e.stopPropagation();
|
|
1583
|
+
setConfirmDeleteId(drawing.id);
|
|
1584
|
+
},
|
|
1585
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "4px", fontSize: "14px", opacity: 0.5 },
|
|
1586
|
+
title: t.delete,
|
|
1587
|
+
disabled: drawings.length <= 1,
|
|
1588
|
+
children: "\u{1F5D1}\uFE0F"
|
|
1589
|
+
}
|
|
1590
|
+
)
|
|
1591
|
+
] }) })
|
|
1592
|
+
]
|
|
1593
|
+
},
|
|
1594
|
+
drawing.id
|
|
1595
|
+
);
|
|
1596
|
+
const renderFolderGroup = (folder) => {
|
|
1597
|
+
const folderDrawings = drawingsByFolder[folder.id] || [];
|
|
1598
|
+
const isExpanded = expandedFolders.has(folder.id);
|
|
1599
|
+
return /* @__PURE__ */ jsxs4("div", { style: { marginBottom: "4px" }, children: [
|
|
1600
|
+
/* @__PURE__ */ jsxs4(
|
|
1601
|
+
"div",
|
|
1602
|
+
{
|
|
1603
|
+
onClick: () => toggleFolder(folder.id),
|
|
1604
|
+
onDragOver: (e) => {
|
|
1605
|
+
if (!draggingDrawingId) return;
|
|
1606
|
+
e.preventDefault();
|
|
1607
|
+
e.dataTransfer.dropEffect = "move";
|
|
1608
|
+
setDropTargetFolderId(folder.id);
|
|
1609
|
+
},
|
|
1610
|
+
onDragLeave: () => {
|
|
1611
|
+
if (dropTargetFolderId === folder.id) setDropTargetFolderId(null);
|
|
1612
|
+
},
|
|
1613
|
+
onDrop: (e) => {
|
|
1614
|
+
e.preventDefault();
|
|
1615
|
+
if (draggingDrawingId) {
|
|
1616
|
+
handleMoveToFolder(draggingDrawingId, folder.id);
|
|
1617
|
+
setDraggingDrawingId(null);
|
|
1618
|
+
setDropTargetFolderId(null);
|
|
1619
|
+
setExpandedFolders((prev) => /* @__PURE__ */ new Set([...prev, folder.id]));
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
style: {
|
|
1623
|
+
padding: "8px 12px",
|
|
1624
|
+
display: "flex",
|
|
1625
|
+
alignItems: "center",
|
|
1626
|
+
gap: "8px",
|
|
1627
|
+
borderRadius: "8px",
|
|
1628
|
+
cursor: "pointer",
|
|
1629
|
+
backgroundColor: dropTargetFolderId === folder.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.2))" : "var(--color-surface-mid, rgba(0, 0, 0, 0.03))",
|
|
1630
|
+
border: dropTargetFolderId === folder.id ? "2px dashed var(--color-primary, #6c63ff)" : "2px solid transparent",
|
|
1631
|
+
transition: "background-color 0.15s, border-color 0.15s"
|
|
1632
|
+
},
|
|
1633
|
+
children: [
|
|
1634
|
+
/* @__PURE__ */ jsx6("span", { style: { fontSize: "12px", width: "16px", textAlign: "center", flexShrink: 0 }, children: isExpanded ? "\u25BC" : "\u25B6" }),
|
|
1635
|
+
/* @__PURE__ */ jsx6("span", { style: { fontSize: "16px", flexShrink: 0 }, children: busyFolderId === folder.id ? /* @__PURE__ */ jsx6("span", { style: { display: "inline-block", animation: "spin 1s linear infinite" }, children: "\u23F3" }) : "\u{1F4C1}" }),
|
|
1636
|
+
/* @__PURE__ */ jsx6("div", { style: { flex: 1, minWidth: 0 }, children: editingFolderId === folder.id ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "6px", alignItems: "center" }, onClick: (e) => e.stopPropagation(), children: [
|
|
1637
|
+
/* @__PURE__ */ jsx6(
|
|
1638
|
+
"input",
|
|
1639
|
+
{
|
|
1640
|
+
type: "text",
|
|
1641
|
+
value: editFolderName,
|
|
1642
|
+
onChange: (e) => setEditFolderName(e.target.value),
|
|
1643
|
+
onKeyDown: (e) => {
|
|
1644
|
+
if (e.key === "Enter") handleSaveFolderEdit();
|
|
1645
|
+
if (e.key === "Escape") {
|
|
1646
|
+
setEditingFolderId(null);
|
|
1647
|
+
setEditFolderName("");
|
|
1648
|
+
}
|
|
1649
|
+
},
|
|
1650
|
+
autoFocus: true,
|
|
1651
|
+
style: { flex: 1, padding: "2px 6px", fontSize: "14px", border: "1px solid var(--color-primary, #6c63ff)", borderRadius: "4px", outline: "none" }
|
|
1652
|
+
}
|
|
1653
|
+
),
|
|
1654
|
+
/* @__PURE__ */ jsx6(
|
|
1655
|
+
"button",
|
|
1656
|
+
{
|
|
1657
|
+
onClick: handleSaveFolderEdit,
|
|
1658
|
+
style: { padding: "2px 8px", fontSize: "12px", backgroundColor: "var(--color-primary, #6c63ff)", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1659
|
+
children: t.save
|
|
1660
|
+
}
|
|
1661
|
+
)
|
|
1662
|
+
] }) : /* @__PURE__ */ jsxs4("span", { style: { fontWeight: 600, fontSize: "14px" }, children: [
|
|
1663
|
+
folder.name,
|
|
1664
|
+
/* @__PURE__ */ jsxs4("span", { style: { fontWeight: 400, fontSize: "12px", color: "var(--text-secondary-color, #888)", marginLeft: "6px" }, children: [
|
|
1665
|
+
"(",
|
|
1666
|
+
folderDrawings.length,
|
|
1667
|
+
")"
|
|
1668
|
+
] })
|
|
1669
|
+
] }) }),
|
|
1670
|
+
confirmDeleteFolderId === folder.id ? /* @__PURE__ */ jsxs4("div", { onClick: (e) => e.stopPropagation(), style: { display: "flex", gap: "4px" }, children: [
|
|
1671
|
+
/* @__PURE__ */ jsx6(
|
|
1672
|
+
"button",
|
|
1673
|
+
{
|
|
1674
|
+
onClick: () => handleDeleteFolder(folder.id),
|
|
1675
|
+
style: { padding: "2px 8px", fontSize: "12px", backgroundColor: "#dc3545", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1676
|
+
children: t.delete
|
|
1677
|
+
}
|
|
1678
|
+
),
|
|
1679
|
+
/* @__PURE__ */ jsx6(
|
|
1680
|
+
"button",
|
|
1681
|
+
{
|
|
1682
|
+
onClick: () => setConfirmDeleteFolderId(null),
|
|
1683
|
+
style: { padding: "2px 8px", fontSize: "12px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", color: "inherit" },
|
|
1684
|
+
children: t.cancel
|
|
1685
|
+
}
|
|
1686
|
+
)
|
|
1687
|
+
] }) : editingFolderId !== folder.id && /* @__PURE__ */ jsxs4("div", { onClick: (e) => e.stopPropagation(), style: { display: "flex", gap: "2px" }, children: [
|
|
1688
|
+
/* @__PURE__ */ jsx6(
|
|
1689
|
+
"button",
|
|
1690
|
+
{
|
|
1691
|
+
onClick: () => {
|
|
1692
|
+
setEditingFolderId(folder.id);
|
|
1693
|
+
setEditFolderName(folder.name);
|
|
1694
|
+
},
|
|
1695
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "2px", fontSize: "12px", opacity: 0.5 },
|
|
1696
|
+
title: t.renameFolder,
|
|
1697
|
+
children: "\u270F\uFE0F"
|
|
1698
|
+
}
|
|
1699
|
+
),
|
|
1700
|
+
/* @__PURE__ */ jsx6(
|
|
1701
|
+
"button",
|
|
1702
|
+
{
|
|
1703
|
+
onClick: () => setConfirmDeleteFolderId(folder.id),
|
|
1704
|
+
style: { background: "none", border: "none", cursor: "pointer", padding: "2px", fontSize: "12px", opacity: 0.5 },
|
|
1705
|
+
title: t.deleteFolder,
|
|
1706
|
+
children: "\u{1F5D1}\uFE0F"
|
|
1707
|
+
}
|
|
1708
|
+
)
|
|
1709
|
+
] })
|
|
1710
|
+
]
|
|
1711
|
+
}
|
|
1712
|
+
),
|
|
1713
|
+
isExpanded && /* @__PURE__ */ jsxs4("div", { style: { paddingLeft: "24px", marginTop: "2px" }, children: [
|
|
1714
|
+
folderDrawings.length === 0 && /* @__PURE__ */ jsx6("div", { style: { padding: "8px 12px", fontSize: "12px", color: "var(--text-secondary-color, #888)", fontStyle: "italic" }, children: t.noDrawingsYet }),
|
|
1715
|
+
folderDrawings.map(renderDrawingRow)
|
|
1716
|
+
] })
|
|
1717
|
+
] }, folder.id);
|
|
1718
|
+
};
|
|
1148
1719
|
return /* @__PURE__ */ jsx6(
|
|
1149
1720
|
"div",
|
|
1150
1721
|
{
|
|
@@ -1200,181 +1771,65 @@ var DrawingsDialog = ({
|
|
|
1200
1771
|
]
|
|
1201
1772
|
}
|
|
1202
1773
|
),
|
|
1774
|
+
drawings.length > 3 && /* @__PURE__ */ jsx6("div", { style: { padding: "8px 20px", borderBottom: "1px solid var(--default-border-color, #e0e0e0)" }, children: /* @__PURE__ */ jsx6(
|
|
1775
|
+
"input",
|
|
1776
|
+
{
|
|
1777
|
+
type: "text",
|
|
1778
|
+
value: searchQuery,
|
|
1779
|
+
onChange: (e) => setSearchQuery(e.target.value),
|
|
1780
|
+
placeholder: "\u{1F50D}",
|
|
1781
|
+
style: {
|
|
1782
|
+
width: "100%",
|
|
1783
|
+
padding: "6px 12px",
|
|
1784
|
+
fontSize: "14px",
|
|
1785
|
+
border: "1px solid var(--default-border-color, #e0e0e0)",
|
|
1786
|
+
borderRadius: "6px",
|
|
1787
|
+
outline: "none",
|
|
1788
|
+
backgroundColor: "transparent",
|
|
1789
|
+
color: "inherit",
|
|
1790
|
+
boxSizing: "border-box"
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
) }),
|
|
1203
1794
|
/* @__PURE__ */ jsxs4("div", { style: { flex: 1, overflow: "auto" }, children: [
|
|
1204
|
-
drawings.length
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
onKeyDown: (e) => {
|
|
1243
|
-
if (e.key === "Enter") handleSaveEdit();
|
|
1244
|
-
if (e.key === "Escape") handleCancelEdit();
|
|
1245
|
-
},
|
|
1246
|
-
onClick: (e) => e.stopPropagation(),
|
|
1247
|
-
autoFocus: true,
|
|
1248
|
-
style: {
|
|
1249
|
-
flex: 1,
|
|
1250
|
-
padding: "4px 8px",
|
|
1251
|
-
fontSize: "14px",
|
|
1252
|
-
border: "1px solid var(--color-primary, #6c63ff)",
|
|
1253
|
-
borderRadius: "4px",
|
|
1254
|
-
outline: "none"
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
),
|
|
1258
|
-
/* @__PURE__ */ jsx6(
|
|
1259
|
-
"button",
|
|
1260
|
-
{
|
|
1261
|
-
onClick: (e) => {
|
|
1262
|
-
e.stopPropagation();
|
|
1263
|
-
handleSaveEdit();
|
|
1264
|
-
},
|
|
1265
|
-
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "var(--color-primary, #6c63ff)", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1266
|
-
children: t.save
|
|
1267
|
-
}
|
|
1268
|
-
),
|
|
1269
|
-
/* @__PURE__ */ jsx6(
|
|
1270
|
-
"button",
|
|
1271
|
-
{
|
|
1272
|
-
onClick: (e) => {
|
|
1273
|
-
e.stopPropagation();
|
|
1274
|
-
handleCancelEdit();
|
|
1275
|
-
},
|
|
1276
|
-
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", color: "inherit" },
|
|
1277
|
-
children: t.cancel
|
|
1278
|
-
}
|
|
1279
|
-
)
|
|
1280
|
-
] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1281
|
-
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: "6px" }, children: [
|
|
1282
|
-
/* @__PURE__ */ jsxs4(
|
|
1283
|
-
"span",
|
|
1284
|
-
{
|
|
1285
|
-
onClick: (e) => {
|
|
1286
|
-
e.stopPropagation();
|
|
1287
|
-
handleStartEdit(drawing);
|
|
1288
|
-
},
|
|
1289
|
-
style: {
|
|
1290
|
-
fontWeight: activeDrawing?.id === drawing.id ? 600 : 400,
|
|
1291
|
-
fontSize: "14px",
|
|
1292
|
-
overflow: "hidden",
|
|
1293
|
-
textOverflow: "ellipsis",
|
|
1294
|
-
whiteSpace: "nowrap",
|
|
1295
|
-
cursor: "text"
|
|
1296
|
-
},
|
|
1297
|
-
title: t.rename,
|
|
1298
|
-
children: [
|
|
1299
|
-
activeDrawing?.id === drawing.id && "\u2713 ",
|
|
1300
|
-
drawing.name
|
|
1301
|
-
]
|
|
1302
|
-
}
|
|
1303
|
-
),
|
|
1304
|
-
/* @__PURE__ */ jsx6(
|
|
1305
|
-
"button",
|
|
1306
|
-
{
|
|
1307
|
-
onClick: (e) => {
|
|
1308
|
-
e.stopPropagation();
|
|
1309
|
-
handleStartEdit(drawing);
|
|
1310
|
-
},
|
|
1311
|
-
style: { background: "none", border: "none", cursor: "pointer", padding: "0 2px", fontSize: "12px", opacity: 0.4, flexShrink: 0 },
|
|
1312
|
-
title: t.rename,
|
|
1313
|
-
children: "\u270F\uFE0F"
|
|
1314
|
-
}
|
|
1315
|
-
)
|
|
1316
|
-
] }),
|
|
1317
|
-
/* @__PURE__ */ jsxs4("div", { style: { fontSize: "11px", color: "var(--text-secondary-color, #888)", marginTop: "1px" }, children: [
|
|
1318
|
-
t.modified,
|
|
1319
|
-
": ",
|
|
1320
|
-
formatDate(drawing.updatedAt)
|
|
1321
|
-
] })
|
|
1322
|
-
] }) }),
|
|
1323
|
-
/* @__PURE__ */ jsx6("div", { style: { display: "flex", gap: "2px" }, children: confirmDeleteId === drawing.id ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1324
|
-
/* @__PURE__ */ jsx6(
|
|
1325
|
-
"button",
|
|
1326
|
-
{
|
|
1327
|
-
onClick: (e) => {
|
|
1328
|
-
e.stopPropagation();
|
|
1329
|
-
handleDelete(drawing.id);
|
|
1330
|
-
},
|
|
1331
|
-
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "#dc3545", color: "#fff", border: "none", borderRadius: "4px", cursor: "pointer" },
|
|
1332
|
-
children: t.delete
|
|
1333
|
-
}
|
|
1334
|
-
),
|
|
1335
|
-
/* @__PURE__ */ jsx6(
|
|
1336
|
-
"button",
|
|
1337
|
-
{
|
|
1338
|
-
onClick: (e) => {
|
|
1339
|
-
e.stopPropagation();
|
|
1340
|
-
setConfirmDeleteId(null);
|
|
1341
|
-
},
|
|
1342
|
-
style: { padding: "4px 10px", fontSize: "12px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "4px", cursor: "pointer", color: "inherit" },
|
|
1343
|
-
children: t.cancel
|
|
1344
|
-
}
|
|
1345
|
-
)
|
|
1346
|
-
] }) : editingId !== drawing.id && /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1347
|
-
/* @__PURE__ */ jsx6(
|
|
1348
|
-
"button",
|
|
1349
|
-
{
|
|
1350
|
-
onClick: (e) => {
|
|
1351
|
-
e.stopPropagation();
|
|
1352
|
-
exportDrawingAsExcalidraw(drawing.id);
|
|
1353
|
-
},
|
|
1354
|
-
style: { background: "none", border: "none", cursor: "pointer", padding: "4px", fontSize: "14px", opacity: 0.5 },
|
|
1355
|
-
title: t.exportDrawing,
|
|
1356
|
-
children: "\u{1F4BE}"
|
|
1357
|
-
}
|
|
1358
|
-
),
|
|
1359
|
-
/* @__PURE__ */ jsx6(
|
|
1360
|
-
"button",
|
|
1361
|
-
{
|
|
1362
|
-
onClick: (e) => {
|
|
1363
|
-
e.stopPropagation();
|
|
1364
|
-
setConfirmDeleteId(drawing.id);
|
|
1365
|
-
},
|
|
1366
|
-
style: { background: "none", border: "none", cursor: "pointer", padding: "4px", fontSize: "14px", opacity: 0.5 },
|
|
1367
|
-
title: t.delete,
|
|
1368
|
-
disabled: drawings.length <= 1,
|
|
1369
|
-
children: "\u{1F5D1}\uFE0F"
|
|
1370
|
-
}
|
|
1371
|
-
)
|
|
1372
|
-
] }) })
|
|
1373
|
-
]
|
|
1374
|
-
},
|
|
1375
|
-
drawing.id
|
|
1376
|
-
)) }),
|
|
1377
|
-
drawings.length === 0 && /* @__PURE__ */ jsxs4("div", { style: { padding: "24px 20px", textAlign: "center", color: "var(--text-secondary-color, #666)" }, children: [
|
|
1795
|
+
isRefreshing && drawings.length === 0 ? /* @__PURE__ */ jsx6("div", { style: { padding: "24px 20px", textAlign: "center", color: "var(--text-secondary-color, #666)" }, children: /* @__PURE__ */ jsx6("span", { style: { display: "inline-block", animation: "spin 1s linear infinite", fontSize: "24px" }, children: "\u23F3" }) }) : drawings.length > 0 || folders.length > 0 ? /* @__PURE__ */ jsxs4("div", { style: { padding: "8px 20px 0" }, children: [
|
|
1796
|
+
filteredFolders.map(renderFolderGroup),
|
|
1797
|
+
draggingDrawingId && folders.length > 0 && /* @__PURE__ */ jsx6(
|
|
1798
|
+
"div",
|
|
1799
|
+
{
|
|
1800
|
+
onDragOver: (e) => {
|
|
1801
|
+
e.preventDefault();
|
|
1802
|
+
e.dataTransfer.dropEffect = "move";
|
|
1803
|
+
setDropTargetFolderId("__root__");
|
|
1804
|
+
},
|
|
1805
|
+
onDragLeave: () => {
|
|
1806
|
+
if (dropTargetFolderId === "__root__") setDropTargetFolderId(null);
|
|
1807
|
+
},
|
|
1808
|
+
onDrop: (e) => {
|
|
1809
|
+
e.preventDefault();
|
|
1810
|
+
if (draggingDrawingId) {
|
|
1811
|
+
handleMoveToFolder(draggingDrawingId, null);
|
|
1812
|
+
setDraggingDrawingId(null);
|
|
1813
|
+
setDropTargetFolderId(null);
|
|
1814
|
+
}
|
|
1815
|
+
},
|
|
1816
|
+
style: {
|
|
1817
|
+
padding: "8px 12px",
|
|
1818
|
+
marginBottom: "4px",
|
|
1819
|
+
borderRadius: "8px",
|
|
1820
|
+
textAlign: "center",
|
|
1821
|
+
fontSize: "12px",
|
|
1822
|
+
color: "var(--text-secondary-color, #888)",
|
|
1823
|
+
backgroundColor: dropTargetFolderId === "__root__" ? "var(--color-primary-light, rgba(108, 99, 255, 0.2))" : "transparent",
|
|
1824
|
+
border: "2px dashed var(--default-border-color, #ccc)",
|
|
1825
|
+
borderColor: dropTargetFolderId === "__root__" ? "var(--color-primary, #6c63ff)" : "var(--default-border-color, #ccc)",
|
|
1826
|
+
transition: "background-color 0.15s, border-color 0.15s"
|
|
1827
|
+
},
|
|
1828
|
+
children: t.moveToRoot
|
|
1829
|
+
}
|
|
1830
|
+
),
|
|
1831
|
+
rootDrawings.map(renderDrawingRow)
|
|
1832
|
+
] }) : /* @__PURE__ */ jsxs4("div", { style: { padding: "24px 20px", textAlign: "center", color: "var(--text-secondary-color, #666)" }, children: [
|
|
1378
1833
|
/* @__PURE__ */ jsx6("p", { children: t.noDrawingsYet }),
|
|
1379
1834
|
/* @__PURE__ */ jsx6("p", { children: t.clickNewToStart })
|
|
1380
1835
|
] }),
|
|
@@ -1386,7 +1841,7 @@ var DrawingsDialog = ({
|
|
|
1386
1841
|
icon: "\u{1F4C4}",
|
|
1387
1842
|
label: t.createNewDrawing,
|
|
1388
1843
|
description: t.createNewDrawingDesc,
|
|
1389
|
-
onClick: handleCreate,
|
|
1844
|
+
onClick: () => handleCreate(),
|
|
1390
1845
|
primary: true
|
|
1391
1846
|
}
|
|
1392
1847
|
),
|
|
@@ -1398,6 +1853,61 @@ var DrawingsDialog = ({
|
|
|
1398
1853
|
description: t.openFromFileDesc,
|
|
1399
1854
|
onClick: importExcalidrawFile
|
|
1400
1855
|
}
|
|
1856
|
+
),
|
|
1857
|
+
creatingFolder ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "8px", alignItems: "center", padding: "4px 0" }, children: [
|
|
1858
|
+
/* @__PURE__ */ jsx6(
|
|
1859
|
+
"input",
|
|
1860
|
+
{
|
|
1861
|
+
ref: newFolderInputRef,
|
|
1862
|
+
type: "text",
|
|
1863
|
+
value: newFolderName,
|
|
1864
|
+
onChange: (e) => setNewFolderName(e.target.value),
|
|
1865
|
+
onKeyDown: (e) => {
|
|
1866
|
+
if (e.key === "Enter") handleCreateFolder();
|
|
1867
|
+
if (e.key === "Escape") {
|
|
1868
|
+
setCreatingFolder(false);
|
|
1869
|
+
setNewFolderName("");
|
|
1870
|
+
}
|
|
1871
|
+
},
|
|
1872
|
+
placeholder: t.newFolderName,
|
|
1873
|
+
style: {
|
|
1874
|
+
flex: 1,
|
|
1875
|
+
padding: "8px 12px",
|
|
1876
|
+
fontSize: "14px",
|
|
1877
|
+
border: "1px solid var(--color-primary, #6c63ff)",
|
|
1878
|
+
borderRadius: "8px",
|
|
1879
|
+
outline: "none"
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
),
|
|
1883
|
+
/* @__PURE__ */ jsx6(
|
|
1884
|
+
"button",
|
|
1885
|
+
{
|
|
1886
|
+
onClick: handleCreateFolder,
|
|
1887
|
+
disabled: busyFolderId === "__creating__",
|
|
1888
|
+
style: { padding: "8px 16px", fontSize: "14px", backgroundColor: "var(--color-primary, #6c63ff)", color: "#fff", border: "none", borderRadius: "8px", cursor: "pointer", opacity: busyFolderId === "__creating__" ? 0.6 : 1 },
|
|
1889
|
+
children: busyFolderId === "__creating__" ? "\u23F3" : t.save
|
|
1890
|
+
}
|
|
1891
|
+
),
|
|
1892
|
+
/* @__PURE__ */ jsx6(
|
|
1893
|
+
"button",
|
|
1894
|
+
{
|
|
1895
|
+
onClick: () => {
|
|
1896
|
+
setCreatingFolder(false);
|
|
1897
|
+
setNewFolderName("");
|
|
1898
|
+
},
|
|
1899
|
+
style: { padding: "8px 16px", fontSize: "14px", backgroundColor: "transparent", border: "1px solid var(--default-border-color, #ccc)", borderRadius: "8px", cursor: "pointer", color: "inherit" },
|
|
1900
|
+
children: t.cancel
|
|
1901
|
+
}
|
|
1902
|
+
)
|
|
1903
|
+
] }) : /* @__PURE__ */ jsx6(
|
|
1904
|
+
ActionButton,
|
|
1905
|
+
{
|
|
1906
|
+
icon: "\u{1F4C1}",
|
|
1907
|
+
label: t.createFolder,
|
|
1908
|
+
description: "",
|
|
1909
|
+
onClick: () => setCreatingFolder(true)
|
|
1910
|
+
}
|
|
1401
1911
|
)
|
|
1402
1912
|
] }),
|
|
1403
1913
|
/* @__PURE__ */ jsx6("div", { style: sectionHeaderStyle, children: t.sectionWorkspace }),
|
|
@@ -1643,16 +2153,22 @@ export {
|
|
|
1643
2153
|
addDrawingToWorkspace,
|
|
1644
2154
|
closeDB,
|
|
1645
2155
|
createDrawing,
|
|
2156
|
+
createFolder,
|
|
1646
2157
|
deleteDrawing,
|
|
2158
|
+
deleteFolder,
|
|
1647
2159
|
duplicateDrawing,
|
|
1648
2160
|
getAllDrawings,
|
|
2161
|
+
getAllFolders,
|
|
1649
2162
|
getDB,
|
|
1650
2163
|
getDrawing,
|
|
2164
|
+
getFolder,
|
|
1651
2165
|
getOrCreateDefaultWorkspace,
|
|
1652
2166
|
getTranslations,
|
|
1653
2167
|
getWorkspace,
|
|
1654
2168
|
isLanguageSupported,
|
|
2169
|
+
moveDrawingToFolder,
|
|
1655
2170
|
removeDrawingFromWorkspace,
|
|
2171
|
+
renameFolder,
|
|
1656
2172
|
setActiveDrawing,
|
|
1657
2173
|
updateDrawing,
|
|
1658
2174
|
updateWorkspace,
|