@kritzel/engine 0.4.31 → 0.4.32

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 (83) hide show
  1. package/LICENSE.md +49 -49
  2. package/dist/cjs/{engine.constants-CakmdKMW.js → engine.constants-cfbepvaI.js} +595 -98
  3. package/dist/cjs/{index-BbbWU2aN.js → index-BrjM5aN2.js} +12 -0
  4. package/dist/cjs/index.cjs.js +4 -2
  5. package/dist/cjs/kritzel-awareness-cursors_5.cjs.entry.js +257 -113
  6. package/dist/cjs/loader.cjs.js +2 -2
  7. package/dist/cjs/stencil.cjs.js +2 -2
  8. package/dist/collection/classes/core/core.class.js +40 -32
  9. package/dist/collection/classes/core/reviver.class.js +4 -0
  10. package/dist/collection/classes/core/store.class.js +10 -3
  11. package/dist/collection/classes/managers/cursor.manager.js +10 -5
  12. package/dist/collection/classes/objects/dynamic-object.class.js +18 -5
  13. package/dist/collection/classes/objects/iframe.class.js +447 -0
  14. package/dist/collection/classes/registries/icon-registry.class.js +100 -67
  15. package/dist/collection/classes/tools/selection-tool.class.js +3 -3
  16. package/dist/collection/collection-manifest.json +3 -1
  17. package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.css +115 -115
  18. package/dist/collection/components/kritzel-awareness-cursors/kritzel-awareness-cursors.js +1 -1
  19. package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.css +10 -10
  20. package/dist/collection/components/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  21. package/dist/collection/components/kritzel-engine/kritzel-engine.css +80 -80
  22. package/dist/collection/components/kritzel-engine/kritzel-engine.js +196 -44
  23. package/dist/collection/components/kritzel-icon/kritzel-icon.css +18 -18
  24. package/dist/collection/components/kritzel-icon/kritzel-icon.js +25 -4
  25. package/dist/collection/components/kritzel-watermark/kritzel-watermark.css +29 -29
  26. package/dist/collection/components/kritzel-watermark/kritzel-watermark.js +2 -2
  27. package/dist/collection/constants/version.js +1 -1
  28. package/dist/collection/helpers/cursor.helper.js +8 -8
  29. package/dist/collection/helpers/object.helper.js +13 -0
  30. package/dist/collection/helpers/svg-export.helper.js +55 -41
  31. package/dist/collection/helpers/wheel.helper.js +1 -7
  32. package/dist/collection/index.js +3 -0
  33. package/dist/collection/interfaces/embedded-object.interface.js +1 -0
  34. package/dist/collection/themes/dark-theme.js +1 -5
  35. package/dist/collection/themes/light-theme.js +1 -6
  36. package/dist/components/index.js +7 -7
  37. package/dist/components/kritzel-awareness-cursors.js +1 -1
  38. package/dist/components/kritzel-cursor-trail.js +1 -1
  39. package/dist/components/kritzel-engine.js +723 -130
  40. package/dist/components/kritzel-icon.js +1 -1
  41. package/dist/components/kritzel-watermark.js +1 -1
  42. package/dist/components/{p-YP8wAcF4.js → p-9YjkpHyU.js} +2 -2
  43. package/dist/components/p-BIUT0gBe.js +214 -0
  44. package/dist/components/{p-B2Htv9ou.js → p-Bnuu1qip.js} +3 -3
  45. package/dist/components/{p-CfSx6HU_.js → p-CJ5B8cMK.js} +12 -1
  46. package/dist/components/{p-BvRGQvVI.js → p-DXnF4FeS.js} +18 -14
  47. package/dist/esm/{engine.constants-Bl5-pbq-.js → engine.constants-DPDQhv6S.js} +594 -99
  48. package/dist/esm/{index-BU9Mv7y0.js → index-Hqbtnnex.js} +12 -1
  49. package/dist/esm/index.js +3 -3
  50. package/dist/esm/kritzel-awareness-cursors_5.entry.js +257 -113
  51. package/dist/esm/loader.js +3 -3
  52. package/dist/esm/stencil.js +3 -3
  53. package/dist/stencil/index.esm.js +3 -3
  54. package/dist/stencil/{p-Bl5-pbq-.js → p-DPDQhv6S.js} +594 -99
  55. package/dist/stencil/{p-BU9Mv7y0.js → p-Hqbtnnex.js} +12 -1
  56. package/dist/stencil/{p-f0a8b7af.entry.js → p-d678528e.entry.js} +257 -113
  57. package/dist/stencil/stencil.esm.js +3 -3
  58. package/dist/types/classes/core/store.class.d.ts +3 -2
  59. package/dist/types/classes/objects/dynamic-object.class.d.ts +4 -2
  60. package/dist/types/classes/objects/iframe.class.d.ts +76 -0
  61. package/dist/types/classes/registries/icon-registry.class.d.ts +75 -6
  62. package/dist/types/components/kritzel-engine/kritzel-engine.d.ts +14 -1
  63. package/dist/types/components/kritzel-icon/kritzel-icon.d.ts +5 -1
  64. package/dist/types/components.d.ts +26 -8
  65. package/dist/types/constants/version.d.ts +1 -1
  66. package/dist/types/helpers/cursor.helper.d.ts +2 -1
  67. package/dist/types/helpers/object.helper.d.ts +2 -0
  68. package/dist/types/helpers/svg-export.helper.d.ts +1 -0
  69. package/dist/types/helpers/wheel.helper.d.ts +2 -2
  70. package/dist/types/index.d.ts +3 -0
  71. package/dist/types/interfaces/context-menu-item.interface.d.ts +2 -1
  72. package/dist/types/interfaces/dynamic-object-renderer.interface.d.ts +7 -0
  73. package/dist/types/interfaces/embedded-object.interface.d.ts +9 -0
  74. package/dist/types/interfaces/engine-state.interface.d.ts +3 -2
  75. package/dist/types/interfaces/login-config.interface.d.ts +2 -1
  76. package/dist/types/interfaces/master-detail.interface.d.ts +2 -1
  77. package/dist/types/interfaces/menu-item.interface.d.ts +2 -1
  78. package/dist/types/interfaces/theme.interface.d.ts +6 -7
  79. package/dist/types/interfaces/tool.interface.d.ts +9 -0
  80. package/dist/types/interfaces/toolbar-item.interface.d.ts +3 -2
  81. package/package.json +1 -1
  82. package/readme.md +10 -10
  83. package/dist/components/p-fx6msYHJ.js +0 -167
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-BbbWU2aN.js');
4
- var engine_constants = require('./engine.constants-CakmdKMW.js');
3
+ var index = require('./index-BrjM5aN2.js');
4
+ var engine_constants = require('./engine.constants-cfbepvaI.js');
5
5
  var Y = require('yjs');
6
6
 
7
7
  function _interopNamespace(e) {
@@ -26,7 +26,7 @@ var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
26
26
 
27
27
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
28
28
 
29
- const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:1}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));transition:opacity 300ms ease}.edge-arrow.stale{opacity:0}.edge-label{position:absolute;white-space:nowrap;font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
29
+ const kritzelAwarenessCursorsCss = () => `:host{display:block;position:fixed;top:0;left:0;width:100vw;height:100vh;pointer-events:none;z-index:1}.awareness-cursor{position:absolute;top:0;left:0;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform}.awareness-cursor.stale{opacity:0}.awareness-cursor.tracking-object{transition-duration:0ms}.cursor-arrow{filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.cursor-label{position:absolute;left:16px;top:16px;white-space:nowrap;font-family:var(--kritzel-awareness-font-family, var(--kritzel-global-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif));font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;user-select:none}.edge-indicator{position:absolute;top:-12px;left:-12px;width:24px;height:24px;display:flex;align-items:center;justify-content:center;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, opacity 300ms ease;will-change:transform;pointer-events:auto;user-select:none;cursor:pointer}.edge-indicator.stale{opacity:0}.edge-indicator.tracking-object{transition-duration:0ms}.edge-arrow{position:absolute;filter:drop-shadow(0 1px 3px rgba(0, 0, 0, 0.3));transition:opacity 300ms ease}.edge-arrow.stale{opacity:0}.edge-label{position:absolute;white-space:nowrap;font-family:var(--kritzel-awareness-font-family, var(--kritzel-global-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif));font-size:var(--kritzel-awareness-cursor-label-font-size, 12px);color:var(--kritzel-awareness-cursor-label-text-color, #ffffff);padding:2px 8px;border-radius:4px;line-height:1.4;font-weight:500;pointer-events:none;opacity:0;transform-origin:center;transition:opacity 150ms ease}.edge-indicator:hover .edge-label{opacity:1}.remote-selection-box{position:absolute;top:0;left:0;border-width:2px;border-style:solid;pointer-events:none;will-change:transform, width, height;transition:transform var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, width var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out, height var(--kritzel-awareness-cursor-transition-duration, 100ms) ease-out}`;
30
30
 
31
31
  const STALE_THRESHOLD_MS = 10_000;
32
32
  const REMOVE_THRESHOLD_MS = 30_000;
@@ -231,7 +231,7 @@ const KritzelAwarenessCursors = class {
231
231
  }
232
232
  render() {
233
233
  const cursors = Array.from(this.remoteCursors.values());
234
- return (index.h(index.Host, { key: 'cf00004efc0c60e81bd6b2f19ecb7ca066396980' }, cursors.map(remoteCursor => {
234
+ return (index.h(index.Host, { key: '2021875ea3e430c04da438dc5b7a2c22f119ea67' }, cursors.map(remoteCursor => {
235
235
  if (!remoteCursor.cursor)
236
236
  return null;
237
237
  // When a remote user is actively drawing, derive cursor position from
@@ -383,7 +383,7 @@ const KritzelCursorTrail = class {
383
383
  }
384
384
  }
385
385
  render() {
386
- return (index.h(index.Host, { key: '707e81b135931233131a830d6fc63e721e381287' }, this.cursorTrailPoints.length > 1 && (index.h("svg", { key: 'b262b0e3a0cf3b1887ee6acedb2f00d72e95db7e', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
386
+ return (index.h(index.Host, { key: '39d026a0d4c444564f146a90ea1a51181d0652b7' }, this.cursorTrailPoints.length > 1 && (index.h("svg", { key: '927e52cd0828a548ba58e0e783b1d148a40c968c', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
387
387
  position: 'absolute',
388
388
  left: '0',
389
389
  top: '0',
@@ -18851,6 +18851,9 @@ class KritzelReviver {
18851
18851
  case 'KritzelDynamicObject':
18852
18852
  revivedObj = engine_constants.KritzelDynamicObject.create(this._core).deserialize(obj);
18853
18853
  break;
18854
+ case 'KritzelIframeObject':
18855
+ revivedObj = engine_constants.KritzelIframeObject.create(this._core).deserialize(obj);
18856
+ break;
18854
18857
  case 'KritzelSelectionGroup':
18855
18858
  revivedObj = engine_constants.KritzelSelectionGroup.create(this._core).deserialize(obj);
18856
18859
  break;
@@ -20675,9 +20678,16 @@ class KritzelStore {
20675
20678
  * Gets the currently selected dynamic object whose focused descendant is an input-like element.
20676
20679
  * @returns The interactive dynamic object with focused input-like content, or null if none matches
20677
20680
  */
20681
+ get activeInteractiveEmbeddedObject() {
20682
+ for (const object of this.selectedObjects) {
20683
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object) && object.hasFocusedInputElement()) {
20684
+ return object;
20685
+ }
20686
+ }
20687
+ return null;
20688
+ }
20678
20689
  get activeInteractiveDynamicObject() {
20679
- const interactiveDynamicObject = this.selectedObjects.find(object => engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject') && object.hasFocusedInputElement());
20680
- return interactiveDynamicObject ?? null;
20690
+ return this.activeInteractiveEmbeddedObject;
20681
20691
  }
20682
20692
  /**
20683
20693
  * Gets the path currently being drawn (not yet completed).
@@ -21265,11 +21275,13 @@ class KritzelCursorManager {
21265
21275
  // Reset cursor on old target
21266
21276
  if (this._targetElement) {
21267
21277
  this._targetElement.style.cursor = '';
21278
+ this._targetElement.style.removeProperty('--kritzel-global-default-cursor');
21268
21279
  this._targetElement.style.removeProperty('--kritzel-global-pointer-cursor');
21269
21280
  }
21270
21281
  this._targetElement = element;
21271
21282
  // Set the pointer cursor CSS variable for child components to use
21272
21283
  if (this._targetElement) {
21284
+ this._targetElement.style.setProperty('--kritzel-global-default-cursor', engine_constants.KritzelCursorHelper.getCursor({ iconName: 'default' }));
21273
21285
  this._targetElement.style.setProperty('--kritzel-global-pointer-cursor', engine_constants.KritzelCursorHelper.getPointerCursor());
21274
21286
  }
21275
21287
  }
@@ -21314,8 +21326,10 @@ class KritzelCursorManager {
21314
21326
  if (!this._shadowRoot)
21315
21327
  return;
21316
21328
  const elementsAtPoint = this._shadowRoot.elementsFromPoint(ev.clientX, ev.clientY);
21317
- if (!elementsAtPoint || elementsAtPoint.length === 0)
21329
+ if (!elementsAtPoint || elementsAtPoint.length === 0) {
21330
+ this.resetToDefault();
21318
21331
  return;
21332
+ }
21319
21333
  // Check for resize handle overlays (selection group)
21320
21334
  const resizeHandleOverlay = elementsAtPoint.find(el => el.classList.contains('resize-handle-overlay'));
21321
21335
  if (resizeHandleOverlay) {
@@ -21325,19 +21339,19 @@ class KritzelCursorManager {
21325
21339
  const isTopLeft = resizeHandleOverlay.classList.contains('top-left');
21326
21340
  const isBottomRight = resizeHandleOverlay.classList.contains('bottom-right');
21327
21341
  const rotationOffset = (isTopLeft || isBottomRight) ? -45 : 45;
21328
- this._core.store.state.cursor = { icon: 'move-vertical', rotation: rotationDegrees + rotationOffset };
21342
+ this._core.store.state.cursor = { icon: 'moveVertical', rotation: rotationDegrees + rotationOffset };
21329
21343
  return;
21330
21344
  }
21331
21345
  // Check for rotation handle overlay
21332
21346
  const rotationHandleOverlay = elementsAtPoint.find(el => el.classList.contains('rotation-handle-overlay'));
21333
21347
  if (rotationHandleOverlay) {
21334
- this._core.store.state.cursor = { icon: 'hand', iconActive: 'hand-grab' };
21348
+ this._core.store.state.cursor = { icon: 'hand', iconActive: 'handGrab' };
21335
21349
  return;
21336
21350
  }
21337
21351
  // Check for line handle overlays (selection line)
21338
21352
  const lineHandleOverlay = elementsAtPoint.find(el => el.classList.contains('selection-line-handle-overlay'));
21339
21353
  if (lineHandleOverlay) {
21340
- this._core.store.state.cursor = { icon: 'hand', iconActive: 'hand-grab' };
21354
+ this._core.store.state.cursor = { icon: 'hand', iconActive: 'handGrab' };
21341
21355
  return;
21342
21356
  }
21343
21357
  // Reset cursor if not hovering any handle
@@ -21372,7 +21386,8 @@ class KritzelCursorManager {
21372
21386
  cleanup() {
21373
21387
  if (this._targetElement) {
21374
21388
  this._targetElement.style.cursor = '';
21375
- this._targetElement.style.removeProperty('--kritzel-pointer-cursor');
21389
+ this._targetElement.style.removeProperty('--kritzel-global-default-cursor');
21390
+ this._targetElement.style.removeProperty('--kritzel-global-pointer-cursor');
21376
21391
  }
21377
21392
  this._targetElement = null;
21378
21393
  this._shadowRoot = null;
@@ -22297,7 +22312,7 @@ class KritzelCore {
22297
22312
  this._store.state.translateY = viewportHeight / 2 - (viewport.centerWorldY ?? 0) * scale;
22298
22313
  this._store.state.scale = scale;
22299
22314
  await objectsMap.initialize(this, activeWorkspace.id, this._syncConfig);
22300
- this.seedWorkspaceObjects(activeWorkspace, objectsMap);
22315
+ await this.seedWorkspaceObjects(activeWorkspace, objectsMap);
22301
22316
  // Set local user in awareness after object map is initialized
22302
22317
  if (this._user) {
22303
22318
  objectsMap.setLocalUser(this._user);
@@ -22311,15 +22326,21 @@ class KritzelCore {
22311
22326
  this.engine.emitObjectsChange();
22312
22327
  this.rerender();
22313
22328
  }
22314
- seedWorkspaceObjects(workspace, objectsMap) {
22329
+ async seedWorkspaceObjects(workspace, objectsMap) {
22315
22330
  if (!workspace.objects?.length) {
22316
22331
  return;
22317
22332
  }
22333
+ const pendingObjects = workspace.objects.filter(object => !objectsMap.findById(object.id));
22334
+ // Mirrors engine.addObject()/addObjects(): image asset upload is async and must
22335
+ // happen before the (synchronous) Yjs transaction that inserts the objects.
22336
+ for (const object of pendingObjects) {
22337
+ object._core = this;
22338
+ if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelImage')) {
22339
+ await object.prepareForInsert();
22340
+ }
22341
+ }
22318
22342
  objectsMap.transaction(() => {
22319
- for (const object of workspace.objects) {
22320
- if (objectsMap.findById(object.id)) {
22321
- continue;
22322
- }
22343
+ for (const object of pendingObjects) {
22323
22344
  this.prepareObjectForWorkspace(object, workspace.id, this._store.currentZIndex, this._store.state.scale);
22324
22345
  objectsMap.insert(object);
22325
22346
  }
@@ -22358,16 +22379,30 @@ class KritzelCore {
22358
22379
  * @param scaleFallback - Scale applied to objects that do not define one
22359
22380
  * @returns The objects that were added, updated and removed
22360
22381
  */
22361
- reconcileWorkspaceObjects(objectsMap, workspaceId, desiredObjects, nextZIndex, scaleFallback) {
22382
+ async reconcileWorkspaceObjects(objectsMap, workspaceId, desiredObjects, nextZIndex, scaleFallback) {
22362
22383
  const existingObjects = objectsMap.allObjects().filter(o => !(o instanceof engine_constants.KritzelSelectionGroup) && !(o instanceof engine_constants.KritzelSelectionBox));
22363
22384
  const existingById = new Map(existingObjects.map(object => [object.id, object]));
22364
22385
  const desiredIds = new Set();
22365
22386
  const diff = { added: [], updated: [], removed: [] };
22387
+ // Assign IDs up front so the insert-vs-update lookup below is stable, and so the
22388
+ // async image-prepare pass and the (synchronous) transaction agree on identity.
22389
+ for (const object of desiredObjects) {
22390
+ if (!object.id) {
22391
+ object.id = object.generateId();
22392
+ }
22393
+ }
22394
+ // Mirrors engine.addObject()/addObjects(): image asset upload is async and must
22395
+ // happen before the (synchronous) Yjs transaction that inserts the objects.
22396
+ for (const object of desiredObjects) {
22397
+ const existingObject = existingById.get(object.id);
22398
+ const isNewOrReplaced = !existingObject || existingObject !== object;
22399
+ if (isNewOrReplaced && engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelImage')) {
22400
+ object._core = this;
22401
+ await object.prepareForInsert();
22402
+ }
22403
+ }
22366
22404
  objectsMap.transaction(() => {
22367
22405
  for (const object of desiredObjects) {
22368
- if (!object.id) {
22369
- object.id = object.generateId();
22370
- }
22371
22406
  desiredIds.add(object.id);
22372
22407
  const existingObject = existingById.get(object.id);
22373
22408
  if (!existingObject) {
@@ -22405,7 +22440,7 @@ class KritzelCore {
22405
22440
  * object set stays exactly what the caller declared.
22406
22441
  */
22407
22442
  detachObject(object) {
22408
- if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
22443
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
22409
22444
  object.unmount();
22410
22445
  }
22411
22446
  object.isMounted = false;
@@ -22581,13 +22616,13 @@ class KritzelCore {
22581
22616
  activeWorkspace.viewport = { centerWorldX, centerWorldY, scale };
22582
22617
  }
22583
22618
  }
22584
- applyObjectsToActiveWorkspace(workspace, desiredObjects) {
22619
+ async applyObjectsToActiveWorkspace(workspace, desiredObjects) {
22585
22620
  const objectsMap = this._store.objects;
22586
22621
  if (!objectsMap) {
22587
22622
  return null;
22588
22623
  }
22589
22624
  this.clearSelection();
22590
- const diff = this.reconcileWorkspaceObjects(objectsMap, workspace.id, desiredObjects, () => this._store.currentZIndex, this._store.state.scale);
22625
+ const diff = await this.reconcileWorkspaceObjects(objectsMap, workspace.id, desiredObjects, () => this._store.currentZIndex, this._store.state.scale);
22591
22626
  this._anchorManager.rebuildIndex();
22592
22627
  this.rerender();
22593
22628
  return diff;
@@ -22606,7 +22641,7 @@ class KritzelCore {
22606
22641
  try {
22607
22642
  await objectsMap.initialize(this, workspace.id, this._syncConfig, { headless: true, workspaceName: workspace.name });
22608
22643
  await objectsMap.whenNetworkSynced();
22609
- return this.reconcileWorkspaceObjects(objectsMap, workspace.id, desiredObjects, () => this.nextZIndexOf(objectsMap), this._store.state.scale);
22644
+ return await this.reconcileWorkspaceObjects(objectsMap, workspace.id, desiredObjects, () => this.nextZIndexOf(objectsMap), this._store.state.scale);
22610
22645
  }
22611
22646
  finally {
22612
22647
  objectsMap.destroy();
@@ -22692,8 +22727,7 @@ class KritzelCore {
22692
22727
  this.removeObject(child);
22693
22728
  });
22694
22729
  }
22695
- // Unmount dynamic objects before removal
22696
- if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
22730
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
22697
22731
  object.unmount();
22698
22732
  }
22699
22733
  // Handle anchor cleanup
@@ -22739,14 +22773,8 @@ class KritzelCore {
22739
22773
  this.removeLocalSelectionGroups();
22740
22774
  this.removeSelectionBox();
22741
22775
  for (const object of selectionGroup.objects ?? []) {
22742
- if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
22743
- const dynamicObject = object;
22744
- if (typeof dynamicObject.setIsInteractive === 'function') {
22745
- dynamicObject.setIsInteractive(true);
22746
- }
22747
- else {
22748
- dynamicObject.isInteractive = true;
22749
- }
22776
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
22777
+ object.setIsInteractive(true);
22750
22778
  }
22751
22779
  }
22752
22780
  this._store.objects.insert(selectionGroup);
@@ -22767,14 +22795,8 @@ class KritzelCore {
22767
22795
  teardownSelectionGroup(selectionGroup) {
22768
22796
  selectionGroup.deselectAllChildren?.();
22769
22797
  for (const object of selectionGroup.objects ?? []) {
22770
- if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
22771
- const dynamicObject = object;
22772
- if (typeof dynamicObject.setIsInteractive === 'function') {
22773
- dynamicObject.setIsInteractive(false);
22774
- }
22775
- else {
22776
- dynamicObject.isInteractive = false;
22777
- }
22798
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
22799
+ object.setIsInteractive(false);
22778
22800
  }
22779
22801
  }
22780
22802
  }
@@ -24220,13 +24242,7 @@ class KritzelWheelHelper {
24220
24242
  return this.canElementConsumeWheel(scrollContainer, ev.deltaX, ev.deltaY);
24221
24243
  }
24222
24244
  static getScrollableContainerFromEventPath(path, dynamicObject) {
24223
- const potentialRoots = [];
24224
- if (dynamicObject.element instanceof HTMLElement) {
24225
- potentialRoots.push(dynamicObject.element);
24226
- }
24227
- if (dynamicObject.elementRef instanceof HTMLElement) {
24228
- potentialRoots.push(dynamicObject.elementRef);
24229
- }
24245
+ const potentialRoots = dynamicObject.getEmbeddedContentRoots();
24230
24246
  const legacyElementRef = dynamicObject.elmeentRef;
24231
24247
  if (legacyElementRef instanceof HTMLElement) {
24232
24248
  potentialRoots.push(legacyElementRef);
@@ -24384,8 +24400,8 @@ class KritzelSvgExportHelper {
24384
24400
  .join('\n ');
24385
24401
  const xmlDecl = includeXmlDeclaration ? '<?xml version="1.0" encoding="UTF-8"?>\n' : '';
24386
24402
  const defsContent = defs.length > 0 ? `\n <defs>\n ${defs.join('\n ')}\n </defs>` : '';
24387
- return `${xmlDecl}<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}" width="${viewBoxWidth}" height="${viewBoxHeight}">${defsContent}
24388
- ${elements}
24403
+ return `${xmlDecl}<svg xmlns="http://www.w3.org/2000/svg" viewBox="${viewBoxX} ${viewBoxY} ${viewBoxWidth} ${viewBoxHeight}" width="${viewBoxWidth}" height="${viewBoxHeight}">${defsContent}
24404
+ ${elements}
24389
24405
  </svg>`;
24390
24406
  }
24391
24407
  /**
@@ -24537,8 +24553,8 @@ class KritzelSvgExportHelper {
24537
24553
  const orient = end === 'start' ? 'auto-start-reverse' : 'auto';
24538
24554
  const refX = 0;
24539
24555
  const refY = size / 2;
24540
- return `<marker id="${markerId}" markerWidth="${size}" markerHeight="${size}" refX="${refX}" refY="${refY}" orient="${orient}" markerUnits="userSpaceOnUse">
24541
- <path d="${path}" fill="${fill}" transform="scale(${size / 10})"/>
24556
+ return `<marker id="${markerId}" markerWidth="${size}" markerHeight="${size}" refX="${refX}" refY="${refY}" orient="${orient}" markerUnits="userSpaceOnUse">
24557
+ <path d="${path}" fill="${fill}" transform="scale(${size / 10})"/>
24542
24558
  </marker>`;
24543
24559
  }
24544
24560
  /**
@@ -24567,6 +24583,9 @@ class KritzelSvgExportHelper {
24567
24583
  if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
24568
24584
  return this.dynamicObjectToSvg(object, rasterSafeForCanvas);
24569
24585
  }
24586
+ if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelIframeObject')) {
24587
+ return this.iframeObjectToSvg(object);
24588
+ }
24570
24589
  if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelGroup')) {
24571
24590
  return this.groupToSvg(object, theme, imageDataUrls, rasterSafeForCanvas);
24572
24591
  }
@@ -24585,10 +24604,10 @@ class KritzelSvgExportHelper {
24585
24604
  const opacity = path.opacity !== 1 ? ` opacity="${path.opacity}"` : '';
24586
24605
  const scale = this.getObjectScale(path);
24587
24606
  // Path needs to be wrapped in a g with translation since path.d uses local coordinates
24588
- return `<g transform="${transform}"${opacity}>
24589
- <svg viewBox="${path.viewBox}" width="${path.totalWidth / scale}" height="${path.totalHeight / scale}" overflow="visible">
24590
- <path d="${path.d}" fill="${fill || 'none'}" stroke="${stroke || 'none'}"${path.strokeWidth ? ` stroke-width="${path.strokeWidth}"` : ''}/>
24591
- </svg>
24607
+ return `<g transform="${transform}"${opacity}>
24608
+ <svg viewBox="${path.viewBox}" width="${path.totalWidth / scale}" height="${path.totalHeight / scale}" overflow="visible">
24609
+ <path d="${path.d}" fill="${fill || 'none'}" stroke="${stroke || 'none'}"${path.strokeWidth ? ` stroke-width="${path.strokeWidth}"` : ''}/>
24610
+ </svg>
24592
24611
  </g>`;
24593
24612
  }
24594
24613
  /**
@@ -24604,10 +24623,10 @@ class KritzelSvgExportHelper {
24604
24623
  const scale = this.getObjectScale(line);
24605
24624
  const markerStart = line.hasStartArrow ? ` marker-start="url(#${line.startMarkerId})"` : '';
24606
24625
  const markerEnd = line.hasEndArrow ? ` marker-end="url(#${line.endMarkerId})"` : '';
24607
- return `<g transform="${transform}"${opacity}>
24608
- <svg viewBox="${line.viewBox}" width="${line.totalWidth / scale}" height="${line.totalHeight / scale}" overflow="visible">
24609
- <path d="${line.d}" fill="none" stroke="${stroke}" stroke-width="${line.strokeWidth}" stroke-linecap="round"${markerStart}${markerEnd}/>
24610
- </svg>
24626
+ return `<g transform="${transform}"${opacity}>
24627
+ <svg viewBox="${line.viewBox}" width="${line.totalWidth / scale}" height="${line.totalHeight / scale}" overflow="visible">
24628
+ <path d="${line.d}" fill="none" stroke="${stroke}" stroke-width="${line.strokeWidth}" stroke-linecap="round"${markerStart}${markerEnd}/>
24629
+ </svg>
24611
24630
  </g>`;
24612
24631
  }
24613
24632
  /**
@@ -24631,18 +24650,18 @@ class KritzelSvgExportHelper {
24631
24650
  const textHtml = this.prosemirrorToHtml(shape.content, shape, theme);
24632
24651
  if (textHtml) {
24633
24652
  const fontColor = engine_constants.KritzelColorHelper.resolveThemeColor(shape.fontColor, theme);
24634
- textContent = `
24635
- <foreignObject x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}">
24636
- <div xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; text-align: center; font-family: ${shape.fontFamily}; font-size: ${shape.fontSize}px; color: ${fontColor}; overflow: hidden;">
24637
- ${textHtml}
24638
- </div>
24653
+ textContent = `
24654
+ <foreignObject x="${shape.x}" y="${shape.y}" width="${shape.width}" height="${shape.height}">
24655
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; text-align: center; font-family: ${shape.fontFamily}; font-size: ${shape.fontSize}px; color: ${fontColor}; overflow: hidden;">
24656
+ ${textHtml}
24657
+ </div>
24639
24658
  </foreignObject>`;
24640
24659
  }
24641
24660
  }
24642
- return `<g transform="${transform}"${opacity}>
24643
- <svg viewBox="${shape.viewBox}" width="${shape.totalWidth / scale}" height="${shape.totalHeight / scale}" overflow="visible" preserveAspectRatio="none">
24644
- <path d="${pathD}" fill="${fill || 'transparent'}" stroke="${stroke}" stroke-width="${shape.strokeWidth}"/>${textContent}
24645
- </svg>
24661
+ return `<g transform="${transform}"${opacity}>
24662
+ <svg viewBox="${shape.viewBox}" width="${shape.totalWidth / scale}" height="${shape.totalHeight / scale}" overflow="visible" preserveAspectRatio="none">
24663
+ <path d="${pathD}" fill="${fill || 'transparent'}" stroke="${stroke}" stroke-width="${shape.strokeWidth}"/>${textContent}
24664
+ </svg>
24646
24665
  </g>`;
24647
24666
  }
24648
24667
  /**
@@ -24673,10 +24692,10 @@ class KritzelSvgExportHelper {
24673
24692
  // adjusted font-size already accounts for it – keeping both would double-scale
24674
24693
  // the content and overflow the foreignObject.
24675
24694
  const svgFontSizePx = (text.fontSize * (4 / 3) * (text.scaleFactor || 1) / scale).toFixed(4);
24676
- return `<g transform="${transform}"${opacity}>
24677
- <foreignObject x="0" y="0" width="${width}" height="${height}" overflow="visible">
24678
- <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${svgFontSizePx}px; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; white-space: pre-wrap; word-wrap: break-word; width: 100%; margin: 0; padding: 0; box-sizing: border-box;">${htmlContent.trim()}</div>
24679
- </foreignObject>
24695
+ return `<g transform="${transform}"${opacity}>
24696
+ <foreignObject x="0" y="0" width="${width}" height="${height}" overflow="visible">
24697
+ <div xmlns="http://www.w3.org/1999/xhtml" style="font-family: ${text.fontFamily}; font-size: ${svgFontSizePx}px; color: ${fontColor}; background-color: ${bgColor || 'transparent'}; white-space: pre-wrap; word-wrap: break-word; width: 100%; margin: 0; padding: 0; box-sizing: border-box;">${htmlContent.trim()}</div>
24698
+ </foreignObject>
24680
24699
  </g>`;
24681
24700
  }
24682
24701
  /**
@@ -24710,9 +24729,9 @@ class KritzelSvgExportHelper {
24710
24729
  return `<tspan x="0" y="${y}" xml:space="preserve">${this.escapeHtml(line)}</tspan>`;
24711
24730
  })
24712
24731
  .join('');
24713
- return `<g transform="${transform}"${opacity}>
24714
- ${bgRect}
24715
- <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSizePx}" fill="${fontColor}">${tspans}</text>
24732
+ return `<g transform="${transform}"${opacity}>
24733
+ ${bgRect}
24734
+ <text font-family="${this.escapeHtml(text.fontFamily)}" font-size="${resolvedFontSizePx}" fill="${fontColor}">${tspans}</text>
24716
24735
  </g>`;
24717
24736
  }
24718
24737
  /**
@@ -24740,8 +24759,8 @@ class KritzelSvgExportHelper {
24740
24759
  const width = image.totalWidth / scale;
24741
24760
  const height = image.totalHeight / scale;
24742
24761
  const href = imageDataUrls?.get(image.id) || image.resolvedSrc || image.src || '';
24743
- return `<g transform="${transform}"${opacity}>
24744
- <image href="${href}" x="0" y="0" width="${width}" height="${height}" preserveAspectRatio="xMidYMid meet"/>
24762
+ return `<g transform="${transform}"${opacity}>
24763
+ <image href="${href}" x="0" y="0" width="${width}" height="${height}" preserveAspectRatio="xMidYMid meet"/>
24745
24764
  </g>`;
24746
24765
  }
24747
24766
  /**
@@ -24761,19 +24780,30 @@ class KritzelSvgExportHelper {
24761
24780
  if (rasterSafeForCanvas) {
24762
24781
  // PNG export: render placeholder rect (foreignObject doesn't rasterize to PNG well)
24763
24782
  console.warn('[KritzelDynamicObject SVG Export] Dynamic objects do not serialize well to PNG. Use SVG export or screenshot instead.');
24764
- return `<g transform="${transform}"${opacity}>
24765
- <rect x="0" y="0" width="${width}" height="${height}" fill="#f0f0f0" stroke="#999" stroke-width="1" />
24766
- <text x="${width / 2}" y="${height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999">[Dynamic Object]</text>
24783
+ return `<g transform="${transform}"${opacity}>
24784
+ <rect x="0" y="0" width="${width}" height="${height}" fill="#f0f0f0" stroke="#999" stroke-width="1" />
24785
+ <text x="${width / 2}" y="${height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999">[Dynamic Object]</text>
24767
24786
  </g>`;
24768
24787
  }
24769
24788
  // SVG export: use foreignObject to embed HTML
24770
24789
  const html = obj.element?.outerHTML || '<div style="border:1px dashed #ccc">Empty</div>';
24771
- return `<g transform="${transform}"${opacity}>
24772
- <foreignObject x="0" y="0" width="${width}" height="${height}">
24773
- <div xmlns="http://www.w3.org/1999/xhtml" style="width:100%;height:100%;overflow:hidden;">
24774
- ${html}
24775
- </div>
24776
- </foreignObject>
24790
+ return `<g transform="${transform}"${opacity}>
24791
+ <foreignObject x="0" y="0" width="${width}" height="${height}">
24792
+ <div xmlns="http://www.w3.org/1999/xhtml" style="width:100%;height:100%;overflow:hidden;">
24793
+ ${html}
24794
+ </div>
24795
+ </foreignObject>
24796
+ </g>`;
24797
+ }
24798
+ static iframeObjectToSvg(object) {
24799
+ const transform = this.buildTransform(object);
24800
+ const opacity = object.opacity !== 1 ? ` opacity="${object.opacity}"` : '';
24801
+ const scale = this.getObjectScale(object);
24802
+ const width = object.totalWidth / scale;
24803
+ const height = object.totalHeight / scale;
24804
+ return `<g transform="${transform}"${opacity}>
24805
+ <rect x="0" y="0" width="${width}" height="${height}" fill="#f0f0f0" stroke="#999" stroke-width="1" />
24806
+ <text x="${width / 2}" y="${height / 2}" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#999">[Iframe]</text>
24777
24807
  </g>`;
24778
24808
  }
24779
24809
  /**
@@ -24793,8 +24823,8 @@ class KritzelSvgExportHelper {
24793
24823
  return '';
24794
24824
  }
24795
24825
  const opacity = group.opacity !== 1 ? ` opacity="${group.opacity}"` : '';
24796
- return `<g${opacity}>
24797
- ${children}
24826
+ return `<g${opacity}>
24827
+ ${children}
24798
24828
  </g>`;
24799
24829
  }
24800
24830
  /**
@@ -24979,12 +25009,26 @@ const KritzelEngine = class {
24979
25009
  /** Authoritative workspace catalog for this engine instance. When provided, provider-only workspaces are hidden but not deleted. */
24980
25010
  workspaces;
24981
25011
  async onWorkspacesPropChange() {
24982
- if (!this._isYjsInitialized) {
25012
+ this._workspacesRevision++;
25013
+ if (!this._isYjsInitialized || this._syncInitPromise) {
24983
25014
  return;
24984
25015
  }
24985
- await this.reconcileProvidedWorkspaces();
24986
- await this.ensureActiveWorkspaceIsVisible();
24987
- this.workspacesChange.emit(this.getVisibleWorkspaces());
25016
+ const previousReconciliation = this._workspacePropReconciliationPromise;
25017
+ const reconciliationPromise = (async () => {
25018
+ await previousReconciliation;
25019
+ await this.reconcileProvidedWorkspaces();
25020
+ await this.ensureActiveWorkspaceIsVisible();
25021
+ this.workspacesChange.emit(this.getVisibleWorkspaces());
25022
+ })();
25023
+ this._workspacePropReconciliationPromise = reconciliationPromise;
25024
+ try {
25025
+ await reconciliationPromise;
25026
+ }
25027
+ finally {
25028
+ if (this._workspacePropReconciliationPromise === reconciliationPromise) {
25029
+ this._workspacePropReconciliationPromise = null;
25030
+ }
25031
+ }
24988
25032
  }
24989
25033
  /** Optional unique identifier for namespacing storage keys across multiple editor instances. */
24990
25034
  editorId;
@@ -25038,6 +25082,12 @@ const KritzelEngine = class {
25038
25082
  onUserChange(newValue) {
25039
25083
  this.core.setUser(newValue);
25040
25084
  }
25085
+ /** Tool definitions to register with this engine instance. */
25086
+ tools = [];
25087
+ onToolsChange() {
25088
+ this._toolsRevision++;
25089
+ void this.registerConfiguredTools();
25090
+ }
25041
25091
  /** Context menu items shown when right-clicking the canvas background. */
25042
25092
  globalContextMenuItems;
25043
25093
  onGlobalContextMenuItemsChange(newValue) {
@@ -25228,7 +25278,7 @@ const KritzelEngine = class {
25228
25278
  }
25229
25279
  const hoveredObject = this.core.getObjectFromPointerEvent(ev);
25230
25280
  if (hoveredObject &&
25231
- engine_constants.ObjectHelper.isClass(hoveredObject, 'KritzelDynamicObject') &&
25281
+ engine_constants.ObjectHelper.isKritzelEmbeddedObject(hoveredObject) &&
25232
25282
  KritzelWheelHelper.shouldKeepWheelInDynamicObject(ev, hoveredObject)) {
25233
25283
  return;
25234
25284
  }
@@ -25330,7 +25380,7 @@ const KritzelEngine = class {
25330
25380
  return false;
25331
25381
  }
25332
25382
  const object = this.core.store.objects.findById(objectElement.id);
25333
- if (!object || !engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
25383
+ if (!object || !engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
25334
25384
  return false;
25335
25385
  }
25336
25386
  const isSelected = !!this.core.store.selectionGroup?.objects?.some(selectedObject => selectedObject.id === object.id);
@@ -25340,13 +25390,7 @@ const KritzelEngine = class {
25340
25390
  if (object.isInteractive !== true) {
25341
25391
  return false;
25342
25392
  }
25343
- const potentialRoots = [];
25344
- if (object.element instanceof HTMLElement) {
25345
- potentialRoots.push(object.element);
25346
- }
25347
- if (object.elementRef instanceof HTMLElement) {
25348
- potentialRoots.push(object.elementRef);
25349
- }
25393
+ const potentialRoots = object.getEmbeddedContentRoots();
25350
25394
  if (potentialRoots.length === 0) {
25351
25395
  return false;
25352
25396
  }
@@ -25520,7 +25564,7 @@ const KritzelEngine = class {
25520
25564
  * Switches the active drawing tool by its registered name.
25521
25565
  * @param toolName - The name the tool was registered with (e.g., 'brush', 'eraser').
25522
25566
  */
25523
- async changeActiveTool(toolName) {
25567
+ async setActiveTool(toolName) {
25524
25568
  const tool = this.core.toolRegistry.getTool(toolName);
25525
25569
  if (!tool) {
25526
25570
  console.warn(`[KritzelEngine] No tool registered with name: ${toolName}`);
@@ -26826,9 +26870,13 @@ const KritzelEngine = class {
26826
26870
  _stateChangeListenersRegistered = false;
26827
26871
  _workspaceInitializationPromise = null;
26828
26872
  _workspaceInitializationTargetKey = null;
26873
+ _workspacePropReconciliationPromise = null;
26829
26874
  _syncInitPromise = null;
26875
+ _toolsInitPromise = null;
26876
+ _toolsRevision = 0;
26830
26877
  _syncConfigRevision = 0;
26831
26878
  _assetStorageConfigRevision = 0;
26879
+ _workspacesRevision = 0;
26832
26880
  _hasLoggedUnlicensedWelcomeBanner = false;
26833
26881
  _previousVisibleDynamicObjects = new Set();
26834
26882
  _revealedImageKeys = new Set();
@@ -26841,6 +26889,37 @@ const KritzelEngine = class {
26841
26889
  undoStackSize: 0,
26842
26890
  redoStackSize: 0,
26843
26891
  };
26892
+ /** Returns a tool registered in this engine instance. */
26893
+ async getTool(toolName) {
26894
+ await this.registerConfiguredTools();
26895
+ return this.core.toolRegistry.getTool(toolName);
26896
+ }
26897
+ async registerConfiguredTools() {
26898
+ if (this._toolsInitPromise) {
26899
+ return this._toolsInitPromise;
26900
+ }
26901
+ this._toolsInitPromise = (async () => {
26902
+ do {
26903
+ const revision = this._toolsRevision;
26904
+ for (const definition of this.tools) {
26905
+ let toolClass = definition.tool;
26906
+ if (typeof toolClass !== 'function') {
26907
+ toolClass = toolClass.constructor;
26908
+ }
26909
+ await this.registerTool(definition.name, toolClass, definition.config);
26910
+ }
26911
+ if (revision === this._toolsRevision) {
26912
+ break;
26913
+ }
26914
+ } while (true);
26915
+ })();
26916
+ try {
26917
+ await this._toolsInitPromise;
26918
+ }
26919
+ finally {
26920
+ this._toolsInitPromise = null;
26921
+ }
26922
+ }
26844
26923
  setLoadingState(isLoading) {
26845
26924
  if (this.core.store.state.isLoading === isLoading) {
26846
26925
  return;
@@ -26968,6 +27047,8 @@ const KritzelEngine = class {
26968
27047
  this.core.licenseManager.destroy();
26969
27048
  }
26970
27049
  componentWillLoad() {
27050
+ this._toolsRevision++;
27051
+ void this.registerConfiguredTools();
26971
27052
  this.core.setEditorId(this.editorId);
26972
27053
  if (this.themes && this.themes.length > 0) {
26973
27054
  this.core.themeManager.registerThemes(this.themes);
@@ -27007,7 +27088,9 @@ const KritzelEngine = class {
27007
27088
  const editorElement = this.host.closest('kritzel-editor');
27008
27089
  this.core.themeManager.setTargetElement(editorElement || this.host);
27009
27090
  // Initialize sync config and Yjs
27091
+ await this.registerConfiguredTools();
27010
27092
  await this.initializeSyncAndWorkspace();
27093
+ await this.waitForWorkspaceAndSceneReady();
27011
27094
  if (this.core.store.state.isReady === false) {
27012
27095
  this.core.store.state.isReady = true;
27013
27096
  this.logUnlicensedWelcomeBanner();
@@ -27029,6 +27112,20 @@ const KritzelEngine = class {
27029
27112
  this._syncInitPromise = null;
27030
27113
  }
27031
27114
  }
27115
+ async waitForSceneRender() {
27116
+ await new Promise(resolve => {
27117
+ requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
27118
+ });
27119
+ }
27120
+ async waitForWorkspaceAndSceneReady() {
27121
+ let observedRevision;
27122
+ do {
27123
+ observedRevision = this._workspacesRevision;
27124
+ await this._workspacePropReconciliationPromise;
27125
+ await this.waitForSceneRender();
27126
+ } while (observedRevision !== this._workspacesRevision ||
27127
+ this._workspacePropReconciliationPromise);
27128
+ }
27032
27129
  /**
27033
27130
  * Initializes the asset storage layer with the latest config. Mirrors
27034
27131
  * the in-flight replay logic of {@link doInitializeSyncAndWorkspace} for
@@ -27411,7 +27508,7 @@ const KritzelEngine = class {
27411
27508
  if (!engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup')) {
27412
27509
  return 'auto';
27413
27510
  }
27414
- if (object.objects.length === 1 && engine_constants.KritzelClassHelper.isInstanceOf(object.objects[0], 'KritzelDynamicObject')) {
27511
+ if (object.objects.length === 1 && engine_constants.ObjectHelper.isKritzelEmbeddedObject(object.objects[0])) {
27415
27512
  return 'none';
27416
27513
  }
27417
27514
  return 'auto';
@@ -27419,7 +27516,7 @@ const KritzelEngine = class {
27419
27516
  // Track visible dynamic objects and unmount culled ones
27420
27517
  const currentVisibleDynamicObjects = new Set();
27421
27518
  visibleObjects.forEach(object => {
27422
- if (engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
27519
+ if (engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
27423
27520
  currentVisibleDynamicObjects.add(object.id);
27424
27521
  }
27425
27522
  });
@@ -27427,7 +27524,7 @@ const KritzelEngine = class {
27427
27524
  this._previousVisibleDynamicObjects.forEach(id => {
27428
27525
  if (!currentVisibleDynamicObjects.has(id)) {
27429
27526
  const object = this.core.store.objects.findById(id);
27430
- if (object && engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject')) {
27527
+ if (object && engine_constants.ObjectHelper.isKritzelEmbeddedObject(object)) {
27431
27528
  object.unmount();
27432
27529
  }
27433
27530
  }
@@ -27520,7 +27617,7 @@ const KritzelEngine = class {
27520
27617
  display: 'flex',
27521
27618
  alignItems: 'center',
27522
27619
  justifyContent: 'center',
27523
- } }, index.h("kritzel-icon", { name: object.loadState === 'error' ? 'image-off' : 'image', size: Math.max(16, Math.min(object.totalWidth, object.totalHeight) * 0.3), style: {
27620
+ } }, index.h("kritzel-icon", { name: object.loadState === 'error' ? 'imageOff' : 'image', size: Math.max(16, Math.min(object.totalWidth, object.totalHeight) * 0.3), style: {
27524
27621
  color: engine_constants.KritzelColorHelper.resolveThemeColor({ light: '#9ca3af', dark: '#6b7280' }, currentTheme),
27525
27622
  } }))), engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelDynamicObject') && (index.h("div", { ref: el => el && object.mount(el), style: {
27526
27623
  position: 'absolute',
@@ -27539,7 +27636,37 @@ const KritzelEngine = class {
27539
27636
  padding: object.padding + 'px',
27540
27637
  overflow: 'visible',
27541
27638
  display: 'block',
27542
- } })), engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && !this.core.displaySelectionLineUI(object) && (index.h("div", { ref: el => el && object.mount(el), style: {
27639
+ } })), engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelIframeObject') && (index.h("div", { style: {
27640
+ position: 'absolute',
27641
+ left: '0',
27642
+ top: '0',
27643
+ width: object.totalWidth + 'px',
27644
+ height: object.totalHeight + 'px',
27645
+ transform: object.rotationDegrees !== 0 ? `rotate(${object.rotationDegrees}deg)` : undefined,
27646
+ transformOrigin: object.rotationDegrees !== 0 ? `${object.totalWidth / 2}px ${object.totalHeight / 2}px` : undefined,
27647
+ opacity: effectiveOpacityString,
27648
+ backgroundColor: engine_constants.KritzelColorHelper.resolveThemeColor(object.backgroundColor, currentTheme),
27649
+ borderColor: engine_constants.KritzelColorHelper.resolveThemeColor(object.borderColor, currentTheme),
27650
+ borderWidth: object.borderWidth + 'px',
27651
+ borderStyle: 'solid',
27652
+ borderRadius: object.borderRadius !== undefined ? (typeof object.borderRadius === 'number' ? `${object.borderRadius}px` : object.borderRadius) : undefined,
27653
+ boxShadow: object.boxShadow,
27654
+ overflow: 'hidden',
27655
+ display: 'block',
27656
+ pointerEvents: object.markedForRemoval ? 'none' : 'auto',
27657
+ } }, index.h("iframe", { ref: element => element && object.mount(element), title: object.title, sandbox: object.sandboxAttribute, style: {
27658
+ display: 'block',
27659
+ width: '100%',
27660
+ height: '100%',
27661
+ border: '0',
27662
+ borderRadius: object.borderRadius !== undefined ? (typeof object.borderRadius === 'number' ? `${object.borderRadius}px` : object.borderRadius) : undefined,
27663
+ pointerEvents: object.isInteractive ? 'auto' : 'none',
27664
+ } }), index.h("div", { style: {
27665
+ position: 'absolute',
27666
+ inset: '0',
27667
+ borderRadius: object.borderRadius !== undefined ? (typeof object.borderRadius === 'number' ? `${object.borderRadius}px` : object.borderRadius) : undefined,
27668
+ pointerEvents: object.isInteractive ? 'none' : 'auto',
27669
+ } }))), engine_constants.KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && !this.core.displaySelectionLineUI(object) && (index.h("div", { ref: el => el && object.mount(el), style: {
27543
27670
  position: 'absolute',
27544
27671
  left: '0',
27545
27672
  top: '0',
@@ -27897,6 +28024,9 @@ const KritzelEngine = class {
27897
28024
  "user": [{
27898
28025
  "onUserChange": 0
27899
28026
  }],
28027
+ "tools": [{
28028
+ "onToolsChange": 0
28029
+ }],
27900
28030
  "globalContextMenuItems": [{
27901
28031
  "onGlobalContextMenuItemsChange": 0
27902
28032
  }],
@@ -27961,6 +28091,20 @@ const KritzelIcon = class {
27961
28091
  name;
27962
28092
  label;
27963
28093
  size = 24;
28094
+ unsubscribe;
28095
+ connectedCallback() {
28096
+ this.unsubscribe?.();
28097
+ this.unsubscribe = engine_constants.KritzelIconRegistry.subscribe(name => {
28098
+ if (name === this.name) {
28099
+ index.forceUpdate(this);
28100
+ }
28101
+ });
28102
+ index.forceUpdate(this);
28103
+ }
28104
+ disconnectedCallback() {
28105
+ this.unsubscribe?.();
28106
+ this.unsubscribe = undefined;
28107
+ }
27964
28108
  render() {
27965
28109
  const svgContent = engine_constants.KritzelIconRegistry.get(this.name);
27966
28110
  if (!svgContent) {
@@ -27976,7 +28120,7 @@ const KritzelIcon = class {
27976
28120
  };
27977
28121
  KritzelIcon.style = kritzelIconCss();
27978
28122
 
27979
- const kritzelWatermarkCss = () => `:host{position:absolute;bottom:var(--kritzel-watermark-offset-bottom, 12px);right:var(--kritzel-watermark-offset-right, 12px);z-index:10000;pointer-events:none}.watermark-link{pointer-events:auto;display:inline-flex;align-items:center;padding:4px 8px;border-radius:var(--kritzel-watermark-border-radius, 6px);font-family:var(--kritzel-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);font-size:var(--kritzel-watermark-font-size, 11px);line-height:1;text-decoration:none;color:var(--kritzel-watermark-color, rgba(60, 60, 60, 0.75));background-color:var(--kritzel-watermark-background, rgba(255, 255, 255, 0.6));box-shadow:var(--kritzel-watermark-box-shadow, 0 1px 2px rgba(0, 0, 0, 0.12));opacity:var(--kritzel-watermark-opacity, 0.85);transition:opacity 150ms ease;user-select:none}.watermark-link:hover{opacity:1}`;
28123
+ const kritzelWatermarkCss = () => `:host{position:absolute;bottom:var(--kritzel-watermark-offset-bottom, 12px);right:var(--kritzel-watermark-offset-right, 12px);z-index:10000;pointer-events:none}.watermark-link{pointer-events:auto;display:inline-flex;align-items:center;padding:4px 8px;border-radius:var(--kritzel-watermark-border-radius, 6px);font-family:var(--kritzel-global-font-family, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif);font-size:var(--kritzel-watermark-font-size, 11px);line-height:1;text-decoration:none;color:var(--kritzel-watermark-color, rgba(60, 60, 60, 0.75));background-color:var(--kritzel-watermark-background, rgba(255, 255, 255, 0.6));box-shadow:var(--kritzel-watermark-box-shadow, 0 1px 2px rgba(0, 0, 0, 0.12));opacity:var(--kritzel-watermark-opacity, 0.85);transition:opacity 150ms ease;user-select:none}.watermark-link:hover{opacity:1}`;
27980
28124
 
27981
28125
  const KritzelWatermark = class {
27982
28126
  constructor(hostRef) {
@@ -27992,7 +28136,7 @@ const KritzelWatermark = class {
27992
28136
  label;
27993
28137
  render() {
27994
28138
  const label = this.label ?? this.core.localizationManager.translate('watermark.poweredBy');
27995
- return (index.h(index.Host, { key: '2bacbc19d9469c7fcaf5d6b1b3133ec14d3ff864' }, index.h("a", { key: '2de44e340afd092ed388b7575350af1ee9155d58', class: "watermark-link", href: engine_constants.KRITZEL_WEBSITE_URL, target: "_blank", rel: "noopener noreferrer", part: "watermark", onPointerDown: (ev) => ev.stopPropagation(), onPointerUp: (ev) => ev.stopPropagation() }, label)));
28139
+ return (index.h(index.Host, { key: '5bc76650ecd1f82f5ed283d4433b0e5fc8937cab' }, index.h("a", { key: 'b0828f36df618e461794772d2e8f55c0a8cb357e', class: "watermark-link", href: engine_constants.KRITZEL_WEBSITE_URL, target: "_blank", rel: "noopener noreferrer", part: "watermark", onPointerDown: (ev) => ev.stopPropagation(), onPointerUp: (ev) => ev.stopPropagation() }, label)));
27996
28140
  }
27997
28141
  };
27998
28142
  KritzelWatermark.style = kritzelWatermarkCss();