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.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 = 1;
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 = JSON.parse(localStorage.getItem(TABS_KEY) || "{}");
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(raw)) {
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
- localStorage.setItem(TABS_KEY, JSON.stringify(tabs));
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 createNewDrawing = useCallback(async (name) => {
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} ${drawings.length + 1}`;
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, drawings.length, t]);
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 (activeDrawing?.id === id) {
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
- }, [activeDrawing]);
555
+ }, []);
464
556
  const removeDrawing = useCallback(async (id) => {
465
- if (!workspace || drawings.length <= 1) return;
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 (activeDrawing?.id === id && updatedWorkspace.activeDrawingId) {
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, drawings.length, activeDrawing]);
572
+ }, [workspace]);
481
573
  const duplicateCurrentDrawing = useCallback(async () => {
482
- if (!activeDrawing || !workspace) return null;
574
+ if (!activeDrawingIdRef.current || !workspace) return null;
483
575
  try {
484
- const duplicate = await duplicateDrawing(activeDrawing.id);
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
- }, [activeDrawing, workspace]);
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
- refreshDrawings();
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 > 0 && /* @__PURE__ */ jsx6("div", { style: { padding: "8px 20px 0" }, children: drawings.map((drawing) => /* @__PURE__ */ jsxs4(
1205
- "div",
1206
- {
1207
- onClick: () => {
1208
- if (editingId || confirmDeleteId) return;
1209
- if (activeDrawing?.id !== drawing.id) handleSelect(drawing);
1210
- },
1211
- style: {
1212
- padding: "10px 12px",
1213
- display: "flex",
1214
- alignItems: "center",
1215
- gap: "12px",
1216
- borderRadius: "8px",
1217
- marginBottom: "4px",
1218
- cursor: editingId || confirmDeleteId ? "default" : "pointer",
1219
- backgroundColor: activeDrawing?.id === drawing.id ? "var(--color-primary-light, rgba(108, 99, 255, 0.1))" : "transparent",
1220
- transition: "background-color 0.15s"
1221
- },
1222
- children: [
1223
- renderThumbnail && /* @__PURE__ */ jsx6("div", { style: {
1224
- width: "64px",
1225
- height: "48px",
1226
- flexShrink: 0,
1227
- borderRadius: "4px",
1228
- overflow: "hidden",
1229
- border: "1px solid var(--default-border-color, #e0e0e0)",
1230
- backgroundColor: "#fff",
1231
- display: "flex",
1232
- alignItems: "center",
1233
- justifyContent: "center"
1234
- }, children: renderThumbnail(drawing) }),
1235
- /* @__PURE__ */ jsx6("div", { style: { flex: 1, minWidth: 0 }, children: editingId === drawing.id ? /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: "6px", alignItems: "center" }, children: [
1236
- /* @__PURE__ */ jsx6(
1237
- "input",
1238
- {
1239
- type: "text",
1240
- value: editName,
1241
- onChange: (e) => setEditName(e.target.value),
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,