@excalidraw/excalidraw 0.17.1-a38e82f → 0.17.1-b7babe5

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/browser/dev/excalidraw-assets-dev/{chunk-IM4WTX2M.js → chunk-6NMK7JTV.js} +2 -1
  3. package/dist/browser/dev/excalidraw-assets-dev/chunk-6NMK7JTV.js.map +7 -0
  4. package/dist/browser/dev/excalidraw-assets-dev/{chunk-5VWQDKDR.js → chunk-CX3RATXT.js} +50 -5
  5. package/dist/browser/dev/excalidraw-assets-dev/chunk-CX3RATXT.js.map +7 -0
  6. package/dist/browser/dev/excalidraw-assets-dev/{en-IOBA4CS2.js → en-BZY7JRTM.js} +2 -2
  7. package/dist/browser/dev/excalidraw-assets-dev/{image-VKDAL6BQ.js → image-CVN3YKRW.js} +2 -2
  8. package/dist/browser/dev/index.js +332 -76
  9. package/dist/browser/dev/index.js.map +4 -4
  10. package/dist/browser/prod/excalidraw-assets/{chunk-N2C5DK3B.js → chunk-VJAIK3AX.js} +15 -15
  11. package/dist/browser/prod/excalidraw-assets/{chunk-LIG3S5TN.js → chunk-YYO5DFUW.js} +3 -3
  12. package/dist/browser/prod/excalidraw-assets/{en-WFZVQ7I6.js → en-O2YCQM2W.js} +1 -1
  13. package/dist/browser/prod/excalidraw-assets/image-6FKY54X5.js +1 -0
  14. package/dist/browser/prod/index.js +16 -16
  15. package/dist/{prod/en-TDNWCAOT.json → dev/en-EY7E2L5O.json} +1 -0
  16. package/dist/dev/index.js +372 -77
  17. package/dist/dev/index.js.map +3 -3
  18. package/dist/excalidraw/data/library.d.ts +60 -8
  19. package/dist/excalidraw/data/library.js +302 -33
  20. package/dist/excalidraw/element/index.d.ts +8 -0
  21. package/dist/excalidraw/element/index.js +23 -0
  22. package/dist/excalidraw/element/textElement.d.ts +16 -1
  23. package/dist/excalidraw/element/textElement.js +10 -3
  24. package/dist/excalidraw/index.d.ts +2 -2
  25. package/dist/excalidraw/index.js +2 -2
  26. package/dist/excalidraw/locales/en.json +1 -0
  27. package/dist/excalidraw/queue.d.ts +9 -0
  28. package/dist/excalidraw/queue.js +27 -0
  29. package/dist/excalidraw/types.d.ts +6 -6
  30. package/dist/excalidraw/utility-types.d.ts +2 -0
  31. package/dist/excalidraw/utils.d.ts +3 -1
  32. package/dist/excalidraw/utils.js +6 -0
  33. package/dist/{dev/en-TDNWCAOT.json → prod/en-EY7E2L5O.json} +1 -0
  34. package/dist/prod/index.js +26 -26
  35. package/package.json +1 -1
  36. package/dist/browser/dev/excalidraw-assets-dev/chunk-5VWQDKDR.js.map +0 -7
  37. package/dist/browser/dev/excalidraw-assets-dev/chunk-IM4WTX2M.js.map +0 -7
  38. package/dist/browser/prod/excalidraw-assets/image-4AT7LYMR.js +0 -1
  39. /package/dist/browser/dev/excalidraw-assets-dev/{en-IOBA4CS2.js.map → en-BZY7JRTM.js.map} +0 -0
  40. /package/dist/browser/dev/excalidraw-assets-dev/{image-VKDAL6BQ.js.map → image-CVN3YKRW.js.map} +0 -0
@@ -29,7 +29,7 @@ import {
29
29
  toolBar,
30
30
  userList,
31
31
  welcomeScreen
32
- } from "./chunk-IM4WTX2M.js";
32
+ } from "./chunk-6NMK7JTV.js";
33
33
  import "./chunk-F3UQABQJ.js";
34
34
  export {
35
35
  alerts,
@@ -63,4 +63,4 @@ export {
63
63
  userList,
64
64
  welcomeScreen
65
65
  };
66
- //# sourceMappingURL=en-IOBA4CS2.js.map
66
+ //# sourceMappingURL=en-BZY7JRTM.js.map
@@ -4,7 +4,7 @@ import {
4
4
  encodePngMetadata,
5
5
  encodeSvgMetadata,
6
6
  getTEXtChunk
7
- } from "./chunk-5VWQDKDR.js";
7
+ } from "./chunk-CX3RATXT.js";
8
8
  import "./chunk-YRUDZAGT.js";
9
9
  import "./chunk-F3UQABQJ.js";
10
10
  export {
@@ -14,4 +14,4 @@ export {
14
14
  encodeSvgMetadata,
15
15
  getTEXtChunk
16
16
  };
17
- //# sourceMappingURL=image-VKDAL6BQ.js.map
17
+ //# sourceMappingURL=image-CVN3YKRW.js.map
@@ -226,6 +226,8 @@ import {
226
226
  hasStrokeColor,
227
227
  hasStrokeStyle,
228
228
  hasStrokeWidth,
229
+ hashElementsVersion,
230
+ hashString,
229
231
  hitTest,
230
232
  isArrowElement,
231
233
  isArrowKey,
@@ -319,8 +321,10 @@ import {
319
321
  originalContainerCache,
320
322
  parseClipboard,
321
323
  parseLibraryJSON,
324
+ preventUnload,
322
325
  probablySupportsClipboardBlob,
323
326
  probablySupportsClipboardWriteText,
327
+ promiseTry,
324
328
  queryFocusableElements,
325
329
  randomId,
326
330
  randomInteger,
@@ -342,6 +346,7 @@ import {
342
346
  resetOriginalContainerCache,
343
347
  resizeImageFile,
344
348
  resizeMultipleElements,
349
+ resolvablePromise,
345
350
  restore,
346
351
  restoreAppState,
347
352
  restoreElements,
@@ -385,14 +390,14 @@ import {
385
390
  viewportCoordsToSceneCoords,
386
391
  wrapEvent,
387
392
  wrapText
388
- } from "./excalidraw-assets-dev/chunk-5VWQDKDR.js";
393
+ } from "./excalidraw-assets-dev/chunk-CX3RATXT.js";
389
394
  import {
390
395
  define_import_meta_env_default,
391
396
  init_define_import_meta_env
392
397
  } from "./excalidraw-assets-dev/chunk-YRUDZAGT.js";
393
398
  import {
394
399
  en_default
395
- } from "./excalidraw-assets-dev/chunk-IM4WTX2M.js";
400
+ } from "./excalidraw-assets-dev/chunk-6NMK7JTV.js";
396
401
  import {
397
402
  percentages_default
398
403
  } from "./excalidraw-assets-dev/chunk-YZTYRBEQ.js";
@@ -2580,7 +2585,7 @@ var globImport_locales_json = __glob({
2580
2585
  "./locales/da-DK.json": () => import("./excalidraw-assets-dev/da-DK-6OKJ2GQ6.js"),
2581
2586
  "./locales/de-DE.json": () => import("./excalidraw-assets-dev/de-DE-BD5ICOQ2.js"),
2582
2587
  "./locales/el-GR.json": () => import("./excalidraw-assets-dev/el-GR-JFDBTDHS.js"),
2583
- "./locales/en.json": () => import("./excalidraw-assets-dev/en-IOBA4CS2.js"),
2588
+ "./locales/en.json": () => import("./excalidraw-assets-dev/en-BZY7JRTM.js"),
2584
2589
  "./locales/es-ES.json": () => import("./excalidraw-assets-dev/es-ES-CBBTGYGR.js"),
2585
2590
  "./locales/eu-ES.json": () => import("./excalidraw-assets-dev/eu-ES-CHAPMSLE.js"),
2586
2591
  "./locales/fa-IR.json": () => import("./excalidraw-assets-dev/fa-IR-LOJOKEJO.js"),
@@ -12147,7 +12152,7 @@ var exportCanvas = async (type, elements, appState, files, {
12147
12152
  let blob = canvasToBlob(tempCanvas);
12148
12153
  if (appState.exportEmbedScene) {
12149
12154
  blob = blob.then(
12150
- (blob2) => import("./excalidraw-assets-dev/image-VKDAL6BQ.js").then(
12155
+ (blob2) => import("./excalidraw-assets-dev/image-CVN3YKRW.js").then(
12151
12156
  ({ encodePngMetadata }) => encodePngMetadata({
12152
12157
  blob: blob2,
12153
12158
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -14840,8 +14845,76 @@ var useLibraryCache = () => {
14840
14845
  };
14841
14846
  };
14842
14847
 
14848
+ // emitter.ts
14849
+ init_define_import_meta_env();
14850
+ var Emitter = class {
14851
+ subscribers = [];
14852
+ /**
14853
+ * Attaches subscriber
14854
+ *
14855
+ * @returns unsubscribe function
14856
+ */
14857
+ on(...handlers) {
14858
+ const _handlers = handlers.flat().filter((item) => typeof item === "function");
14859
+ this.subscribers.push(..._handlers);
14860
+ return () => this.off(_handlers);
14861
+ }
14862
+ once(...handlers) {
14863
+ const _handlers = handlers.flat().filter((item) => typeof item === "function");
14864
+ _handlers.push(() => detach());
14865
+ const detach = this.on(..._handlers);
14866
+ return detach;
14867
+ }
14868
+ off(...handlers) {
14869
+ const _handlers = handlers.flat();
14870
+ this.subscribers = this.subscribers.filter(
14871
+ (handler) => !_handlers.includes(handler)
14872
+ );
14873
+ }
14874
+ trigger(...payload) {
14875
+ for (const handler of this.subscribers) {
14876
+ handler(...payload);
14877
+ }
14878
+ return this;
14879
+ }
14880
+ clear() {
14881
+ this.subscribers = [];
14882
+ }
14883
+ };
14884
+
14885
+ // queue.ts
14886
+ init_define_import_meta_env();
14887
+ var Queue = class {
14888
+ jobs = [];
14889
+ running = false;
14890
+ tick() {
14891
+ if (this.running) {
14892
+ return;
14893
+ }
14894
+ const job = this.jobs.shift();
14895
+ if (job) {
14896
+ this.running = true;
14897
+ job.promise.resolve(
14898
+ promiseTry(job.jobFactory, ...job.args).finally(() => {
14899
+ this.running = false;
14900
+ this.tick();
14901
+ })
14902
+ );
14903
+ } else {
14904
+ this.running = false;
14905
+ }
14906
+ }
14907
+ push(jobFactory, ...args) {
14908
+ const promise = resolvablePromise();
14909
+ this.jobs.push({ jobFactory, promise, args });
14910
+ this.tick();
14911
+ return promise;
14912
+ }
14913
+ };
14914
+
14843
14915
  // data/library.ts
14844
- var libraryItemsAtom = atom2({ status: "loaded", isInitialized: true, libraryItems: [] });
14916
+ var onLibraryUpdateEmitter = new Emitter();
14917
+ var libraryItemsAtom = atom2({ status: "loaded", isInitialized: false, libraryItems: [] });
14845
14918
  var cloneLibraryItems = (libraryItems) => cloneJSON(libraryItems);
14846
14919
  var isUniqueItem = (existingLibraryItems, targetLibraryItem) => {
14847
14920
  return !existingLibraryItems.find((libraryItem) => {
@@ -14862,12 +14935,30 @@ var mergeLibraryItems = (localItems, otherItems) => {
14862
14935
  }
14863
14936
  return [...newItems, ...localItems];
14864
14937
  };
14938
+ var createLibraryUpdate = (prevLibraryItems, nextLibraryItems) => {
14939
+ const nextItemsMap = arrayToMap(nextLibraryItems);
14940
+ const update = {
14941
+ deletedItems: /* @__PURE__ */ new Map(),
14942
+ addedItems: /* @__PURE__ */ new Map()
14943
+ };
14944
+ for (const item of prevLibraryItems) {
14945
+ if (!nextItemsMap.has(item.id)) {
14946
+ update.deletedItems.set(item.id, item);
14947
+ }
14948
+ }
14949
+ const prevItemsMap = arrayToMap(prevLibraryItems);
14950
+ for (const item of nextLibraryItems) {
14951
+ if (!prevItemsMap.has(item.id)) {
14952
+ update.addedItems.set(item.id, item);
14953
+ }
14954
+ }
14955
+ return update;
14956
+ };
14865
14957
  var Library = class {
14866
14958
  /** latest libraryItems */
14867
- lastLibraryItems = [];
14868
- /** indicates whether library is initialized with library items (has gone
14869
- * though at least one update) */
14870
- isInitialized = false;
14959
+ currLibraryItems = [];
14960
+ /** snapshot of library items since last onLibraryChange call */
14961
+ prevLibraryItems = cloneLibraryItems(this.currLibraryItems);
14871
14962
  app;
14872
14963
  constructor(app) {
14873
14964
  this.app = app;
@@ -14878,21 +14969,25 @@ var Library = class {
14878
14969
  };
14879
14970
  notifyListeners = () => {
14880
14971
  if (this.updateQueue.length > 0) {
14881
- jotaiStore.set(libraryItemsAtom, {
14972
+ jotaiStore.set(libraryItemsAtom, (s3) => ({
14882
14973
  status: "loading",
14883
- libraryItems: this.lastLibraryItems,
14884
- isInitialized: this.isInitialized
14885
- });
14974
+ libraryItems: this.currLibraryItems,
14975
+ isInitialized: s3.isInitialized
14976
+ }));
14886
14977
  } else {
14887
- this.isInitialized = true;
14888
14978
  jotaiStore.set(libraryItemsAtom, {
14889
14979
  status: "loaded",
14890
- libraryItems: this.lastLibraryItems,
14891
- isInitialized: this.isInitialized
14980
+ libraryItems: this.currLibraryItems,
14981
+ isInitialized: true
14892
14982
  });
14893
14983
  try {
14894
- this.app.props.onLibraryChange?.(
14895
- cloneLibraryItems(this.lastLibraryItems)
14984
+ const prevLibraryItems = this.prevLibraryItems;
14985
+ this.prevLibraryItems = cloneLibraryItems(this.currLibraryItems);
14986
+ const nextLibraryItems = cloneLibraryItems(this.currLibraryItems);
14987
+ this.app.props.onLibraryChange?.(nextLibraryItems);
14988
+ onLibraryUpdateEmitter.trigger(
14989
+ createLibraryUpdate(prevLibraryItems, nextLibraryItems),
14990
+ nextLibraryItems
14896
14991
  );
14897
14992
  } catch (error) {
14898
14993
  console.error(error);
@@ -14901,9 +14996,8 @@ var Library = class {
14901
14996
  };
14902
14997
  /** call on excalidraw instance unmount */
14903
14998
  destroy = () => {
14904
- this.isInitialized = false;
14905
14999
  this.updateQueue = [];
14906
- this.lastLibraryItems = [];
15000
+ this.currLibraryItems = [];
14907
15001
  jotaiStore.set(libraryItemSvgsCache, /* @__PURE__ */ new Map());
14908
15002
  };
14909
15003
  resetLibrary = () => {
@@ -14915,14 +15009,14 @@ var Library = class {
14915
15009
  getLatestLibrary = () => {
14916
15010
  return new Promise(async (resolve) => {
14917
15011
  try {
14918
- const libraryItems = await (this.getLastUpdateTask() || this.lastLibraryItems);
15012
+ const libraryItems = await (this.getLastUpdateTask() || this.currLibraryItems);
14919
15013
  if (this.updateQueue.length > 0) {
14920
15014
  resolve(this.getLatestLibrary());
14921
15015
  } else {
14922
15016
  resolve(cloneLibraryItems(libraryItems));
14923
15017
  }
14924
15018
  } catch (error) {
14925
- return resolve(this.lastLibraryItems);
15019
+ return resolve(this.currLibraryItems);
14926
15020
  }
14927
15021
  });
14928
15022
  };
@@ -14944,7 +15038,7 @@ var Library = class {
14944
15038
  return this.setLibrary(() => {
14945
15039
  return new Promise(async (resolve, reject) => {
14946
15040
  try {
14947
- const source = await (typeof libraryItems === "function" && !(libraryItems instanceof Blob) ? libraryItems(this.lastLibraryItems) : libraryItems);
15041
+ const source = await (typeof libraryItems === "function" && !(libraryItems instanceof Blob) ? libraryItems(this.currLibraryItems) : libraryItems);
14948
15042
  let nextItems;
14949
15043
  if (source instanceof Blob) {
14950
15044
  nextItems = await loadLibraryFromBlob(source, defaultStatus);
@@ -14960,7 +15054,7 @@ var Library = class {
14960
15054
  this.app.focusContainer();
14961
15055
  }
14962
15056
  if (merge) {
14963
- resolve(mergeLibraryItems(this.lastLibraryItems, nextItems));
15057
+ resolve(mergeLibraryItems(this.currLibraryItems, nextItems));
14964
15058
  } else {
14965
15059
  resolve(nextItems);
14966
15060
  }
@@ -14978,17 +15072,17 @@ var Library = class {
14978
15072
  try {
14979
15073
  await this.getLastUpdateTask();
14980
15074
  if (typeof libraryItems === "function") {
14981
- libraryItems = libraryItems(this.lastLibraryItems);
15075
+ libraryItems = libraryItems(this.currLibraryItems);
14982
15076
  }
14983
- this.lastLibraryItems = cloneLibraryItems(await libraryItems);
14984
- resolve(this.lastLibraryItems);
15077
+ this.currLibraryItems = cloneLibraryItems(await libraryItems);
15078
+ resolve(this.currLibraryItems);
14985
15079
  } catch (error) {
14986
15080
  reject(error);
14987
15081
  }
14988
15082
  }).catch((error) => {
14989
15083
  if (error.name === "AbortError") {
14990
15084
  console.warn("Library update aborted by user");
14991
- return this.lastLibraryItems;
15085
+ return this.currLibraryItems;
14992
15086
  }
14993
15087
  throw error;
14994
15088
  }).finally(() => {
@@ -15080,15 +15174,86 @@ var parseLibraryTokensFromUrl = () => {
15080
15174
  const idToken = libraryUrl ? new URLSearchParams(window.location.hash.slice(1)).get("token") : null;
15081
15175
  return libraryUrl ? { libraryUrl, idToken } : null;
15082
15176
  };
15083
- var useHandleLibrary = ({
15084
- excalidrawAPI,
15085
- getInitialLibraryItems
15086
- }) => {
15087
- const getInitialLibraryRef = (0, import_react45.useRef)(getInitialLibraryItems);
15177
+ var AdapterTransaction = class _AdapterTransaction {
15178
+ static queue = new Queue();
15179
+ static async getLibraryItems(adapter, source, _queue = true) {
15180
+ const task = () => new Promise(async (resolve, reject) => {
15181
+ try {
15182
+ const data = await adapter.load({ source });
15183
+ resolve(restoreLibraryItems(data?.libraryItems || [], "published"));
15184
+ } catch (error) {
15185
+ reject(error);
15186
+ }
15187
+ });
15188
+ if (_queue) {
15189
+ return _AdapterTransaction.queue.push(task);
15190
+ }
15191
+ return task();
15192
+ }
15193
+ static run = async (adapter, fn) => {
15194
+ const transaction = new _AdapterTransaction(adapter);
15195
+ return _AdapterTransaction.queue.push(() => fn(transaction));
15196
+ };
15197
+ // ------------------
15198
+ adapter;
15199
+ constructor(adapter) {
15200
+ this.adapter = adapter;
15201
+ }
15202
+ getLibraryItems(source) {
15203
+ return _AdapterTransaction.getLibraryItems(this.adapter, source, false);
15204
+ }
15205
+ };
15206
+ var lastSavedLibraryItemsHash = 0;
15207
+ var librarySaveCounter = 0;
15208
+ var getLibraryItemsHash = (items) => {
15209
+ return hashString(
15210
+ items.map((item) => {
15211
+ return `${item.id}:${hashElementsVersion(item.elements)}`;
15212
+ }).sort().join()
15213
+ );
15214
+ };
15215
+ var persistLibraryUpdate = async (adapter, update) => {
15216
+ try {
15217
+ librarySaveCounter++;
15218
+ return await AdapterTransaction.run(adapter, async (transaction) => {
15219
+ const nextLibraryItemsMap = arrayToMap(
15220
+ await transaction.getLibraryItems("save")
15221
+ );
15222
+ for (const [id] of update.deletedItems) {
15223
+ nextLibraryItemsMap.delete(id);
15224
+ }
15225
+ const addedItems = [];
15226
+ for (const [id, item] of update.addedItems) {
15227
+ if (nextLibraryItemsMap.has(id)) {
15228
+ nextLibraryItemsMap.set(id, item);
15229
+ } else {
15230
+ addedItems.push(item);
15231
+ }
15232
+ }
15233
+ const nextLibraryItems = addedItems.concat(
15234
+ Array.from(nextLibraryItemsMap.values())
15235
+ );
15236
+ const version = getLibraryItemsHash(nextLibraryItems);
15237
+ if (version !== lastSavedLibraryItemsHash) {
15238
+ await adapter.save({ libraryItems: nextLibraryItems });
15239
+ }
15240
+ lastSavedLibraryItemsHash = version;
15241
+ return nextLibraryItems;
15242
+ });
15243
+ } finally {
15244
+ librarySaveCounter--;
15245
+ }
15246
+ };
15247
+ var useHandleLibrary = (opts) => {
15248
+ const { excalidrawAPI } = opts;
15249
+ const optsRef = (0, import_react45.useRef)(opts);
15250
+ optsRef.current = opts;
15251
+ const isLibraryLoadedRef = (0, import_react45.useRef)(false);
15088
15252
  (0, import_react45.useEffect)(() => {
15089
15253
  if (!excalidrawAPI) {
15090
15254
  return;
15091
15255
  }
15256
+ isLibraryLoadedRef.current = false;
15092
15257
  const importLibraryFromURL = async ({
15093
15258
  libraryUrl,
15094
15259
  idToken
@@ -15139,20 +15304,145 @@ var useHandleLibrary = ({
15139
15304
  importLibraryFromURL(libraryUrlTokens2);
15140
15305
  }
15141
15306
  };
15142
- if (getInitialLibraryRef.current) {
15143
- excalidrawAPI.updateLibrary({
15144
- libraryItems: getInitialLibraryRef.current()
15145
- });
15146
- }
15147
15307
  const libraryUrlTokens = parseLibraryTokensFromUrl();
15148
15308
  if (libraryUrlTokens) {
15149
15309
  importLibraryFromURL(libraryUrlTokens);
15150
15310
  }
15311
+ if ("getInitialLibraryItems" in optsRef.current && optsRef.current.getInitialLibraryItems) {
15312
+ console.warn(
15313
+ "useHandleLibrar `opts.getInitialLibraryItems` is deprecated. Use `opts.adapter` instead."
15314
+ );
15315
+ Promise.resolve(optsRef.current.getInitialLibraryItems()).then((libraryItems) => {
15316
+ excalidrawAPI.updateLibrary({
15317
+ libraryItems,
15318
+ // merge with current library items because we may have already
15319
+ // populated it (e.g. by installing 3rd party library which can
15320
+ // happen before the DB data is loaded)
15321
+ merge: true
15322
+ });
15323
+ }).catch((error) => {
15324
+ console.error(
15325
+ `UseHandeLibrary getInitialLibraryItems failed: ${error?.message}`
15326
+ );
15327
+ });
15328
+ }
15329
+ if ("adapter" in optsRef.current && optsRef.current.adapter) {
15330
+ const adapter = optsRef.current.adapter;
15331
+ const migrationAdapter = optsRef.current.migrationAdapter;
15332
+ const initDataPromise = resolvablePromise();
15333
+ if (migrationAdapter) {
15334
+ initDataPromise.resolve(
15335
+ promiseTry(migrationAdapter.load).then(async (libraryData) => {
15336
+ let restoredData = null;
15337
+ try {
15338
+ if (!libraryData) {
15339
+ return AdapterTransaction.getLibraryItems(adapter, "load");
15340
+ }
15341
+ restoredData = restoreLibraryItems(
15342
+ libraryData.libraryItems || [],
15343
+ "published"
15344
+ );
15345
+ const nextItems = await persistLibraryUpdate(
15346
+ adapter,
15347
+ createLibraryUpdate([], restoredData)
15348
+ );
15349
+ try {
15350
+ await migrationAdapter.clear();
15351
+ } catch (error) {
15352
+ console.error(
15353
+ `couldn't delete legacy library data: ${error.message}`
15354
+ );
15355
+ }
15356
+ return nextItems;
15357
+ } catch (error) {
15358
+ console.error(
15359
+ `couldn't migrate legacy library data: ${error.message}`
15360
+ );
15361
+ return restoredData;
15362
+ }
15363
+ }).catch((error) => {
15364
+ console.error(`error during library migration: ${error.message}`);
15365
+ return AdapterTransaction.getLibraryItems(adapter, "load");
15366
+ })
15367
+ );
15368
+ } else {
15369
+ initDataPromise.resolve(
15370
+ promiseTry(AdapterTransaction.getLibraryItems, adapter, "load")
15371
+ );
15372
+ }
15373
+ excalidrawAPI.updateLibrary({
15374
+ libraryItems: initDataPromise.then((libraryItems) => {
15375
+ const _libraryItems = libraryItems || [];
15376
+ lastSavedLibraryItemsHash = getLibraryItemsHash(_libraryItems);
15377
+ return _libraryItems;
15378
+ }),
15379
+ // merge with current library items because we may have already
15380
+ // populated it (e.g. by installing 3rd party library which can
15381
+ // happen before the DB data is loaded)
15382
+ merge: true
15383
+ }).finally(() => {
15384
+ isLibraryLoadedRef.current = true;
15385
+ });
15386
+ }
15151
15387
  window.addEventListener("hashchange" /* HASHCHANGE */, onHashChange);
15152
15388
  return () => {
15153
15389
  window.removeEventListener("hashchange" /* HASHCHANGE */, onHashChange);
15154
15390
  };
15155
- }, [excalidrawAPI]);
15391
+ }, [
15392
+ // important this useEffect only depends on excalidrawAPI so it only reruns
15393
+ // on editor remounts (the excalidrawAPI changes)
15394
+ excalidrawAPI
15395
+ ]);
15396
+ (0, import_react45.useEffect)(
15397
+ () => {
15398
+ const unsubOnLibraryUpdate = onLibraryUpdateEmitter.on(
15399
+ async (update, nextLibraryItems) => {
15400
+ const isLoaded = isLibraryLoadedRef.current;
15401
+ const adapter = "adapter" in optsRef.current && optsRef.current.adapter || null;
15402
+ try {
15403
+ if (adapter) {
15404
+ if (
15405
+ // if nextLibraryItems hash identical to previously saved hash,
15406
+ // exit early, even if actual upstream state ends up being
15407
+ // different (e.g. has more data than we have locally), as it'd
15408
+ // be low-impact scenario.
15409
+ lastSavedLibraryItemsHash !== getLibraryItemsHash(nextLibraryItems)
15410
+ ) {
15411
+ await persistLibraryUpdate(adapter, update);
15412
+ }
15413
+ }
15414
+ } catch (error) {
15415
+ console.error(
15416
+ `couldn't persist library update: ${error.message}`,
15417
+ update
15418
+ );
15419
+ if (isLoaded && optsRef.current.excalidrawAPI) {
15420
+ optsRef.current.excalidrawAPI.updateScene({
15421
+ appState: {
15422
+ errorMessage: t("errors.saveLibraryError")
15423
+ }
15424
+ });
15425
+ }
15426
+ }
15427
+ }
15428
+ );
15429
+ const onUnload = (event) => {
15430
+ if (librarySaveCounter) {
15431
+ preventUnload(event);
15432
+ }
15433
+ };
15434
+ window.addEventListener("beforeunload" /* BEFORE_UNLOAD */, onUnload);
15435
+ return () => {
15436
+ window.removeEventListener("beforeunload" /* BEFORE_UNLOAD */, onUnload);
15437
+ unsubOnLibraryUpdate();
15438
+ lastSavedLibraryItemsHash = 0;
15439
+ librarySaveCounter = 0;
15440
+ };
15441
+ },
15442
+ [
15443
+ // this effect must not have any deps so it doesn't rerun
15444
+ ]
15445
+ );
15156
15446
  };
15157
15447
 
15158
15448
  // gesture.ts
@@ -26063,43 +26353,6 @@ var SVGLayer = ({ trails }) => {
26063
26353
  return /* @__PURE__ */ (0, import_jsx_runtime121.jsx)("div", { className: "SVGLayer", children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)("svg", { ref: svgRef }) });
26064
26354
  };
26065
26355
 
26066
- // emitter.ts
26067
- init_define_import_meta_env();
26068
- var Emitter = class {
26069
- subscribers = [];
26070
- /**
26071
- * Attaches subscriber
26072
- *
26073
- * @returns unsubscribe function
26074
- */
26075
- on(...handlers) {
26076
- const _handlers = handlers.flat().filter((item) => typeof item === "function");
26077
- this.subscribers.push(..._handlers);
26078
- return () => this.off(_handlers);
26079
- }
26080
- once(...handlers) {
26081
- const _handlers = handlers.flat().filter((item) => typeof item === "function");
26082
- _handlers.push(() => detach());
26083
- const detach = this.on(..._handlers);
26084
- return detach;
26085
- }
26086
- off(...handlers) {
26087
- const _handlers = handlers.flat();
26088
- this.subscribers = this.subscribers.filter(
26089
- (handler) => !_handlers.includes(handler)
26090
- );
26091
- }
26092
- trigger(...payload) {
26093
- for (const handler of this.subscribers) {
26094
- handler(...payload);
26095
- }
26096
- return this;
26097
- }
26098
- clear() {
26099
- this.subscribers = [];
26100
- }
26101
- };
26102
-
26103
26356
  // element/ElementCanvasButtons.tsx
26104
26357
  init_define_import_meta_env();
26105
26358
  var import_jsx_runtime122 = __toESM(require_jsx_runtime(), 1);
@@ -34726,9 +34979,12 @@ export {
34726
34979
  exportToSvg2 as exportToSvg,
34727
34980
  getCommonBounds,
34728
34981
  getFreeDrawSvgPath,
34982
+ getLibraryItemsHash,
34729
34983
  getNonDeletedElements,
34730
34984
  getSceneVersion,
34731
34985
  getVisibleSceneBounds,
34986
+ hashElementsVersion,
34987
+ hashString,
34732
34988
  isElementInsideBBox,
34733
34989
  isInvisiblySmallElement,
34734
34990
  isLinearElement,