rita-workspace 0.5.26 → 0.5.28
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.js +128 -19
- package/dist/index.mjs +128 -19
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -432,19 +432,34 @@ function useWorkspaceLang() {
|
|
|
432
432
|
return { lang: context.lang, t: context.t };
|
|
433
433
|
}
|
|
434
434
|
var TAB_ID_KEY = "rita-workspace-tab-id";
|
|
435
|
+
var TAB_ENTRY_KEY = "rita-workspace-tab-entry";
|
|
436
|
+
function generateFreshTabId() {
|
|
437
|
+
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
438
|
+
}
|
|
435
439
|
var TAB_ID = (() => {
|
|
436
440
|
try {
|
|
437
441
|
const existing = sessionStorage.getItem(TAB_ID_KEY);
|
|
438
442
|
if (existing) return existing;
|
|
439
443
|
} catch {
|
|
440
444
|
}
|
|
441
|
-
const fresh =
|
|
445
|
+
const fresh = generateFreshTabId();
|
|
442
446
|
try {
|
|
443
447
|
sessionStorage.setItem(TAB_ID_KEY, fresh);
|
|
444
448
|
} catch {
|
|
445
449
|
}
|
|
446
450
|
return fresh;
|
|
447
451
|
})();
|
|
452
|
+
function regenerateTabId() {
|
|
453
|
+
TAB_ID = generateFreshTabId();
|
|
454
|
+
try {
|
|
455
|
+
sessionStorage.setItem(TAB_ID_KEY, TAB_ID);
|
|
456
|
+
} catch {
|
|
457
|
+
}
|
|
458
|
+
try {
|
|
459
|
+
sessionStorage.removeItem(TAB_ENTRY_KEY);
|
|
460
|
+
} catch {
|
|
461
|
+
}
|
|
462
|
+
}
|
|
448
463
|
var TABS_KEY = "rita-workspace-tabs";
|
|
449
464
|
var TAB_CHANNEL = "rita-workspace-tabs";
|
|
450
465
|
function broadcastWorkspaceChange() {
|
|
@@ -477,7 +492,6 @@ function getTabsMap() {
|
|
|
477
492
|
return {};
|
|
478
493
|
}
|
|
479
494
|
}
|
|
480
|
-
var TAB_ENTRY_KEY = "rita-workspace-tab-entry";
|
|
481
495
|
function setTabDrawing(drawingId) {
|
|
482
496
|
const tabs = getTabsMap();
|
|
483
497
|
if (drawingId) {
|
|
@@ -516,6 +530,28 @@ function isDrawingOpenedEarlierInOtherTab(drawingId) {
|
|
|
516
530
|
([tabId, entry]) => tabId !== TAB_ID && entry.drawingId === drawingId && entry.openedAt <= myOpenedAt
|
|
517
531
|
);
|
|
518
532
|
}
|
|
533
|
+
function detectTabIdCollision() {
|
|
534
|
+
return new Promise((resolve) => {
|
|
535
|
+
let channel;
|
|
536
|
+
try {
|
|
537
|
+
channel = new BroadcastChannel(TAB_CHANNEL);
|
|
538
|
+
} catch {
|
|
539
|
+
resolve(false);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
let collided = false;
|
|
543
|
+
channel.onmessage = (event) => {
|
|
544
|
+
if (event.data?.type === "id-collision" && event.data?.tabId === TAB_ID) {
|
|
545
|
+
collided = true;
|
|
546
|
+
}
|
|
547
|
+
};
|
|
548
|
+
channel.postMessage({ type: "id-claim", tabId: TAB_ID });
|
|
549
|
+
setTimeout(() => {
|
|
550
|
+
channel.close();
|
|
551
|
+
resolve(collided);
|
|
552
|
+
}, 300);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
519
555
|
function cleanupStaleTabs() {
|
|
520
556
|
const tabs = getTabsMap();
|
|
521
557
|
const otherTabIds = Object.keys(tabs).filter((id) => id !== TAB_ID);
|
|
@@ -597,6 +633,8 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
597
633
|
channel.onmessage = (event) => {
|
|
598
634
|
if (event.data?.type === "ping") {
|
|
599
635
|
channel?.postMessage({ type: "pong", tabId: TAB_ID });
|
|
636
|
+
} else if (event.data?.type === "id-claim" && event.data?.tabId === TAB_ID) {
|
|
637
|
+
channel?.postMessage({ type: "id-collision", tabId: TAB_ID });
|
|
600
638
|
} else if (event.data?.type === "workspace-changed" && event.data?.tabId !== TAB_ID) {
|
|
601
639
|
refreshDrawingsRef.current();
|
|
602
640
|
}
|
|
@@ -608,18 +646,32 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
608
646
|
};
|
|
609
647
|
}, []);
|
|
610
648
|
const hasCleanedUpRef = (0, import_react.useRef)(false);
|
|
649
|
+
const [tabIdReady, setTabIdReady] = (0, import_react.useState)(false);
|
|
611
650
|
(0, import_react.useEffect)(() => {
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
setIsDrawingConflict(isDrawingOpenedEarlierInOtherTab(drawingId));
|
|
651
|
+
let cancelled = false;
|
|
652
|
+
let timer = null;
|
|
653
|
+
(async () => {
|
|
654
|
+
if (await detectTabIdCollision()) {
|
|
655
|
+
regenerateTabId();
|
|
618
656
|
}
|
|
619
|
-
|
|
620
|
-
|
|
657
|
+
if (cancelled) return;
|
|
658
|
+
setTabIdReady(true);
|
|
659
|
+
cleanupStaleTabs();
|
|
660
|
+
timer = setTimeout(() => {
|
|
661
|
+
hasCleanedUpRef.current = true;
|
|
662
|
+
const drawingId = activeDrawingIdRef.current;
|
|
663
|
+
if (drawingId) {
|
|
664
|
+
setIsDrawingConflict(isDrawingOpenedEarlierInOtherTab(drawingId));
|
|
665
|
+
}
|
|
666
|
+
}, 600);
|
|
667
|
+
})();
|
|
668
|
+
return () => {
|
|
669
|
+
cancelled = true;
|
|
670
|
+
if (timer) clearTimeout(timer);
|
|
671
|
+
};
|
|
621
672
|
}, []);
|
|
622
673
|
(0, import_react.useEffect)(() => {
|
|
674
|
+
if (!tabIdReady) return;
|
|
623
675
|
const drawingId = activeDrawing?.id || null;
|
|
624
676
|
setTabDrawing(drawingId);
|
|
625
677
|
if (hasCleanedUpRef.current) {
|
|
@@ -679,7 +731,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
679
731
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
680
732
|
stopPolling();
|
|
681
733
|
};
|
|
682
|
-
}, [activeDrawing?.id]);
|
|
734
|
+
}, [activeDrawing?.id, tabIdReady]);
|
|
683
735
|
(0, import_react.useEffect)(() => {
|
|
684
736
|
const onUnload = () => {
|
|
685
737
|
const tabs = getTabsMap();
|
|
@@ -771,7 +823,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
771
823
|
const now = Date.now();
|
|
772
824
|
const tempId = `temp-${now}`;
|
|
773
825
|
const allDrawings = await getAllDrawings();
|
|
774
|
-
const
|
|
826
|
+
const prefix = t.newDrawing;
|
|
827
|
+
const suffixRegex = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} (\\d+)$`);
|
|
828
|
+
let maxSuffix = 0;
|
|
829
|
+
for (const d of allDrawings) {
|
|
830
|
+
const m = d.name.match(suffixRegex);
|
|
831
|
+
if (m) {
|
|
832
|
+
const n = parseInt(m[1], 10);
|
|
833
|
+
if (!isNaN(n) && n > maxSuffix) maxSuffix = n;
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const defaultName = `${prefix} ${maxSuffix + 1}`;
|
|
775
837
|
const tempDrawing = {
|
|
776
838
|
id: tempId,
|
|
777
839
|
name: name || defaultName,
|
|
@@ -835,7 +897,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
835
897
|
}
|
|
836
898
|
}, [refreshDrawings]);
|
|
837
899
|
const removeDrawing = (0, import_react.useCallback)(async (id) => {
|
|
838
|
-
if (!workspace
|
|
900
|
+
if (!workspace) return;
|
|
839
901
|
const removedDrawing = drawingsRef.current.find((d) => d.id === id);
|
|
840
902
|
const wasActive = activeDrawingIdRef.current === id;
|
|
841
903
|
const remaining = drawingsRef.current.filter((d) => d.id !== id);
|
|
@@ -850,6 +912,9 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
850
912
|
if (updatedWorkspace) {
|
|
851
913
|
setWorkspace(updatedWorkspace);
|
|
852
914
|
}
|
|
915
|
+
if (remaining.length === 0) {
|
|
916
|
+
await createNewDrawing();
|
|
917
|
+
}
|
|
853
918
|
broadcastWorkspaceChange();
|
|
854
919
|
} catch (err) {
|
|
855
920
|
if (removedDrawing) {
|
|
@@ -857,7 +922,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
857
922
|
}
|
|
858
923
|
setError(err instanceof Error ? err.message : "Failed to delete drawing");
|
|
859
924
|
}
|
|
860
|
-
}, [workspace]);
|
|
925
|
+
}, [workspace, createNewDrawing]);
|
|
861
926
|
const duplicateCurrentDrawing = (0, import_react.useCallback)(async () => {
|
|
862
927
|
if (!activeDrawingIdRef.current || !workspace) return null;
|
|
863
928
|
const source = drawingsRef.current.find((d) => d.id === activeDrawingIdRef.current);
|
|
@@ -904,6 +969,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
904
969
|
updateData.files = files;
|
|
905
970
|
}
|
|
906
971
|
await updateDrawing(activeDrawing.id, updateData);
|
|
972
|
+
const now = Date.now();
|
|
973
|
+
const patch = {
|
|
974
|
+
elements,
|
|
975
|
+
appState,
|
|
976
|
+
...files ? { files } : {},
|
|
977
|
+
updatedAt: now
|
|
978
|
+
};
|
|
979
|
+
setDrawings((prev) => prev.map((d) => d.id === activeDrawing.id ? { ...d, ...patch } : d));
|
|
980
|
+
setActiveDrawing2((prev) => prev && prev.id === activeDrawing.id ? { ...prev, ...patch } : prev);
|
|
907
981
|
} catch (err) {
|
|
908
982
|
setError(err instanceof Error ? err.message : "Failed to save drawing");
|
|
909
983
|
}
|
|
@@ -919,6 +993,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
919
993
|
updateData.files = files;
|
|
920
994
|
}
|
|
921
995
|
await updateDrawing(id, updateData);
|
|
996
|
+
const now = Date.now();
|
|
997
|
+
const patch = {
|
|
998
|
+
elements,
|
|
999
|
+
appState,
|
|
1000
|
+
...files ? { files } : {},
|
|
1001
|
+
updatedAt: now
|
|
1002
|
+
};
|
|
1003
|
+
setDrawings((prev) => prev.map((d) => d.id === id ? { ...d, ...patch } : d));
|
|
1004
|
+
setActiveDrawing2((prev) => prev && prev.id === id ? { ...prev, ...patch } : prev);
|
|
922
1005
|
} catch (err) {
|
|
923
1006
|
setError(err instanceof Error ? err.message : "Failed to save drawing");
|
|
924
1007
|
}
|
|
@@ -926,11 +1009,18 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
926
1009
|
const exportWorkspace = (0, import_react.useCallback)(async () => {
|
|
927
1010
|
try {
|
|
928
1011
|
const exportData = {
|
|
929
|
-
version:
|
|
1012
|
+
version: 2,
|
|
930
1013
|
name: workspace?.name || "Min Arbetsyta",
|
|
931
1014
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1015
|
+
folders: folders.map((f) => ({
|
|
1016
|
+
id: f.id,
|
|
1017
|
+
name: f.name,
|
|
1018
|
+
createdAt: f.createdAt,
|
|
1019
|
+
updatedAt: f.updatedAt
|
|
1020
|
+
})),
|
|
932
1021
|
drawings: drawings.map((d) => ({
|
|
933
1022
|
name: d.name,
|
|
1023
|
+
folderId: d.folderId ?? null,
|
|
934
1024
|
elements: d.elements,
|
|
935
1025
|
appState: d.appState,
|
|
936
1026
|
files: d.files,
|
|
@@ -950,7 +1040,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
950
1040
|
} catch (err) {
|
|
951
1041
|
setError(err instanceof Error ? err.message : "Failed to export workspace");
|
|
952
1042
|
}
|
|
953
|
-
}, [workspace, drawings]);
|
|
1043
|
+
}, [workspace, drawings, folders]);
|
|
954
1044
|
const importWorkspace = (0, import_react.useCallback)(async () => {
|
|
955
1045
|
if (!workspace) return;
|
|
956
1046
|
try {
|
|
@@ -967,8 +1057,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
967
1057
|
if (!data.version || !Array.isArray(data.drawings)) {
|
|
968
1058
|
throw new Error("Invalid workspace file");
|
|
969
1059
|
}
|
|
1060
|
+
const folderIdMap = /* @__PURE__ */ new Map();
|
|
1061
|
+
if (Array.isArray(data.folders)) {
|
|
1062
|
+
for (const f of data.folders) {
|
|
1063
|
+
if (!f?.name || !f?.id) continue;
|
|
1064
|
+
const created = await createFolder(f.name);
|
|
1065
|
+
folderIdMap.set(f.id, created.id);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
970
1068
|
for (const d of data.drawings) {
|
|
971
|
-
const
|
|
1069
|
+
const mappedFolderId = d.folderId ? folderIdMap.get(d.folderId) ?? null : null;
|
|
1070
|
+
const drawing = await createDrawing(d.name || t.newDrawing, [], {}, mappedFolderId);
|
|
972
1071
|
await updateDrawing(drawing.id, {
|
|
973
1072
|
elements: d.elements || [],
|
|
974
1073
|
appState: d.appState || {},
|
|
@@ -976,11 +1075,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
976
1075
|
});
|
|
977
1076
|
await addDrawingToWorkspace(workspace.id, drawing.id);
|
|
978
1077
|
}
|
|
979
|
-
const allDrawings = await
|
|
980
|
-
|
|
1078
|
+
const [allDrawings, allFolders, ws] = await Promise.all([
|
|
1079
|
+
getAllDrawings(),
|
|
1080
|
+
getAllFolders(),
|
|
1081
|
+
getOrCreateDefaultWorkspace()
|
|
1082
|
+
]);
|
|
981
1083
|
const wsDrawings = allDrawings.filter((dr) => ws.drawingIds.includes(dr.id));
|
|
982
1084
|
setWorkspace(ws);
|
|
983
1085
|
setDrawings(wsDrawings);
|
|
1086
|
+
setFolders(allFolders);
|
|
984
1087
|
} catch (err) {
|
|
985
1088
|
setError(err instanceof Error ? err.message : "Failed to import workspace");
|
|
986
1089
|
}
|
|
@@ -1749,6 +1852,12 @@ var DrawingsDialog = ({
|
|
|
1749
1852
|
if (editingId || confirmDeleteId || movingDrawingId) return;
|
|
1750
1853
|
setSelectedId(drawing.id);
|
|
1751
1854
|
},
|
|
1855
|
+
onDoubleClick: (e) => {
|
|
1856
|
+
e.stopPropagation();
|
|
1857
|
+
if (editingId || confirmDeleteId || movingDrawingId) return;
|
|
1858
|
+
if (activeDrawing?.id === drawing.id) return;
|
|
1859
|
+
handleSelect(drawing);
|
|
1860
|
+
},
|
|
1752
1861
|
style: {
|
|
1753
1862
|
padding: "10px 12px",
|
|
1754
1863
|
display: "flex",
|
package/dist/index.mjs
CHANGED
|
@@ -361,19 +361,34 @@ function useWorkspaceLang() {
|
|
|
361
361
|
return { lang: context.lang, t: context.t };
|
|
362
362
|
}
|
|
363
363
|
var TAB_ID_KEY = "rita-workspace-tab-id";
|
|
364
|
+
var TAB_ENTRY_KEY = "rita-workspace-tab-entry";
|
|
365
|
+
function generateFreshTabId() {
|
|
366
|
+
return typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
|
367
|
+
}
|
|
364
368
|
var TAB_ID = (() => {
|
|
365
369
|
try {
|
|
366
370
|
const existing = sessionStorage.getItem(TAB_ID_KEY);
|
|
367
371
|
if (existing) return existing;
|
|
368
372
|
} catch {
|
|
369
373
|
}
|
|
370
|
-
const fresh =
|
|
374
|
+
const fresh = generateFreshTabId();
|
|
371
375
|
try {
|
|
372
376
|
sessionStorage.setItem(TAB_ID_KEY, fresh);
|
|
373
377
|
} catch {
|
|
374
378
|
}
|
|
375
379
|
return fresh;
|
|
376
380
|
})();
|
|
381
|
+
function regenerateTabId() {
|
|
382
|
+
TAB_ID = generateFreshTabId();
|
|
383
|
+
try {
|
|
384
|
+
sessionStorage.setItem(TAB_ID_KEY, TAB_ID);
|
|
385
|
+
} catch {
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
sessionStorage.removeItem(TAB_ENTRY_KEY);
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
}
|
|
377
392
|
var TABS_KEY = "rita-workspace-tabs";
|
|
378
393
|
var TAB_CHANNEL = "rita-workspace-tabs";
|
|
379
394
|
function broadcastWorkspaceChange() {
|
|
@@ -406,7 +421,6 @@ function getTabsMap() {
|
|
|
406
421
|
return {};
|
|
407
422
|
}
|
|
408
423
|
}
|
|
409
|
-
var TAB_ENTRY_KEY = "rita-workspace-tab-entry";
|
|
410
424
|
function setTabDrawing(drawingId) {
|
|
411
425
|
const tabs = getTabsMap();
|
|
412
426
|
if (drawingId) {
|
|
@@ -445,6 +459,28 @@ function isDrawingOpenedEarlierInOtherTab(drawingId) {
|
|
|
445
459
|
([tabId, entry]) => tabId !== TAB_ID && entry.drawingId === drawingId && entry.openedAt <= myOpenedAt
|
|
446
460
|
);
|
|
447
461
|
}
|
|
462
|
+
function detectTabIdCollision() {
|
|
463
|
+
return new Promise((resolve) => {
|
|
464
|
+
let channel;
|
|
465
|
+
try {
|
|
466
|
+
channel = new BroadcastChannel(TAB_CHANNEL);
|
|
467
|
+
} catch {
|
|
468
|
+
resolve(false);
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
471
|
+
let collided = false;
|
|
472
|
+
channel.onmessage = (event) => {
|
|
473
|
+
if (event.data?.type === "id-collision" && event.data?.tabId === TAB_ID) {
|
|
474
|
+
collided = true;
|
|
475
|
+
}
|
|
476
|
+
};
|
|
477
|
+
channel.postMessage({ type: "id-claim", tabId: TAB_ID });
|
|
478
|
+
setTimeout(() => {
|
|
479
|
+
channel.close();
|
|
480
|
+
resolve(collided);
|
|
481
|
+
}, 300);
|
|
482
|
+
});
|
|
483
|
+
}
|
|
448
484
|
function cleanupStaleTabs() {
|
|
449
485
|
const tabs = getTabsMap();
|
|
450
486
|
const otherTabIds = Object.keys(tabs).filter((id) => id !== TAB_ID);
|
|
@@ -526,6 +562,8 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
526
562
|
channel.onmessage = (event) => {
|
|
527
563
|
if (event.data?.type === "ping") {
|
|
528
564
|
channel?.postMessage({ type: "pong", tabId: TAB_ID });
|
|
565
|
+
} else if (event.data?.type === "id-claim" && event.data?.tabId === TAB_ID) {
|
|
566
|
+
channel?.postMessage({ type: "id-collision", tabId: TAB_ID });
|
|
529
567
|
} else if (event.data?.type === "workspace-changed" && event.data?.tabId !== TAB_ID) {
|
|
530
568
|
refreshDrawingsRef.current();
|
|
531
569
|
}
|
|
@@ -537,18 +575,32 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
537
575
|
};
|
|
538
576
|
}, []);
|
|
539
577
|
const hasCleanedUpRef = useRef(false);
|
|
578
|
+
const [tabIdReady, setTabIdReady] = useState(false);
|
|
540
579
|
useEffect(() => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
setIsDrawingConflict(isDrawingOpenedEarlierInOtherTab(drawingId));
|
|
580
|
+
let cancelled = false;
|
|
581
|
+
let timer = null;
|
|
582
|
+
(async () => {
|
|
583
|
+
if (await detectTabIdCollision()) {
|
|
584
|
+
regenerateTabId();
|
|
547
585
|
}
|
|
548
|
-
|
|
549
|
-
|
|
586
|
+
if (cancelled) return;
|
|
587
|
+
setTabIdReady(true);
|
|
588
|
+
cleanupStaleTabs();
|
|
589
|
+
timer = setTimeout(() => {
|
|
590
|
+
hasCleanedUpRef.current = true;
|
|
591
|
+
const drawingId = activeDrawingIdRef.current;
|
|
592
|
+
if (drawingId) {
|
|
593
|
+
setIsDrawingConflict(isDrawingOpenedEarlierInOtherTab(drawingId));
|
|
594
|
+
}
|
|
595
|
+
}, 600);
|
|
596
|
+
})();
|
|
597
|
+
return () => {
|
|
598
|
+
cancelled = true;
|
|
599
|
+
if (timer) clearTimeout(timer);
|
|
600
|
+
};
|
|
550
601
|
}, []);
|
|
551
602
|
useEffect(() => {
|
|
603
|
+
if (!tabIdReady) return;
|
|
552
604
|
const drawingId = activeDrawing?.id || null;
|
|
553
605
|
setTabDrawing(drawingId);
|
|
554
606
|
if (hasCleanedUpRef.current) {
|
|
@@ -608,7 +660,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
608
660
|
document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
609
661
|
stopPolling();
|
|
610
662
|
};
|
|
611
|
-
}, [activeDrawing?.id]);
|
|
663
|
+
}, [activeDrawing?.id, tabIdReady]);
|
|
612
664
|
useEffect(() => {
|
|
613
665
|
const onUnload = () => {
|
|
614
666
|
const tabs = getTabsMap();
|
|
@@ -700,7 +752,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
700
752
|
const now = Date.now();
|
|
701
753
|
const tempId = `temp-${now}`;
|
|
702
754
|
const allDrawings = await getAllDrawings();
|
|
703
|
-
const
|
|
755
|
+
const prefix = t.newDrawing;
|
|
756
|
+
const suffixRegex = new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} (\\d+)$`);
|
|
757
|
+
let maxSuffix = 0;
|
|
758
|
+
for (const d of allDrawings) {
|
|
759
|
+
const m = d.name.match(suffixRegex);
|
|
760
|
+
if (m) {
|
|
761
|
+
const n = parseInt(m[1], 10);
|
|
762
|
+
if (!isNaN(n) && n > maxSuffix) maxSuffix = n;
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
const defaultName = `${prefix} ${maxSuffix + 1}`;
|
|
704
766
|
const tempDrawing = {
|
|
705
767
|
id: tempId,
|
|
706
768
|
name: name || defaultName,
|
|
@@ -764,7 +826,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
764
826
|
}
|
|
765
827
|
}, [refreshDrawings]);
|
|
766
828
|
const removeDrawing = useCallback(async (id) => {
|
|
767
|
-
if (!workspace
|
|
829
|
+
if (!workspace) return;
|
|
768
830
|
const removedDrawing = drawingsRef.current.find((d) => d.id === id);
|
|
769
831
|
const wasActive = activeDrawingIdRef.current === id;
|
|
770
832
|
const remaining = drawingsRef.current.filter((d) => d.id !== id);
|
|
@@ -779,6 +841,9 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
779
841
|
if (updatedWorkspace) {
|
|
780
842
|
setWorkspace(updatedWorkspace);
|
|
781
843
|
}
|
|
844
|
+
if (remaining.length === 0) {
|
|
845
|
+
await createNewDrawing();
|
|
846
|
+
}
|
|
782
847
|
broadcastWorkspaceChange();
|
|
783
848
|
} catch (err) {
|
|
784
849
|
if (removedDrawing) {
|
|
@@ -786,7 +851,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
786
851
|
}
|
|
787
852
|
setError(err instanceof Error ? err.message : "Failed to delete drawing");
|
|
788
853
|
}
|
|
789
|
-
}, [workspace]);
|
|
854
|
+
}, [workspace, createNewDrawing]);
|
|
790
855
|
const duplicateCurrentDrawing = useCallback(async () => {
|
|
791
856
|
if (!activeDrawingIdRef.current || !workspace) return null;
|
|
792
857
|
const source = drawingsRef.current.find((d) => d.id === activeDrawingIdRef.current);
|
|
@@ -833,6 +898,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
833
898
|
updateData.files = files;
|
|
834
899
|
}
|
|
835
900
|
await updateDrawing(activeDrawing.id, updateData);
|
|
901
|
+
const now = Date.now();
|
|
902
|
+
const patch = {
|
|
903
|
+
elements,
|
|
904
|
+
appState,
|
|
905
|
+
...files ? { files } : {},
|
|
906
|
+
updatedAt: now
|
|
907
|
+
};
|
|
908
|
+
setDrawings((prev) => prev.map((d) => d.id === activeDrawing.id ? { ...d, ...patch } : d));
|
|
909
|
+
setActiveDrawing2((prev) => prev && prev.id === activeDrawing.id ? { ...prev, ...patch } : prev);
|
|
836
910
|
} catch (err) {
|
|
837
911
|
setError(err instanceof Error ? err.message : "Failed to save drawing");
|
|
838
912
|
}
|
|
@@ -848,6 +922,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
848
922
|
updateData.files = files;
|
|
849
923
|
}
|
|
850
924
|
await updateDrawing(id, updateData);
|
|
925
|
+
const now = Date.now();
|
|
926
|
+
const patch = {
|
|
927
|
+
elements,
|
|
928
|
+
appState,
|
|
929
|
+
...files ? { files } : {},
|
|
930
|
+
updatedAt: now
|
|
931
|
+
};
|
|
932
|
+
setDrawings((prev) => prev.map((d) => d.id === id ? { ...d, ...patch } : d));
|
|
933
|
+
setActiveDrawing2((prev) => prev && prev.id === id ? { ...prev, ...patch } : prev);
|
|
851
934
|
} catch (err) {
|
|
852
935
|
setError(err instanceof Error ? err.message : "Failed to save drawing");
|
|
853
936
|
}
|
|
@@ -855,11 +938,18 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
855
938
|
const exportWorkspace = useCallback(async () => {
|
|
856
939
|
try {
|
|
857
940
|
const exportData = {
|
|
858
|
-
version:
|
|
941
|
+
version: 2,
|
|
859
942
|
name: workspace?.name || "Min Arbetsyta",
|
|
860
943
|
exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
944
|
+
folders: folders.map((f) => ({
|
|
945
|
+
id: f.id,
|
|
946
|
+
name: f.name,
|
|
947
|
+
createdAt: f.createdAt,
|
|
948
|
+
updatedAt: f.updatedAt
|
|
949
|
+
})),
|
|
861
950
|
drawings: drawings.map((d) => ({
|
|
862
951
|
name: d.name,
|
|
952
|
+
folderId: d.folderId ?? null,
|
|
863
953
|
elements: d.elements,
|
|
864
954
|
appState: d.appState,
|
|
865
955
|
files: d.files,
|
|
@@ -879,7 +969,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
879
969
|
} catch (err) {
|
|
880
970
|
setError(err instanceof Error ? err.message : "Failed to export workspace");
|
|
881
971
|
}
|
|
882
|
-
}, [workspace, drawings]);
|
|
972
|
+
}, [workspace, drawings, folders]);
|
|
883
973
|
const importWorkspace = useCallback(async () => {
|
|
884
974
|
if (!workspace) return;
|
|
885
975
|
try {
|
|
@@ -896,8 +986,17 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
896
986
|
if (!data.version || !Array.isArray(data.drawings)) {
|
|
897
987
|
throw new Error("Invalid workspace file");
|
|
898
988
|
}
|
|
989
|
+
const folderIdMap = /* @__PURE__ */ new Map();
|
|
990
|
+
if (Array.isArray(data.folders)) {
|
|
991
|
+
for (const f of data.folders) {
|
|
992
|
+
if (!f?.name || !f?.id) continue;
|
|
993
|
+
const created = await createFolder(f.name);
|
|
994
|
+
folderIdMap.set(f.id, created.id);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
899
997
|
for (const d of data.drawings) {
|
|
900
|
-
const
|
|
998
|
+
const mappedFolderId = d.folderId ? folderIdMap.get(d.folderId) ?? null : null;
|
|
999
|
+
const drawing = await createDrawing(d.name || t.newDrawing, [], {}, mappedFolderId);
|
|
901
1000
|
await updateDrawing(drawing.id, {
|
|
902
1001
|
elements: d.elements || [],
|
|
903
1002
|
appState: d.appState || {},
|
|
@@ -905,11 +1004,15 @@ function WorkspaceProvider({ children, lang = "en" }) {
|
|
|
905
1004
|
});
|
|
906
1005
|
await addDrawingToWorkspace(workspace.id, drawing.id);
|
|
907
1006
|
}
|
|
908
|
-
const allDrawings = await
|
|
909
|
-
|
|
1007
|
+
const [allDrawings, allFolders, ws] = await Promise.all([
|
|
1008
|
+
getAllDrawings(),
|
|
1009
|
+
getAllFolders(),
|
|
1010
|
+
getOrCreateDefaultWorkspace()
|
|
1011
|
+
]);
|
|
910
1012
|
const wsDrawings = allDrawings.filter((dr) => ws.drawingIds.includes(dr.id));
|
|
911
1013
|
setWorkspace(ws);
|
|
912
1014
|
setDrawings(wsDrawings);
|
|
1015
|
+
setFolders(allFolders);
|
|
913
1016
|
} catch (err) {
|
|
914
1017
|
setError(err instanceof Error ? err.message : "Failed to import workspace");
|
|
915
1018
|
}
|
|
@@ -1678,6 +1781,12 @@ var DrawingsDialog = ({
|
|
|
1678
1781
|
if (editingId || confirmDeleteId || movingDrawingId) return;
|
|
1679
1782
|
setSelectedId(drawing.id);
|
|
1680
1783
|
},
|
|
1784
|
+
onDoubleClick: (e) => {
|
|
1785
|
+
e.stopPropagation();
|
|
1786
|
+
if (editingId || confirmDeleteId || movingDrawingId) return;
|
|
1787
|
+
if (activeDrawing?.id === drawing.id) return;
|
|
1788
|
+
handleSelect(drawing);
|
|
1789
|
+
},
|
|
1681
1790
|
style: {
|
|
1682
1791
|
padding: "10px 12px",
|
|
1683
1792
|
display: "flex",
|