rita-workspace 0.4.7 → 0.4.9

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 CHANGED
@@ -99,6 +99,7 @@ interface WorkspaceContextValue {
99
99
  activeDrawing: Drawing | null;
100
100
  isLoading: boolean;
101
101
  error: string | null;
102
+ isDrawingConflict: boolean;
102
103
  lang: string;
103
104
  t: Translations;
104
105
  createNewDrawing: (name?: string) => Promise<Drawing | null>;
package/dist/index.d.ts CHANGED
@@ -99,6 +99,7 @@ interface WorkspaceContextValue {
99
99
  activeDrawing: Drawing | null;
100
100
  isLoading: boolean;
101
101
  error: string | null;
102
+ isDrawingConflict: boolean;
102
103
  lang: string;
103
104
  t: Translations;
104
105
  createNewDrawing: (name?: string) => Promise<Drawing | null>;
package/dist/index.js CHANGED
@@ -323,12 +323,70 @@ function useWorkspaceLang() {
323
323
  }
324
324
  return { lang: context.lang, t: context.t };
325
325
  }
326
+ var TAB_ID = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);
327
+ var TABS_KEY = "rita-workspace-tabs";
328
+ var TAB_CHANNEL = "rita-workspace-tabs";
329
+ function getTabsMap() {
330
+ try {
331
+ return JSON.parse(localStorage.getItem(TABS_KEY) || "{}");
332
+ } catch {
333
+ return {};
334
+ }
335
+ }
336
+ function setTabDrawing(drawingId) {
337
+ const tabs = getTabsMap();
338
+ if (drawingId) {
339
+ tabs[TAB_ID] = drawingId;
340
+ } else {
341
+ delete tabs[TAB_ID];
342
+ }
343
+ localStorage.setItem(TABS_KEY, JSON.stringify(tabs));
344
+ }
345
+ function isDrawingOpenInOtherTab(drawingId) {
346
+ const tabs = getTabsMap();
347
+ return Object.entries(tabs).some(
348
+ ([tabId, dId]) => tabId !== TAB_ID && dId === drawingId
349
+ );
350
+ }
326
351
  function WorkspaceProvider({ children, lang = "en" }) {
327
352
  const [workspace, setWorkspace] = (0, import_react.useState)(null);
328
353
  const [drawings, setDrawings] = (0, import_react.useState)([]);
329
354
  const [activeDrawing, setActiveDrawing2] = (0, import_react.useState)(null);
330
355
  const [isLoading, setIsLoading] = (0, import_react.useState)(true);
331
356
  const [error, setError] = (0, import_react.useState)(null);
357
+ const [isDrawingConflict, setIsDrawingConflict] = (0, import_react.useState)(false);
358
+ (0, import_react.useEffect)(() => {
359
+ const drawingId = activeDrawing?.id || null;
360
+ setTabDrawing(drawingId);
361
+ if (drawingId) {
362
+ setIsDrawingConflict(isDrawingOpenInOtherTab(drawingId));
363
+ } else {
364
+ setIsDrawingConflict(false);
365
+ }
366
+ let channel = null;
367
+ try {
368
+ channel = new BroadcastChannel(TAB_CHANNEL);
369
+ channel.postMessage({ type: "drawing-changed", tabId: TAB_ID, drawingId });
370
+ channel.onmessage = (event) => {
371
+ if (event.data?.tabId !== TAB_ID && drawingId) {
372
+ setIsDrawingConflict(isDrawingOpenInOtherTab(drawingId));
373
+ }
374
+ };
375
+ } catch {
376
+ }
377
+ return () => {
378
+ channel?.close();
379
+ };
380
+ }, [activeDrawing?.id]);
381
+ (0, import_react.useEffect)(() => {
382
+ const onUnload = () => {
383
+ const tabs = getTabsMap();
384
+ delete tabs[TAB_ID];
385
+ localStorage.setItem(TABS_KEY, JSON.stringify(tabs));
386
+ };
387
+ window.addEventListener("beforeunload", onUnload);
388
+ return () => window.removeEventListener("beforeunload", onUnload);
389
+ }, []);
332
390
  const t = getTranslations(lang);
333
391
  (0, import_react.useEffect)(() => {
334
392
  async function init() {
@@ -339,9 +397,23 @@ function WorkspaceProvider({ children, lang = "en" }) {
339
397
  const allDrawings = await getAllDrawings();
340
398
  const wsDrawings = allDrawings.filter((d) => ws.drawingIds.includes(d.id));
341
399
  setDrawings(wsDrawings);
342
- if (ws.activeDrawingId) {
343
- const active = await getDrawing(ws.activeDrawingId);
344
- setActiveDrawing2(active || null);
400
+ const lastDrawingId = sessionStorage.getItem("rita-workspace-tab-drawing");
401
+ let active = null;
402
+ if (lastDrawingId) {
403
+ active = wsDrawings.find((d) => d.id === lastDrawingId) || null;
404
+ if (!active) {
405
+ const fromDb = await getDrawing(lastDrawingId);
406
+ if (fromDb && ws.drawingIds.includes(fromDb.id)) {
407
+ active = fromDb;
408
+ }
409
+ }
410
+ }
411
+ if (!active && wsDrawings.length > 0) {
412
+ active = wsDrawings[0];
413
+ }
414
+ if (active) {
415
+ setActiveDrawing2(active);
416
+ sessionStorage.setItem("rita-workspace-tab-drawing", active.id);
345
417
  }
346
418
  } catch (err) {
347
419
  setError(err instanceof Error ? err.message : "Failed to load workspace");
@@ -357,10 +429,6 @@ function WorkspaceProvider({ children, lang = "en" }) {
357
429
  const allDrawings = await getAllDrawings();
358
430
  const wsDrawings = allDrawings.filter((d) => workspace.drawingIds.includes(d.id));
359
431
  setDrawings(wsDrawings);
360
- if (workspace.activeDrawingId) {
361
- const active = await getDrawing(workspace.activeDrawingId);
362
- if (active) setActiveDrawing2(active);
363
- }
364
432
  } catch (err) {
365
433
  }
366
434
  }, [workspace]);
@@ -370,7 +438,6 @@ function WorkspaceProvider({ children, lang = "en" }) {
370
438
  const defaultName = `${t.newDrawing} ${drawings.length + 1}`;
371
439
  const drawing = await createDrawing(name || defaultName);
372
440
  await addDrawingToWorkspace(workspace.id, drawing.id);
373
- await setActiveDrawing(workspace.id, drawing.id);
374
441
  setDrawings((prev) => [...prev, drawing]);
375
442
  setActiveDrawing2(drawing);
376
443
  setWorkspace((prev) => prev ? {
@@ -378,6 +445,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
378
445
  drawingIds: [...prev.drawingIds, drawing.id],
379
446
  activeDrawingId: drawing.id
380
447
  } : null);
448
+ sessionStorage.setItem("rita-workspace-tab-drawing", drawing.id);
381
449
  return drawing;
382
450
  } catch (err) {
383
451
  setError(err instanceof Error ? err.message : "Failed to create drawing");
@@ -389,9 +457,9 @@ function WorkspaceProvider({ children, lang = "en" }) {
389
457
  try {
390
458
  const drawing = await getDrawing(id);
391
459
  if (drawing) {
392
- await setActiveDrawing(workspace.id, id);
393
460
  setActiveDrawing2(drawing);
394
461
  setWorkspace((prev) => prev ? { ...prev, activeDrawingId: id } : null);
462
+ sessionStorage.setItem("rita-workspace-tab-drawing", id);
395
463
  }
396
464
  } catch (err) {
397
465
  setError(err instanceof Error ? err.message : "Failed to switch drawing");
@@ -607,6 +675,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
607
675
  activeDrawing,
608
676
  isLoading,
609
677
  error,
678
+ isDrawingConflict,
610
679
  lang,
611
680
  t,
612
681
  createNewDrawing,
@@ -988,11 +1057,15 @@ var DrawingsDialog = ({
988
1057
  const [position, setPosition] = (0, import_react5.useState)(null);
989
1058
  const dragRef = (0, import_react5.useRef)(null);
990
1059
  const dialogRef = (0, import_react5.useRef)(null);
1060
+ const prevOpenRef = (0, import_react5.useRef)(false);
991
1061
  (0, import_react5.useEffect)(() => {
992
1062
  if (open) {
993
1063
  refreshDrawings();
994
- setPosition(null);
1064
+ if (!prevOpenRef.current) {
1065
+ setPosition(null);
1066
+ }
995
1067
  }
1068
+ prevOpenRef.current = open;
996
1069
  }, [open, refreshDrawings]);
997
1070
  const handleMouseDown = (0, import_react5.useCallback)((e) => {
998
1071
  if (e.target.closest("button")) return;
@@ -1099,17 +1172,16 @@ var DrawingsDialog = ({
1099
1172
  left: 0,
1100
1173
  right: 0,
1101
1174
  bottom: 0,
1102
- backgroundColor: position ? "transparent" : "rgba(0, 0, 0, 0.4)",
1175
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
1103
1176
  display: position ? "block" : "flex",
1104
1177
  alignItems: "center",
1105
1178
  justifyContent: "center",
1106
- zIndex: 9999,
1107
- pointerEvents: position ? "none" : "auto"
1179
+ zIndex: 9999
1108
1180
  },
1109
1181
  onClick: (e) => {
1110
- if (!position && e.target === e.currentTarget) onClose();
1182
+ if (e.target === e.currentTarget) onClose();
1111
1183
  },
1112
- children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: dialogRef, className: "rita-workspace-dialog", style: { ...dialogStyle, pointerEvents: "auto" }, children: [
1184
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { ref: dialogRef, className: "rita-workspace-dialog", style: dialogStyle, children: [
1113
1185
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
1114
1186
  "div",
1115
1187
  {
package/dist/index.mjs CHANGED
@@ -261,12 +261,70 @@ function useWorkspaceLang() {
261
261
  }
262
262
  return { lang: context.lang, t: context.t };
263
263
  }
264
+ var TAB_ID = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).slice(2);
265
+ var TABS_KEY = "rita-workspace-tabs";
266
+ var TAB_CHANNEL = "rita-workspace-tabs";
267
+ function getTabsMap() {
268
+ try {
269
+ return JSON.parse(localStorage.getItem(TABS_KEY) || "{}");
270
+ } catch {
271
+ return {};
272
+ }
273
+ }
274
+ function setTabDrawing(drawingId) {
275
+ const tabs = getTabsMap();
276
+ if (drawingId) {
277
+ tabs[TAB_ID] = drawingId;
278
+ } else {
279
+ delete tabs[TAB_ID];
280
+ }
281
+ localStorage.setItem(TABS_KEY, JSON.stringify(tabs));
282
+ }
283
+ function isDrawingOpenInOtherTab(drawingId) {
284
+ const tabs = getTabsMap();
285
+ return Object.entries(tabs).some(
286
+ ([tabId, dId]) => tabId !== TAB_ID && dId === drawingId
287
+ );
288
+ }
264
289
  function WorkspaceProvider({ children, lang = "en" }) {
265
290
  const [workspace, setWorkspace] = useState(null);
266
291
  const [drawings, setDrawings] = useState([]);
267
292
  const [activeDrawing, setActiveDrawing2] = useState(null);
268
293
  const [isLoading, setIsLoading] = useState(true);
269
294
  const [error, setError] = useState(null);
295
+ const [isDrawingConflict, setIsDrawingConflict] = useState(false);
296
+ useEffect(() => {
297
+ const drawingId = activeDrawing?.id || null;
298
+ setTabDrawing(drawingId);
299
+ if (drawingId) {
300
+ setIsDrawingConflict(isDrawingOpenInOtherTab(drawingId));
301
+ } else {
302
+ setIsDrawingConflict(false);
303
+ }
304
+ let channel = null;
305
+ try {
306
+ channel = new BroadcastChannel(TAB_CHANNEL);
307
+ channel.postMessage({ type: "drawing-changed", tabId: TAB_ID, drawingId });
308
+ channel.onmessage = (event) => {
309
+ if (event.data?.tabId !== TAB_ID && drawingId) {
310
+ setIsDrawingConflict(isDrawingOpenInOtherTab(drawingId));
311
+ }
312
+ };
313
+ } catch {
314
+ }
315
+ return () => {
316
+ channel?.close();
317
+ };
318
+ }, [activeDrawing?.id]);
319
+ useEffect(() => {
320
+ const onUnload = () => {
321
+ const tabs = getTabsMap();
322
+ delete tabs[TAB_ID];
323
+ localStorage.setItem(TABS_KEY, JSON.stringify(tabs));
324
+ };
325
+ window.addEventListener("beforeunload", onUnload);
326
+ return () => window.removeEventListener("beforeunload", onUnload);
327
+ }, []);
270
328
  const t = getTranslations(lang);
271
329
  useEffect(() => {
272
330
  async function init() {
@@ -277,9 +335,23 @@ function WorkspaceProvider({ children, lang = "en" }) {
277
335
  const allDrawings = await getAllDrawings();
278
336
  const wsDrawings = allDrawings.filter((d) => ws.drawingIds.includes(d.id));
279
337
  setDrawings(wsDrawings);
280
- if (ws.activeDrawingId) {
281
- const active = await getDrawing(ws.activeDrawingId);
282
- setActiveDrawing2(active || null);
338
+ const lastDrawingId = sessionStorage.getItem("rita-workspace-tab-drawing");
339
+ let active = null;
340
+ if (lastDrawingId) {
341
+ active = wsDrawings.find((d) => d.id === lastDrawingId) || null;
342
+ if (!active) {
343
+ const fromDb = await getDrawing(lastDrawingId);
344
+ if (fromDb && ws.drawingIds.includes(fromDb.id)) {
345
+ active = fromDb;
346
+ }
347
+ }
348
+ }
349
+ if (!active && wsDrawings.length > 0) {
350
+ active = wsDrawings[0];
351
+ }
352
+ if (active) {
353
+ setActiveDrawing2(active);
354
+ sessionStorage.setItem("rita-workspace-tab-drawing", active.id);
283
355
  }
284
356
  } catch (err) {
285
357
  setError(err instanceof Error ? err.message : "Failed to load workspace");
@@ -295,10 +367,6 @@ function WorkspaceProvider({ children, lang = "en" }) {
295
367
  const allDrawings = await getAllDrawings();
296
368
  const wsDrawings = allDrawings.filter((d) => workspace.drawingIds.includes(d.id));
297
369
  setDrawings(wsDrawings);
298
- if (workspace.activeDrawingId) {
299
- const active = await getDrawing(workspace.activeDrawingId);
300
- if (active) setActiveDrawing2(active);
301
- }
302
370
  } catch (err) {
303
371
  }
304
372
  }, [workspace]);
@@ -308,7 +376,6 @@ function WorkspaceProvider({ children, lang = "en" }) {
308
376
  const defaultName = `${t.newDrawing} ${drawings.length + 1}`;
309
377
  const drawing = await createDrawing(name || defaultName);
310
378
  await addDrawingToWorkspace(workspace.id, drawing.id);
311
- await setActiveDrawing(workspace.id, drawing.id);
312
379
  setDrawings((prev) => [...prev, drawing]);
313
380
  setActiveDrawing2(drawing);
314
381
  setWorkspace((prev) => prev ? {
@@ -316,6 +383,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
316
383
  drawingIds: [...prev.drawingIds, drawing.id],
317
384
  activeDrawingId: drawing.id
318
385
  } : null);
386
+ sessionStorage.setItem("rita-workspace-tab-drawing", drawing.id);
319
387
  return drawing;
320
388
  } catch (err) {
321
389
  setError(err instanceof Error ? err.message : "Failed to create drawing");
@@ -327,9 +395,9 @@ function WorkspaceProvider({ children, lang = "en" }) {
327
395
  try {
328
396
  const drawing = await getDrawing(id);
329
397
  if (drawing) {
330
- await setActiveDrawing(workspace.id, id);
331
398
  setActiveDrawing2(drawing);
332
399
  setWorkspace((prev) => prev ? { ...prev, activeDrawingId: id } : null);
400
+ sessionStorage.setItem("rita-workspace-tab-drawing", id);
333
401
  }
334
402
  } catch (err) {
335
403
  setError(err instanceof Error ? err.message : "Failed to switch drawing");
@@ -545,6 +613,7 @@ function WorkspaceProvider({ children, lang = "en" }) {
545
613
  activeDrawing,
546
614
  isLoading,
547
615
  error,
616
+ isDrawingConflict,
548
617
  lang,
549
618
  t,
550
619
  createNewDrawing,
@@ -926,11 +995,15 @@ var DrawingsDialog = ({
926
995
  const [position, setPosition] = useState4(null);
927
996
  const dragRef = useRef2(null);
928
997
  const dialogRef = useRef2(null);
998
+ const prevOpenRef = useRef2(false);
929
999
  useEffect3(() => {
930
1000
  if (open) {
931
1001
  refreshDrawings();
932
- setPosition(null);
1002
+ if (!prevOpenRef.current) {
1003
+ setPosition(null);
1004
+ }
933
1005
  }
1006
+ prevOpenRef.current = open;
934
1007
  }, [open, refreshDrawings]);
935
1008
  const handleMouseDown = useCallback2((e) => {
936
1009
  if (e.target.closest("button")) return;
@@ -1037,17 +1110,16 @@ var DrawingsDialog = ({
1037
1110
  left: 0,
1038
1111
  right: 0,
1039
1112
  bottom: 0,
1040
- backgroundColor: position ? "transparent" : "rgba(0, 0, 0, 0.4)",
1113
+ backgroundColor: "rgba(0, 0, 0, 0.4)",
1041
1114
  display: position ? "block" : "flex",
1042
1115
  alignItems: "center",
1043
1116
  justifyContent: "center",
1044
- zIndex: 9999,
1045
- pointerEvents: position ? "none" : "auto"
1117
+ zIndex: 9999
1046
1118
  },
1047
1119
  onClick: (e) => {
1048
- if (!position && e.target === e.currentTarget) onClose();
1120
+ if (e.target === e.currentTarget) onClose();
1049
1121
  },
1050
- children: /* @__PURE__ */ jsxs4("div", { ref: dialogRef, className: "rita-workspace-dialog", style: { ...dialogStyle, pointerEvents: "auto" }, children: [
1122
+ children: /* @__PURE__ */ jsxs4("div", { ref: dialogRef, className: "rita-workspace-dialog", style: dialogStyle, children: [
1051
1123
  /* @__PURE__ */ jsxs4(
1052
1124
  "div",
1053
1125
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rita-workspace",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Multi-drawing workspace feature for Rita (Excalidraw fork)",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",