kritzel-stencil 0.0.162 → 0.0.163

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 (118) hide show
  1. package/dist/cjs/{default-line-tool.config-D1Ns0NmM.js → default-line-tool.config-DJMYrkSu.js} +340 -9
  2. package/dist/cjs/default-line-tool.config-DJMYrkSu.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +1 -1
  4. package/dist/cjs/kritzel-color_22.cjs.entry.js +761 -28
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/stencil.cjs.js +1 -1
  7. package/dist/collection/classes/core/core.class.js +19 -3
  8. package/dist/collection/classes/core/core.class.js.map +1 -1
  9. package/dist/collection/classes/core/reviver.class.js +16 -0
  10. package/dist/collection/classes/core/reviver.class.js.map +1 -1
  11. package/dist/collection/classes/core/store.class.js +5 -0
  12. package/dist/collection/classes/core/store.class.js.map +1 -1
  13. package/dist/collection/classes/core/viewport.class.js +8 -0
  14. package/dist/collection/classes/core/viewport.class.js.map +1 -1
  15. package/dist/collection/classes/managers/anchor.manager.js +181 -3
  16. package/dist/collection/classes/managers/anchor.manager.js.map +1 -1
  17. package/dist/collection/classes/objects/path.class.js +85 -0
  18. package/dist/collection/classes/objects/path.class.js.map +1 -1
  19. package/dist/collection/classes/objects/shape.class.js +372 -0
  20. package/dist/collection/classes/objects/shape.class.js.map +1 -0
  21. package/dist/collection/classes/registries/icon-registry.class.js +4 -0
  22. package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
  23. package/dist/collection/classes/tools/brush-tool.class.js +2 -2
  24. package/dist/collection/classes/tools/brush-tool.class.js.map +1 -1
  25. package/dist/collection/classes/tools/line-tool.class.js +2 -2
  26. package/dist/collection/classes/tools/line-tool.class.js.map +1 -1
  27. package/dist/collection/classes/tools/shape-tool.class.js +204 -0
  28. package/dist/collection/classes/tools/shape-tool.class.js.map +1 -0
  29. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +17 -6
  30. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  31. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +44 -8
  32. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  33. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +143 -5
  34. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +76 -12
  35. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js.map +1 -1
  36. package/dist/collection/configs/default-engine-config.js +1 -1
  37. package/dist/collection/configs/default-engine-config.js.map +1 -1
  38. package/dist/collection/configs/default-shape-tool.config.js +26 -0
  39. package/dist/collection/configs/default-shape-tool.config.js.map +1 -0
  40. package/dist/collection/enums/shape-type.enum.js +7 -0
  41. package/dist/collection/enums/shape-type.enum.js.map +1 -0
  42. package/dist/collection/helpers/geometry.helper.js +55 -0
  43. package/dist/collection/helpers/geometry.helper.js.map +1 -1
  44. package/dist/collection/interfaces/toolbar-control.interface.js.map +1 -1
  45. package/dist/components/index.js +3 -3
  46. package/dist/components/kritzel-brush-style.js +1 -1
  47. package/dist/components/kritzel-context-menu.js +1 -1
  48. package/dist/components/kritzel-control-brush-config.js +1 -1
  49. package/dist/components/kritzel-control-text-config.js +1 -1
  50. package/dist/components/kritzel-controls.js +1 -1
  51. package/dist/components/kritzel-editor.js +49 -16
  52. package/dist/components/kritzel-editor.js.map +1 -1
  53. package/dist/components/kritzel-engine.js +1 -1
  54. package/dist/components/kritzel-icon.js +1 -1
  55. package/dist/components/kritzel-menu-item.js +1 -1
  56. package/dist/components/kritzel-menu.js +1 -1
  57. package/dist/components/kritzel-split-button.js +1 -1
  58. package/dist/components/kritzel-utility-panel.js +1 -1
  59. package/dist/components/kritzel-workspace-manager.js +1 -1
  60. package/dist/components/{p-Cqr0Bah5.js → p-5OECjGHq.js} +3 -3
  61. package/dist/components/{p-Cqr0Bah5.js.map → p-5OECjGHq.js.map} +1 -1
  62. package/dist/components/{p-CvLFRlQU.js → p-BSBMBjhq.js} +3 -3
  63. package/dist/components/{p-CvLFRlQU.js.map → p-BSBMBjhq.js.map} +1 -1
  64. package/dist/components/{p-7_lwv0zQ.js → p-BuS7MM1j.js} +4 -4
  65. package/dist/components/{p-7_lwv0zQ.js.map → p-BuS7MM1j.js.map} +1 -1
  66. package/dist/components/{p-CuhOrcET.js → p-CnRfQsIC.js} +838 -22
  67. package/dist/components/p-CnRfQsIC.js.map +1 -0
  68. package/dist/components/{p-BixlbUD7.js → p-Cv4BGNPb.js} +6 -2
  69. package/dist/components/p-Cv4BGNPb.js.map +1 -0
  70. package/dist/components/{p-dMCB4tJA.js → p-D1YAsWrL.js} +3 -3
  71. package/dist/components/{p-dMCB4tJA.js.map → p-D1YAsWrL.js.map} +1 -1
  72. package/dist/components/{p-CDteBYm9.js → p-D8L0t-Ro.js} +3 -3
  73. package/dist/components/{p-CDteBYm9.js.map → p-D8L0t-Ro.js.map} +1 -1
  74. package/dist/components/{p-DZ7kxJUx.js → p-DguzZn_x.js} +3 -3
  75. package/dist/components/{p-DZ7kxJUx.js.map → p-DguzZn_x.js.map} +1 -1
  76. package/dist/components/{p-sokRZ7Vn.js → p-Dz2XHHqa.js} +145 -5
  77. package/dist/components/p-Dz2XHHqa.js.map +1 -0
  78. package/dist/components/{p-CkD1PQQX.js → p-I3iPEDpx.js} +5 -5
  79. package/dist/components/{p-CkD1PQQX.js.map → p-I3iPEDpx.js.map} +1 -1
  80. package/dist/components/{p-DKwJJuFb.js → p-tp96UZ0l.js} +83 -19
  81. package/dist/components/p-tp96UZ0l.js.map +1 -0
  82. package/dist/esm/{default-line-tool.config-C35m-d1Y.js → default-line-tool.config-C35P3XfD.js} +332 -10
  83. package/dist/esm/default-line-tool.config-C35P3XfD.js.map +1 -0
  84. package/dist/esm/index.js +2 -2
  85. package/dist/esm/kritzel-color_22.entry.js +761 -28
  86. package/dist/esm/loader.js +1 -1
  87. package/dist/esm/stencil.js +1 -1
  88. package/dist/stencil/index.esm.js +1 -1
  89. package/dist/stencil/p-9d43b708.entry.js +10 -0
  90. package/dist/stencil/p-9d43b708.entry.js.map +1 -0
  91. package/dist/stencil/p-C35P3XfD.js +2 -0
  92. package/dist/stencil/p-C35P3XfD.js.map +1 -0
  93. package/dist/stencil/stencil.esm.js +1 -1
  94. package/dist/types/classes/core/core.class.d.ts +1 -0
  95. package/dist/types/classes/core/store.class.d.ts +2 -0
  96. package/dist/types/classes/core/viewport.class.d.ts +6 -0
  97. package/dist/types/classes/managers/anchor.manager.d.ts +20 -0
  98. package/dist/types/classes/objects/path.class.d.ts +7 -0
  99. package/dist/types/classes/objects/shape.class.d.ts +116 -0
  100. package/dist/types/classes/tools/shape-tool.class.d.ts +37 -0
  101. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -2
  102. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +16 -1
  103. package/dist/types/components.d.ts +5 -5
  104. package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
  105. package/dist/types/enums/shape-type.enum.d.ts +5 -0
  106. package/dist/types/helpers/geometry.helper.d.ts +21 -0
  107. package/dist/types/interfaces/toolbar-control.interface.d.ts +21 -3
  108. package/package.json +1 -1
  109. package/dist/cjs/default-line-tool.config-D1Ns0NmM.js.map +0 -1
  110. package/dist/components/p-BixlbUD7.js.map +0 -1
  111. package/dist/components/p-CuhOrcET.js.map +0 -1
  112. package/dist/components/p-DKwJJuFb.js.map +0 -1
  113. package/dist/components/p-sokRZ7Vn.js.map +0 -1
  114. package/dist/esm/default-line-tool.config-C35m-d1Y.js.map +0 -1
  115. package/dist/stencil/p-C35m-d1Y.js +0 -2
  116. package/dist/stencil/p-C35m-d1Y.js.map +0 -1
  117. package/dist/stencil/p-d142ef46.entry.js +0 -10
  118. package/dist/stencil/p-d142ef46.entry.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { r as registerInstance, h, H as Host, c as createEvent, g as getElement } from './index-SGde3HXB.js';
2
- import { Y as KritzelDevicesHelper, I as KritzelBrushTool, N as KritzelTextTool, Z as KritzelMouseButton, Q as KritzelSelectionTool, V as DEFAULT_BRUSH_CONFIG, X as DEFAULT_LINE_TOOL_CONFIG, J as KritzelLineTool, L as KritzelEraserTool, W as DEFAULT_TEXT_CONFIG, M as KritzelImageTool, T as KritzelWorkspace, _ as KritzelIconRegistry, $ as KritzelKeyboardHelper, a0 as KritzelBaseHandler, a1 as KritzelToolRegistry, a2 as KritzelSelectionBox, a3 as KritzelSelectionGroup, a4 as KritzelBaseObject, F as KritzelImage, K as KritzelText, G as KritzelLine, E as KritzelPath, a5 as Doc, a6 as DEFAULT_SYNC_CONFIG, a7 as UndoManager, P as KritzelCursorHelper, S as KritzelAppStateMap, U as KritzelAnchorManager, a8 as ObjectHelper, a9 as KritzelEventHelper, aa as KritzelBaseTool, ab as KritzelClassHelper } from './default-line-tool.config-C35m-d1Y.js';
2
+ import { Y as KritzelDevicesHelper, I as KritzelBrushTool, N as KritzelTextTool, Z as KritzelMouseButton, _ as KritzelBaseObject, $ as Schema, a0 as schema, a1 as addListNodes, a2 as EditorView, a3 as EditorState, a4 as keymap, a5 as TextSelection, a6 as KritzelKeyboardHelper, a7 as KritzelToolRegistry, a8 as KritzelGeometryHelper, a9 as baseKeymap, aa as KritzelBaseTool, ab as KritzelEventHelper, Q as KritzelSelectionTool, V as DEFAULT_BRUSH_CONFIG, X as DEFAULT_LINE_TOOL_CONFIG, J as KritzelLineTool, L as KritzelEraserTool, W as DEFAULT_TEXT_CONFIG, M as KritzelImageTool, T as KritzelWorkspace, ac as KritzelIconRegistry, ad as KritzelBaseHandler, ae as KritzelSelectionBox, af as KritzelSelectionGroup, F as KritzelImage, K as KritzelText, G as KritzelLine, E as KritzelPath, ag as Doc, ah as DEFAULT_SYNC_CONFIG, ai as UndoManager, P as KritzelCursorHelper, S as KritzelAppStateMap, U as KritzelAnchorManager, aj as ObjectHelper, ak as KritzelClassHelper } from './default-line-tool.config-C35P3XfD.js';
3
3
 
4
4
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
5
5
 
@@ -247,7 +247,7 @@ const KritzelControlTextConfig = class {
247
247
  };
248
248
  KritzelControlTextConfig.style = kritzelControlTextConfigCss;
249
249
 
250
- const kritzelControlsCss = ":host{display:flex;flex-direction:column;user-select:none}:host(.mobile){--kritzel-controls-control-hover-background-color:transparent;--kritzel-controls-control-active-background-color:transparent}.kritzel-controls{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:var(--kritzel-controls-gap, 8px);height:100%;padding:var(--kritzel-controls-padding, 8px);background-color:var(--kritzel-controls-background-color, #ffffff);border-radius:var(--kritzel-controls-border-radius, 16px);box-shadow:var(--kritzel-controls-box-shadow, 0 0 3px rgba(0, 0, 0, 0.08));border:var(--kritzel-controls-border, 1px solid #ebebeb);z-index:10000;position:relative}.kritzel-control{display:flex;justify-content:center;align-items:center;color:var(--kritzel-controls-control-color, #000000);border-radius:var(--kritzel-controls-control-border-radius, 12px);padding:var(--kritzel-controls-control-padding, 8px);border:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);-webkit-tap-highlight-color:transparent;font-weight:bold}.kritzel-control:focus,.kritzel-control:hover{background-color:var(--kritzel-controls-control-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.kritzel-control:active{background-color:var(--kritzel-controls-control-active-background-color, hsl(0, 0%, 0%, 8.6%))}.kritzel-control.selected,.kritzel-control.selected:hover,.kritzel-control.selected:active{background-color:var(--kritzel-controls-control-selected-background-color, #007AFF) !important;color:var(--kritzel-controls-control-selected-color, #ffffff) !important}.kritzel-control.selected:focus{background-color:var(--kritzel-controls-control-selected-background-color, #007bffe3) !important}.kritzel-divider{width:var(--kritzel-controls-divider-width, 1px);height:var(--kritzel-controls-divider-height, 24px);background-color:var(--kritzel-controls-divider-background-color, hsl(0, 0%, 0%, 4.3%))}.kritzel-config-container{position:relative;display:flex;justify-content:center;align-items:center;width:40px;height:40px;box-sizing:border-box;-webkit-tap-highlight-color:transparent}.kritzel-config{display:flex;justify-content:center;align-items:center;cursor:var(--kritzel-pointer-cursor, pointer);border-radius:50%}.color-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:50%;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.font-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:50%;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.no-config{height:24px;width:24px;border-radius:50%;border:1px dashed gray}kritzel-tooltip{position:fixed;bottom:56px;left:50%;transform:translateX(-50%);z-index:10001}";
250
+ const kritzelControlsCss = ":host{display:flex;flex-direction:column;user-select:none}:host(.mobile){--kritzel-controls-control-hover-background-color:transparent;--kritzel-controls-control-active-background-color:transparent}.kritzel-controls{display:flex;flex-direction:row;align-items:center;justify-content:flex-start;gap:var(--kritzel-controls-gap, 8px);height:100%;padding:var(--kritzel-controls-padding, 8px);background-color:var(--kritzel-controls-background-color, #ffffff);border-radius:var(--kritzel-controls-border-radius, 16px);box-shadow:var(--kritzel-controls-box-shadow, 0 0 3px rgba(0, 0, 0, 0.08));border:var(--kritzel-controls-border, 1px solid #ebebeb);z-index:10000;position:relative}.kritzel-control{display:flex;justify-content:center;align-items:center;color:var(--kritzel-controls-control-color, #000000);border-radius:var(--kritzel-controls-control-border-radius, 12px);padding:var(--kritzel-controls-control-padding, 8px);border:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);-webkit-tap-highlight-color:transparent;font-weight:bold}.kritzel-control:focus,.kritzel-control:hover{background-color:var(--kritzel-controls-control-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.kritzel-control:active{background-color:var(--kritzel-controls-control-active-background-color, hsl(0, 0%, 0%, 8.6%))}.kritzel-control.selected,.kritzel-control.selected:hover,.kritzel-control.selected:active{background-color:var(--kritzel-controls-control-selected-background-color, #007AFF) !important;color:var(--kritzel-controls-control-selected-color, #ffffff) !important}.kritzel-control.selected:focus{background-color:var(--kritzel-controls-control-selected-background-color, #007bffe3) !important}.kritzel-control-split{position:relative;display:flex;align-items:center;border-radius:var(--kritzel-controls-control-border-radius, 12px);color:var(--kritzel-controls-control-color, #000000)}.kritzel-control-split .kritzel-control-main{display:flex;justify-content:center;align-items:center;padding:var(--kritzel-controls-control-padding, 8px);border:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);-webkit-tap-highlight-color:transparent;border-radius:var(--kritzel-controls-control-border-radius, 12px);color:inherit}.kritzel-control-split.selected .kritzel-control-main{border-radius:var(--kritzel-controls-control-border-radius, 12px) 0 0 var(--kritzel-controls-control-border-radius, 12px)}.kritzel-control-split .kritzel-control-dropdown{display:flex;justify-content:center;align-items:center;align-self:stretch;border:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);-webkit-tap-highlight-color:transparent;border-radius:0 var(--kritzel-controls-control-border-radius, 12px) var(--kritzel-controls-control-border-radius, 12px) 0;color:inherit;width:0;padding:0;opacity:0;overflow:hidden;pointer-events:none;transition:width 0.15s ease-out, padding 0.15s ease-out, opacity 0.15s ease-out}.kritzel-control-split .kritzel-control-dropdown.visible{width:auto;padding:0 6px;opacity:1;pointer-events:auto}.kritzel-control-split .kritzel-control-main:focus,.kritzel-control-split .kritzel-control-main:hover,.kritzel-control-split .kritzel-control-dropdown:focus,.kritzel-control-split .kritzel-control-dropdown:hover{background-color:var(--kritzel-controls-control-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.kritzel-control-split .kritzel-control-main:active,.kritzel-control-split .kritzel-control-dropdown:active{background-color:var(--kritzel-controls-control-active-background-color, hsl(0, 0%, 0%, 8.6%))}.kritzel-control-split.selected{background-color:var(--kritzel-controls-control-selected-background-color, #007AFF) !important;color:var(--kritzel-controls-control-selected-color, #ffffff) !important}.kritzel-control-split.selected .kritzel-control-main:hover,.kritzel-control-split.selected .kritzel-control-dropdown:hover{background-color:rgba(255, 255, 255, 0.15)}.kritzel-submenu{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%);display:flex;flex-direction:column;background:var(--kritzel-controls-background-color, #ffffff);border-radius:12px;padding:6px;box-shadow:0 4px 12px rgba(0, 0, 0, 0.15);border:1px solid #ebebeb;z-index:10001;min-width:140px}.kritzel-submenu-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);border-radius:8px;color:var(--kritzel-controls-control-color, #000000);font-size:14px;text-align:left;white-space:nowrap;-webkit-tap-highlight-color:transparent}.kritzel-submenu-item:hover{background-color:var(--kritzel-controls-control-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.kritzel-submenu-item.active{background-color:var(--kritzel-controls-control-selected-background-color, #007AFF);color:var(--kritzel-controls-control-selected-color, #ffffff)}.kritzel-submenu-item.active:hover{background-color:var(--kritzel-controls-control-selected-background-color, #007AFF)}.kritzel-config-container{position:relative;display:flex;justify-content:center;align-items:center;height:40px;box-sizing:border-box;-webkit-tap-highlight-color:transparent;width:0;opacity:0;overflow:hidden;pointer-events:none;margin-left:calc(-1 * var(--kritzel-controls-gap, 8px));transition:width 0.2s ease-out, opacity 0.2s ease-out, margin-left 0.2s ease-out}.kritzel-config-container.visible{width:40px;opacity:1;pointer-events:auto;margin-left:0}.kritzel-config{display:flex;justify-content:center;align-items:center;cursor:var(--kritzel-pointer-cursor, pointer);border-radius:50%}.color-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:50%;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.font-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:50%;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.no-config{height:24px;width:24px;border-radius:50%;border:1px dashed gray}kritzel-tooltip{position:fixed;bottom:56px;left:50%;transform:translateX(-50%);z-index:10001}";
251
251
 
252
252
  const KritzelControls = class {
253
253
  constructor(hostRef) {
@@ -263,17 +263,24 @@ const KritzelControls = class {
263
263
  firstConfig = null;
264
264
  isTooltipVisible = false;
265
265
  isTouchDevice = KritzelDevicesHelper.isTouchDevice();
266
+ selectedSubOptions = new Map();
267
+ openSubMenuControl = null;
266
268
  handleDocumentClick(event) {
267
269
  const element = event.target;
268
270
  if (!this.kritzelEngine || element.closest('.kritzel-tooltip')) {
269
271
  return;
270
272
  }
271
273
  this.isTooltipVisible = false;
274
+ // Close submenu when clicking outside
275
+ if (!element.closest('.kritzel-control-split') && !element.closest('.kritzel-submenu')) {
276
+ this.openSubMenuControl = null;
277
+ }
272
278
  }
273
279
  handleKeyDown(event) {
274
280
  if (event.key === 'Escape') {
275
281
  event.preventDefault();
276
282
  this.closeTooltip();
283
+ this.openSubMenuControl = null;
277
284
  this.kritzelEngine?.enable();
278
285
  }
279
286
  }
@@ -349,36 +356,91 @@ const KritzelControls = class {
349
356
  this.isTooltipVisible = false;
350
357
  this.kritzelEngine?.enable();
351
358
  }
359
+ /**
360
+ * Get the currently selected sub-option for a control.
361
+ * Returns the first sub-option as default if none is selected.
362
+ */
363
+ getSelectedSubOption(control) {
364
+ if (!control.subOptions?.length)
365
+ return undefined;
366
+ return this.selectedSubOptions.get(control.name) || control.subOptions[0];
367
+ }
368
+ /**
369
+ * Toggle the submenu for a split-button control
370
+ */
371
+ toggleSubMenu(event, control) {
372
+ event.stopPropagation();
373
+ if (this.openSubMenuControl?.name === control.name) {
374
+ this.openSubMenuControl = null;
375
+ }
376
+ else {
377
+ this.openSubMenuControl = control;
378
+ }
379
+ }
380
+ /**
381
+ * Select a sub-option and update the tool property
382
+ */
383
+ async selectSubOption(control, option) {
384
+ // Update the selected sub-options map (create new Map for reactivity)
385
+ const newMap = new Map(this.selectedSubOptions);
386
+ newMap.set(control.name, option);
387
+ this.selectedSubOptions = newMap;
388
+ // Update the tool property if the tool is instantiated
389
+ if (control.tool && typeof control.tool !== 'function') {
390
+ control.tool[option.toolProperty] = option.value;
391
+ }
392
+ // Close the submenu
393
+ this.openSubMenuControl = null;
394
+ // Activate this control
395
+ await this.handleControlClick(control);
396
+ }
352
397
  render() {
353
- const hasNoConfig = this.activeControl?.config === undefined || this.activeControl?.config === null;
354
- return (h(Host, { key: 'c01351cbf438f2e36249b2ef3ef8725edcabd57b', class: {
398
+ const hasConfigUI = this.activeControl?.tool instanceof KritzelBrushTool ||
399
+ this.activeControl?.tool instanceof KritzelTextTool;
400
+ return (h(Host, { key: '34a8a81224f1714a30a0d6e03fb81ed031fe36a0', class: {
355
401
  mobile: this.isTouchDevice,
356
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '3c14cf25dd51f123dc8d33be92992e2f2c18ac40', style: {
402
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '8ddfe7b4872d59b08b0561dbd61c67b9c245dcc9', style: {
357
403
  position: 'absolute',
358
404
  bottom: '56px',
359
405
  left: '12px',
360
- }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '05719acfe12d1770bf49fb7d9989619f2e27ee0f', class: "kritzel-controls" }, this.controls.map(control => {
406
+ }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '370229830b9a6c0ae5704d9fb0ce35d130fcf049', class: "kritzel-controls" }, this.controls.map(control => {
361
407
  if (control.type === 'tool') {
408
+ // Check if this control has sub-options (split-button)
409
+ if (control.subOptions?.length) {
410
+ const selectedSubOption = this.getSelectedSubOption(control);
411
+ const isActive = this.activeControl?.name === control.name;
412
+ const isSubMenuOpen = this.openSubMenuControl?.name === control.name;
413
+ return (h("div", { class: {
414
+ 'kritzel-control-split': true,
415
+ 'selected': isActive,
416
+ }, key: control.name }, h("button", { class: "kritzel-control-main", onClick: () => this.handleControlClick(control), title: selectedSubOption?.label }, h("kritzel-icon", { name: selectedSubOption?.icon || control.icon })), h("button", { class: {
417
+ 'kritzel-control-dropdown': true,
418
+ 'visible': isActive,
419
+ }, onClick: (e) => this.toggleSubMenu(e, control), "aria-label": "Select shape type", "aria-expanded": isSubMenuOpen ? 'true' : 'false', tabIndex: isActive ? 0 : -1 }, h("kritzel-icon", { name: "chevron-down", size: 12 })), isSubMenuOpen && (h("div", { class: "kritzel-submenu" }, control.subOptions.map(option => (h("button", { class: {
420
+ 'kritzel-submenu-item': true,
421
+ 'active': option.id === selectedSubOption?.id,
422
+ }, key: option.id, onClick: () => this.selectSubOption(control, option) }, h("kritzel-icon", { name: option.icon, size: 20 }), h("span", null, option.label))))))));
423
+ }
424
+ // Regular tool control (no sub-options)
362
425
  return (h("button", { class: {
363
426
  'kritzel-control': true,
364
427
  'selected': this.activeControl?.name === control?.name,
365
428
  }, key: control.name, onClick: _event => this.handleControlClick?.(control) }, h("kritzel-icon", { name: control.icon })));
366
429
  }
367
- if (control.type === 'divider') {
368
- return h("div", { class: "kritzel-divider", key: control.name });
369
- }
370
430
  if (control.type === 'config' && control.name === this.firstConfig?.name && this.activeControl) {
371
- return (h("div", { class: "kritzel-config-container", key: control.name }, h("kritzel-tooltip", { ref: el => (this.tooltipRef = el), isVisible: this.isTooltipVisible, anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), onTooltipClosed: () => this.handleTooltipClosed() }, h("div", { style: { width: '294px', height: '100%' } }, this.activeControl.name === 'brush' && (h("kritzel-control-brush-config", { tool: this.activeToolAsBrushTool, onToolChange: event => this.handleToolChange?.(event) })), this.activeControl.name === 'text' && (h("kritzel-control-text-config", { tool: this.activeToolAsTextTool, onToolChange: event => this.handleToolChange?.(event) })))), h("div", { tabIndex: 0, class: "kritzel-config", onClick: event => this.handleConfigClick?.(event), onKeyDown: event => {
431
+ return (h("div", { class: {
432
+ 'kritzel-config-container': true,
433
+ 'visible': hasConfigUI,
434
+ }, key: control.name }, h("kritzel-tooltip", { ref: el => (this.tooltipRef = el), isVisible: this.isTooltipVisible, anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), onTooltipClosed: () => this.handleTooltipClosed() }, h("div", { style: { width: '294px', height: '100%' } }, this.activeControl.name === 'brush' && (h("kritzel-control-brush-config", { tool: this.activeToolAsBrushTool, onToolChange: event => this.handleToolChange?.(event) })), this.activeControl.name === 'text' && (h("kritzel-control-text-config", { tool: this.activeToolAsTextTool, onToolChange: event => this.handleToolChange?.(event) })))), h("div", { tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", onClick: event => this.handleConfigClick?.(event), onKeyDown: event => {
372
435
  if (event.key === 'Enter') {
373
436
  this.handleConfigClick?.(event);
374
437
  }
375
438
  }, style: {
376
- cursor: this.activeControl.config ? 'pointer' : 'default',
377
- pointerEvents: hasNoConfig ? 'none' : 'auto',
439
+ cursor: 'pointer',
378
440
  } }, this.activeControl.tool instanceof KritzelBrushTool && (h("div", { class: "color-container" }, h("kritzel-color", { value: this.activeToolAsBrushTool?.color, size: this.activeToolAsBrushTool?.size, style: {
379
441
  borderRadius: '50%',
380
442
  border: 'none',
381
- } }))), this.activeControl.tool instanceof KritzelTextTool && (h("div", { class: "font-container" }, h("kritzel-font", { fontFamily: this.activeToolAsTextTool?.fontFamily, size: this.activeToolAsTextTool?.fontSize, color: this.activeToolAsTextTool?.fontColor }))), hasNoConfig && h("div", { class: "no-config" }))));
443
+ } }))), this.activeControl.tool instanceof KritzelTextTool && (h("div", { class: "font-container" }, h("kritzel-font", { fontFamily: this.activeToolAsTextTool?.fontFamily, size: this.activeToolAsTextTool?.fontSize, color: this.activeToolAsTextTool?.fontColor }))))));
382
444
  }
383
445
  }))));
384
446
  }
@@ -573,6 +635,596 @@ const KritzelDropdown = class {
573
635
  };
574
636
  KritzelDropdown.style = kritzelDropdownCss;
575
637
 
638
+ var ShapeType;
639
+ (function (ShapeType) {
640
+ ShapeType["Rectangle"] = "rectangle";
641
+ ShapeType["Ellipse"] = "ellipse";
642
+ ShapeType["Triangle"] = "triangle";
643
+ })(ShapeType || (ShapeType = {}));
644
+
645
+ class KritzelShape extends KritzelBaseObject {
646
+ __class__ = 'KritzelShape';
647
+ shapeType = ShapeType.Rectangle;
648
+ fillColor = 'transparent';
649
+ strokeColor = '#000000';
650
+ strokeWidth = 4;
651
+ fontFamily = 'Arial';
652
+ fontSize = 16;
653
+ fontColor = '#000000';
654
+ /** Screen-space x coordinate of the shape's top-left corner (like Path.x) */
655
+ x = 0;
656
+ /** Screen-space y coordinate of the shape's top-left corner (like Path.y) */
657
+ y = 0;
658
+ scale = 1;
659
+ scaleFactor = 1;
660
+ isDebugInfoVisible = true;
661
+ isEditable = true;
662
+ isEditing = false;
663
+ editor = null;
664
+ content = null;
665
+ _schema = new Schema({
666
+ nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
667
+ marks: schema.spec.marks,
668
+ });
669
+ uneditedObject = null;
670
+ /**
671
+ * Returns the viewBox for the shape's SVG, using screen-space coordinates.
672
+ * This follows the same pattern as KritzelPath.viewBox.
673
+ */
674
+ get viewBox() {
675
+ return `${this.x} ${this.y} ${this.width} ${this.height}`;
676
+ }
677
+ constructor(config) {
678
+ super();
679
+ if (config) {
680
+ this.x = config.x ?? 0;
681
+ this.y = config.y ?? 0;
682
+ this.translateX = config.translateX ?? 0;
683
+ this.translateY = config.translateY ?? 0;
684
+ this.width = config.width ?? 100;
685
+ this.height = config.height ?? 100;
686
+ this.shapeType = config.shapeType ?? ShapeType.Rectangle;
687
+ this.fillColor = config.fillColor ?? 'transparent';
688
+ this.strokeColor = config.strokeColor ?? '#000000';
689
+ this.strokeWidth = config.strokeWidth ?? 4;
690
+ this.fontSize = config.fontSize ?? 16;
691
+ this.fontFamily = config.fontFamily ?? 'Arial';
692
+ this.fontColor = config.fontColor ?? '#000000';
693
+ this.scale = config.scale ?? 1;
694
+ this.scaleFactor = config.scaleX ?? 1;
695
+ }
696
+ }
697
+ /**
698
+ * Creates a new KritzelShape with screen-space coordinates.
699
+ * Following the same pattern as KritzelPath.create():
700
+ * - x, y are screen-space coordinates of the shape's top-left corner
701
+ * - translateX, translateY should be set to -viewportTranslateX, -viewportTranslateY
702
+ * - width, height are in screen-space
703
+ * - scale is the viewport scale at creation time
704
+ */
705
+ static create(core, config) {
706
+ const object = new KritzelShape();
707
+ object._core = core;
708
+ object.id = object.generateId();
709
+ object.workspaceId = core.store.state.activeWorkspace.id;
710
+ object.x = config?.x ?? 0;
711
+ object.y = config?.y ?? 0;
712
+ object.translateX = config?.translateX ?? 0;
713
+ object.translateY = config?.translateY ?? 0;
714
+ object.width = config?.width ?? 100;
715
+ object.height = config?.height ?? 100;
716
+ object.shapeType = config?.shapeType ?? ShapeType.Rectangle;
717
+ object.fillColor = config?.fillColor ?? 'transparent';
718
+ object.strokeColor = config?.strokeColor ?? '#000000';
719
+ object.strokeWidth = config?.strokeWidth ?? 4;
720
+ object.fontSize = config?.fontSize ?? 16;
721
+ object.fontFamily = config?.fontFamily ?? 'Arial';
722
+ object.fontColor = config?.fontColor ?? '#000000';
723
+ object.backgroundColor = 'transparent';
724
+ object.scaleFactor = 1;
725
+ object.scale = core.store.state.scale;
726
+ object.zIndex = core.store.currentZIndex;
727
+ object.editor = object.createEditor();
728
+ // Compute world-space translateX/Y from screen-space coordinates
729
+ // This follows the same pattern as KritzelPath.updateDimensions()
730
+ object.updateDimensions();
731
+ return object;
732
+ }
733
+ /**
734
+ * Updates the translateX/Y to world coordinates based on screen-space x, y.
735
+ * This follows the same pattern as KritzelPath.updateDimensions().
736
+ *
737
+ * The formula: translateX = (x + initialTranslateX) / scale
738
+ * where initialTranslateX was -viewportTranslateX
739
+ *
740
+ * This converts screen-space position to world coordinates.
741
+ */
742
+ updateDimensions() {
743
+ this.translateX = (this.x + this.translateX) / this.scale;
744
+ this.translateY = (this.y + this.translateY) / this.scale;
745
+ }
746
+ mount(element) {
747
+ if (element === null || this.isInViewport() === false) {
748
+ return;
749
+ }
750
+ if (this.isMounted && this.elementRef === element && this.editor.dom.parentElement === element) {
751
+ return;
752
+ }
753
+ this.elementRef = element;
754
+ this.isMounted = true;
755
+ }
756
+ mountTextEditor(element) {
757
+ if (element === null) {
758
+ return;
759
+ }
760
+ if (this.editor.dom.parentElement === element) {
761
+ return;
762
+ }
763
+ element.style.fontFamily = this.fontFamily;
764
+ element.style.fontSize = `${this.fontSize}pt`;
765
+ element.style.color = this.fontColor;
766
+ element.style.whiteSpace = 'pre-wrap';
767
+ element.style.wordWrap = 'break-word';
768
+ element.innerHTML = '';
769
+ element.appendChild(this.editor.dom);
770
+ }
771
+ createEditor() {
772
+ const doc = this._schema.node('doc', null, [this._schema.node('paragraph')]);
773
+ return new EditorView(null, {
774
+ state: EditorState.create({
775
+ doc: doc,
776
+ plugins: [keymap(baseKeymap)],
777
+ }),
778
+ editable: () => false,
779
+ dispatchTransaction: transaction => {
780
+ const newState = this.editor.state.apply(transaction);
781
+ this.editor.updateState(newState);
782
+ if (transaction.docChanged) {
783
+ this.content = newState.doc.toJSON();
784
+ if (!transaction.getMeta('fromRemote')) {
785
+ this._core.store.state.objects.update(this, { temporary: true });
786
+ }
787
+ }
788
+ },
789
+ });
790
+ }
791
+ setContent(content) {
792
+ this.content = content;
793
+ if (this.editor && content) {
794
+ const newDoc = this.editor.state.schema.nodeFromJSON(content);
795
+ const tr = this.editor.state.tr.replaceWith(0, this.editor.state.doc.content.size, newDoc.content);
796
+ tr.setMeta('fromRemote', true);
797
+ this.editor.dispatch(tr);
798
+ }
799
+ }
800
+ resize(x, y, width, height) {
801
+ if (width <= 1 || height <= 1) {
802
+ return;
803
+ }
804
+ this.width = width;
805
+ this.height = height;
806
+ this.translateX = x;
807
+ this.translateY = y;
808
+ this._core.store.state.objects.update(this);
809
+ }
810
+ focus(coords) {
811
+ if (this.editor) {
812
+ const doc = this.editor.state.doc;
813
+ if (coords?.x && coords?.y) {
814
+ const pos = this.editor.posAtCoords({ left: coords.x, top: coords.y });
815
+ if (pos) {
816
+ this.editor.dispatch(this.editor.state.tr.setSelection(TextSelection.create(doc, pos.pos)));
817
+ this.editor.focus();
818
+ if (KritzelDevicesHelper.isIOS()) {
819
+ this.scrollIntoViewOnIOS();
820
+ }
821
+ return;
822
+ }
823
+ }
824
+ const end = Math.max(1, doc.content.size - 1);
825
+ this.editor.dispatch(this.editor.state.tr.setSelection(TextSelection.create(doc, end)));
826
+ this.editor.focus();
827
+ if (KritzelDevicesHelper.isIOS()) {
828
+ this.scrollIntoViewOnIOS();
829
+ }
830
+ }
831
+ }
832
+ scrollIntoViewOnIOS() {
833
+ setTimeout(() => {
834
+ if (this.editor && this.editor.dom) {
835
+ this.editor.dom.scrollIntoView({
836
+ behavior: 'smooth',
837
+ block: 'center',
838
+ inline: 'nearest',
839
+ });
840
+ }
841
+ }, 300);
842
+ }
843
+ edit(event) {
844
+ KritzelKeyboardHelper.disableInteractiveWidget();
845
+ this.uneditedObject = this.clone();
846
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('shape'));
847
+ this.editor.setProps({ editable: () => true });
848
+ this.isEditing = true;
849
+ this._core.rerender();
850
+ this.focus({ x: event?.clientX, y: event?.clientY });
851
+ KritzelKeyboardHelper.enableInteractiveWidget();
852
+ }
853
+ save() {
854
+ this.content = this.editor.state.doc.toJSON();
855
+ this.editor.setProps({ editable: () => false });
856
+ this.editor.dom.blur();
857
+ this.isEditing = false;
858
+ this._core.store.state.objects.consolidateTemporaryItems();
859
+ this._core.store.state.objects.update(this);
860
+ this._core.engine.emitObjectsChange();
861
+ }
862
+ handlePointerDown(event) {
863
+ if (!this.isEditing) {
864
+ return;
865
+ }
866
+ event.stopPropagation();
867
+ }
868
+ handlePointerMove(event) {
869
+ if (!this.isEditing) {
870
+ return;
871
+ }
872
+ event.stopPropagation();
873
+ }
874
+ handlePointerUp(event) {
875
+ if (!this.isEditing) {
876
+ return;
877
+ }
878
+ event.stopPropagation();
879
+ }
880
+ copy() {
881
+ const copiedObject = super.copy();
882
+ copiedObject.editor = copiedObject.createEditor();
883
+ if (this.content) {
884
+ copiedObject.setContent(this.content);
885
+ }
886
+ return copiedObject;
887
+ }
888
+ serialize() {
889
+ const { _core, _elementRef, _schema, element, totalWidth, totalHeight, editor, uneditedObject, ...remainingProps } = this;
890
+ const clonedProps = structuredClone(remainingProps);
891
+ if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
892
+ clonedProps.element = element.cloneNode(true);
893
+ }
894
+ return clonedProps;
895
+ }
896
+ deserialize(object) {
897
+ super.deserialize(object);
898
+ if (object.content) {
899
+ this.setContent(object.content);
900
+ }
901
+ return this;
902
+ }
903
+ /**
904
+ * Returns the clipping polygon for arrow intersection.
905
+ * For ellipse: returns a many-sided polygon approximation
906
+ * For triangle: returns the 3 corners
907
+ * For rectangle: returns null (uses default rotatedPolygon)
908
+ *
909
+ * Includes padding for half the stroke width so arrow heads don't overlap the stroke.
910
+ */
911
+ getClipPolygon() {
912
+ // Calculate world-space center and dimensions
913
+ const worldWidth = this.totalWidth / this.scale;
914
+ const worldHeight = this.totalHeight / this.scale;
915
+ const centerX = this.translateX + worldWidth / 2;
916
+ const centerY = this.translateY + worldHeight / 2;
917
+ // Add padding for stroke width so arrows don't overlap the stroke
918
+ const strokePadding = (this.strokeWidth / this.scale) / 2;
919
+ switch (this.shapeType) {
920
+ case ShapeType.Ellipse:
921
+ // Return a 32-segment polygon approximation of the ellipse
922
+ // Add stroke padding to radii
923
+ return KritzelGeometryHelper.getEllipsePolygonApproximation(centerX, centerY, worldWidth / 2 + strokePadding, worldHeight / 2 + strokePadding, 32, this.rotation);
924
+ case ShapeType.Triangle:
925
+ // Return the 3 corners of the triangle in world coordinates
926
+ // Triangle: top-center, bottom-right, bottom-left
927
+ // Expand each vertex outward from center by strokePadding
928
+ const expandVertex = (vx, vy) => {
929
+ const dx = vx - centerX;
930
+ const dy = vy - centerY;
931
+ const dist = Math.sqrt(dx * dx + dy * dy);
932
+ if (dist === 0)
933
+ return { x: vx, y: vy };
934
+ const scale = (dist + strokePadding) / dist;
935
+ return {
936
+ x: centerX + dx * scale,
937
+ y: centerY + dy * scale
938
+ };
939
+ };
940
+ const topX = this.translateX + worldWidth / 2;
941
+ const topY = this.translateY;
942
+ const bottomLeftX = this.translateX;
943
+ const bottomLeftY = this.translateY + worldHeight;
944
+ const bottomRightX = this.translateX + worldWidth;
945
+ const bottomRightY = this.translateY + worldHeight;
946
+ const expandedTop = expandVertex(topX, topY);
947
+ const expandedBottomRight = expandVertex(bottomRightX, bottomRightY);
948
+ const expandedBottomLeft = expandVertex(bottomLeftX, bottomLeftY);
949
+ // Apply rotation around center if rotated
950
+ if (this.rotation !== 0) {
951
+ const cos = Math.cos(this.rotation);
952
+ const sin = Math.sin(this.rotation);
953
+ const rotate = (p) => {
954
+ const dx = p.x - centerX;
955
+ const dy = p.y - centerY;
956
+ return {
957
+ x: centerX + dx * cos - dy * sin,
958
+ y: centerY + dx * sin + dy * cos
959
+ };
960
+ };
961
+ return [
962
+ rotate(expandedTop),
963
+ rotate(expandedBottomRight),
964
+ rotate(expandedBottomLeft)
965
+ ];
966
+ }
967
+ return [expandedTop, expandedBottomRight, expandedBottomLeft];
968
+ case ShapeType.Rectangle:
969
+ default:
970
+ // For rectangles, return null to use the default rotatedPolygon
971
+ return null;
972
+ }
973
+ }
974
+ /**
975
+ * Returns the SVG path for rendering the shape.
976
+ * The path uses screen-space coordinates relative to (x, y).
977
+ */
978
+ getSvgPath() {
979
+ const w = this.width;
980
+ const h = this.height;
981
+ switch (this.shapeType) {
982
+ case ShapeType.Rectangle:
983
+ return `M ${this.x} ${this.y} L ${this.x + w} ${this.y} L ${this.x + w} ${this.y + h} L ${this.x} ${this.y + h} Z`;
984
+ case ShapeType.Ellipse:
985
+ const cx = this.x + w / 2;
986
+ const cy = this.y + h / 2;
987
+ const rx = w / 2;
988
+ const ry = h / 2;
989
+ return `M ${cx - rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy}`;
990
+ case ShapeType.Triangle:
991
+ const topX = this.x + w / 2;
992
+ const topY = this.y;
993
+ const bottomLeftX = this.x;
994
+ const bottomLeftY = this.y + h;
995
+ const bottomRightX = this.x + w;
996
+ const bottomRightY = this.y + h;
997
+ return `M ${topX} ${topY} L ${bottomRightX} ${bottomRightY} L ${bottomLeftX} ${bottomLeftY} Z`;
998
+ default:
999
+ return `M ${this.x} ${this.y} L ${this.x + w} ${this.y} L ${this.x + w} ${this.y + h} L ${this.x} ${this.y + h} Z`;
1000
+ }
1001
+ }
1002
+ }
1003
+
1004
+ class KritzelShapeTool extends KritzelBaseTool {
1005
+ shapeType = ShapeType.Rectangle;
1006
+ fillColor = 'transparent';
1007
+ strokeColor = '#000000';
1008
+ strokeWidth = 4;
1009
+ fontFamily = 'Arial';
1010
+ fontSize = 16;
1011
+ fontColor = '#000000';
1012
+ palette = [
1013
+ '#000000',
1014
+ '#FFFFFF',
1015
+ '#FF0000',
1016
+ '#00FF00',
1017
+ '#0000FF',
1018
+ '#FFFF00',
1019
+ '#FF00FF',
1020
+ '#00FFFF',
1021
+ '#808080',
1022
+ '#C0C0C0',
1023
+ '#800000',
1024
+ '#008000',
1025
+ '#000080',
1026
+ '#808000',
1027
+ '#800080',
1028
+ ];
1029
+ startX = 0;
1030
+ startY = 0;
1031
+ isDrawing = false;
1032
+ currentShape = null;
1033
+ constructor(core) {
1034
+ super(core);
1035
+ }
1036
+ handlePointerDown(event) {
1037
+ if (event.cancelable) {
1038
+ event.preventDefault();
1039
+ }
1040
+ if (event.pointerType === 'mouse') {
1041
+ const path = event.composedPath().slice(1);
1042
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
1043
+ const object = this._core.findObjectById(objectElement?.id);
1044
+ const activeShape = this._core.store.activeShape;
1045
+ if (activeShape === null && object instanceof KritzelShape) {
1046
+ object.edit(event);
1047
+ return;
1048
+ }
1049
+ if (activeShape !== null && object instanceof KritzelShape) {
1050
+ activeShape.save();
1051
+ object.edit(event);
1052
+ return;
1053
+ }
1054
+ if (activeShape !== null && object instanceof KritzelShape === false) {
1055
+ this._core.resetActiveShape();
1056
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1057
+ return;
1058
+ }
1059
+ if (KritzelEventHelper.isLeftClick(event) === false) {
1060
+ return;
1061
+ }
1062
+ this.startDrawing(event.clientX, event.clientY);
1063
+ }
1064
+ if (event.pointerType === 'touch') {
1065
+ const activePointers = Array.from(this._core.store.state.pointers.values());
1066
+ const path = event.composedPath().slice(1);
1067
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
1068
+ const object = this._core.findObjectById(objectElement?.id);
1069
+ const activeShape = this._core.store.activeShape;
1070
+ if (activeShape === null && object instanceof KritzelShape) {
1071
+ object.edit(event);
1072
+ return;
1073
+ }
1074
+ if (activeShape !== null && object instanceof KritzelShape) {
1075
+ activeShape.save();
1076
+ object.edit(event);
1077
+ return;
1078
+ }
1079
+ if (activeShape !== null && object instanceof KritzelShape === false) {
1080
+ this._core.resetActiveShape();
1081
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1082
+ return;
1083
+ }
1084
+ if (activePointers.length > 1) {
1085
+ return;
1086
+ }
1087
+ const clientX = Math.round(activePointers[0].clientX);
1088
+ const clientY = Math.round(activePointers[0].clientY);
1089
+ this.startDrawing(clientX, clientY);
1090
+ }
1091
+ }
1092
+ handlePointerMove(event) {
1093
+ if (event.cancelable) {
1094
+ event.preventDefault();
1095
+ }
1096
+ if (!this.isDrawing || !this.currentShape) {
1097
+ return;
1098
+ }
1099
+ if (event.pointerType === 'mouse') {
1100
+ this.updateShapeSize(event.clientX, event.clientY);
1101
+ }
1102
+ if (event.pointerType === 'touch') {
1103
+ const activePointers = Array.from(this._core.store.state.pointers.values());
1104
+ if (activePointers.length === 1) {
1105
+ const clientX = Math.round(activePointers[0].clientX);
1106
+ const clientY = Math.round(activePointers[0].clientY);
1107
+ this.updateShapeSize(clientX, clientY);
1108
+ }
1109
+ }
1110
+ }
1111
+ handlePointerUp(event) {
1112
+ if (event.cancelable) {
1113
+ event.preventDefault();
1114
+ }
1115
+ if (!this.isDrawing || !this.currentShape) {
1116
+ return;
1117
+ }
1118
+ this.finishDrawing();
1119
+ }
1120
+ /**
1121
+ * Start drawing a shape. Following the same pattern as LineTool/BrushTool:
1122
+ * - Store screen coordinates for startX, startY
1123
+ * - Set translateX/Y to -viewportTranslateX/Y (viewport offset)
1124
+ * - Set x, y to the actual screen position
1125
+ * - Let updateDimensions() convert to world coordinates
1126
+ */
1127
+ startDrawing(clientX, clientY) {
1128
+ // Store screen coordinates (relative to host element)
1129
+ this.startX = clientX - this._core.store.offsetX;
1130
+ this.startY = clientY - this._core.store.offsetY;
1131
+ this.isDrawing = true;
1132
+ // Create shape using screen coordinates, following Path/Line pattern
1133
+ this.currentShape = KritzelShape.create(this._core, {
1134
+ x: this.startX,
1135
+ y: this.startY,
1136
+ translateX: -this._core.store.state.translateX,
1137
+ translateY: -this._core.store.state.translateY,
1138
+ width: 1,
1139
+ height: 1,
1140
+ shapeType: this.shapeType,
1141
+ fillColor: this.fillColor,
1142
+ strokeColor: this.strokeColor,
1143
+ strokeWidth: this.strokeWidth,
1144
+ fontSize: this.fontSize,
1145
+ fontFamily: this.fontFamily,
1146
+ fontColor: this.fontColor,
1147
+ });
1148
+ this._core.store.state.objects.insert(this.currentShape);
1149
+ this._core.rerender();
1150
+ }
1151
+ /**
1152
+ * Update shape size during drawing. Following the same pattern as LineTool:
1153
+ * - Use screen coordinates directly
1154
+ * - The shape's x, y, width, height are all in screen space
1155
+ * - updateDimensions() handles conversion to world coordinates
1156
+ */
1157
+ updateShapeSize(clientX, clientY) {
1158
+ if (!this.currentShape) {
1159
+ return;
1160
+ }
1161
+ const currentX = clientX - this._core.store.offsetX;
1162
+ const currentY = clientY - this._core.store.offsetY;
1163
+ // Calculate bounding box in screen coordinates
1164
+ const minX = Math.min(this.startX, currentX);
1165
+ const minY = Math.min(this.startY, currentY);
1166
+ const width = Math.abs(currentX - this.startX);
1167
+ const height = Math.abs(currentY - this.startY);
1168
+ // Update shape with screen coordinates
1169
+ this.currentShape.x = minX;
1170
+ this.currentShape.y = minY;
1171
+ this.currentShape.width = Math.max(1, width);
1172
+ this.currentShape.height = Math.max(1, height);
1173
+ // Recalculate world-space translateX/Y
1174
+ // Reset translateX/Y to initial value before updateDimensions
1175
+ this.currentShape.translateX = -this._core.store.state.translateX;
1176
+ this.currentShape.translateY = -this._core.store.state.translateY;
1177
+ this.currentShape.updateDimensions();
1178
+ this._core.store.state.objects.update(this.currentShape);
1179
+ }
1180
+ finishDrawing() {
1181
+ if (!this.currentShape) {
1182
+ return;
1183
+ }
1184
+ // Remove shape if it's too small (likely an accidental click)
1185
+ // Compare in screen space
1186
+ if (this.currentShape.width < 10 && this.currentShape.height < 10) {
1187
+ const shapeId = this.currentShape.id;
1188
+ this._core.store.state.objects.remove(o => o.id === shapeId);
1189
+ }
1190
+ else {
1191
+ this.currentShape.zIndex = this._core.store.currentZIndex;
1192
+ this._core.store.state.objects.update(this.currentShape);
1193
+ this._core.engine.emitObjectsChange();
1194
+ this._core.selectObjects([this.currentShape]);
1195
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1196
+ }
1197
+ this.isDrawing = false;
1198
+ this.currentShape = null;
1199
+ this._core.rerender();
1200
+ }
1201
+ }
1202
+
1203
+ const DEFAULT_SHAPE_CONFIG = {
1204
+ shapeType: ShapeType.Rectangle,
1205
+ fillColor: 'transparent',
1206
+ strokeColor: '#000000',
1207
+ strokeWidth: 4,
1208
+ fontColor: '#000000',
1209
+ fontSize: 16,
1210
+ fontFamily: 'Arial',
1211
+ palette: [
1212
+ '#000000',
1213
+ '#ff5252',
1214
+ '#ffbc00',
1215
+ '#00c853',
1216
+ '#0000FF',
1217
+ '#d500f9',
1218
+ '#fafafa',
1219
+ '#a52714',
1220
+ '#ee8100',
1221
+ '#558b2f',
1222
+ '#01579b',
1223
+ '#8e24aa',
1224
+ '#90a4ae',
1225
+ ],
1226
+ };
1227
+
576
1228
  const ABSOLUTE_SCALE_MAX = 1000;
577
1229
  const ABSOLUTE_SCALE_MIN = 0.0001;
578
1230
 
@@ -623,16 +1275,24 @@ const KritzelEditor = class {
623
1275
  icon: 'type',
624
1276
  config: DEFAULT_TEXT_CONFIG,
625
1277
  },
1278
+ {
1279
+ name: 'shape',
1280
+ type: 'tool',
1281
+ tool: KritzelShapeTool,
1282
+ icon: 'shape-rectangle',
1283
+ config: DEFAULT_SHAPE_CONFIG,
1284
+ subOptions: [
1285
+ { id: 'rectangle', icon: 'shape-rectangle', label: 'Rectangle', value: ShapeType.Rectangle, toolProperty: 'shapeType' },
1286
+ { id: 'ellipse', icon: 'shape-ellipse', label: 'Ellipse', value: ShapeType.Ellipse, toolProperty: 'shapeType' },
1287
+ { id: 'triangle', icon: 'shape-triangle', label: 'Triangle', value: ShapeType.Triangle, toolProperty: 'shapeType' },
1288
+ ],
1289
+ },
626
1290
  {
627
1291
  name: 'image',
628
1292
  type: 'tool',
629
1293
  tool: KritzelImageTool,
630
1294
  icon: 'image',
631
1295
  },
632
- {
633
- name: 'divider',
634
- type: 'divider',
635
- },
636
1296
  {
637
1297
  name: 'config',
638
1298
  type: 'config',
@@ -809,7 +1469,7 @@ const KritzelEditor = class {
809
1469
  }
810
1470
  }
811
1471
  render() {
812
- return (h(Host, { key: 'f107cce23625f36978736ef948a51cd91d84c3f1' }, h("kritzel-workspace-manager", { key: 'a924de174ac5226c3ac5507c35e162b35490a5dd', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-engine", { key: '48b6c14ca2c1e1a2c745ec81294a331a463219a1', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, syncConfig: this.syncConfig, scaleMax: this.scaleMax, scaleMin: this.scaleMin, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onObjectsChange: event => this.handleObjectsChange(event), onUndoStateChange: event => this.handleUndoStateChange(event) }), h("kritzel-controls", { key: '2ec26e1444f4ba642c4fdc4ef2da1a62dc0256a6', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, onIsControlsReady: () => (this.isControlsReady = true) })));
1472
+ return (h(Host, { key: 'a39268fb2722bc9e1627a46a3430a574322dfdfb' }, h("kritzel-workspace-manager", { key: 'b0c08f2cab64347c0ee14a87ed0ab769a2e95733', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-engine", { key: 'c0efb9b0cdfdf3a9ba945e71e37c60ebfc45e981', ref: el => (this.engineRef = el), workspace: this.activeWorkspace, syncConfig: this.syncConfig, scaleMax: this.scaleMax, scaleMin: this.scaleMin, globalContextMenuItems: this.globalContextMenuItems, objectContextMenuItems: this.objectContextMenuItems, onIsEngineReady: event => this.onEngineReady(event), onWorkspacesChange: event => this.handleWorkspacesChange(event), onObjectsChange: event => this.handleObjectsChange(event), onUndoStateChange: event => this.handleUndoStateChange(event) }), h("kritzel-controls", { key: '5a275fb94e2f55a1f79d6d5b5f518305cd739f24', class: { 'keyboard-open': this.isVirtualKeyboardOpen }, style: { display: this.isControlsVisible ? 'flex' : 'none' }, ref: el => (this.controlsRef = el), controls: this.controls, isUtilityPanelVisible: this.isUtilityPanelVisible, undoState: this.undoState, onIsControlsReady: () => (this.isControlsReady = true) })));
813
1473
  }
814
1474
  static get watchers() { return {
815
1475
  "isEngineReady": ["onIsEngineReady"],
@@ -18054,6 +18714,14 @@ class KritzelViewport {
18054
18714
  this._core.rerender();
18055
18715
  }, 100);
18056
18716
  }
18717
+ /**
18718
+ * Cancels any pending debounced viewport updates.
18719
+ * Should be called before switching workspaces to prevent the old workspace's
18720
+ * viewport state from being saved to the new workspace.
18721
+ */
18722
+ cancelPendingUpdates() {
18723
+ this._debounceUpdate.cancel();
18724
+ }
18057
18725
  handleResize() {
18058
18726
  this._core.store.state.viewportWidth = this._core.store.state.host.clientWidth;
18059
18727
  this._core.store.state.viewportHeight = this._core.store.state.host.clientHeight;
@@ -18366,7 +19034,7 @@ const DEFAULT_ENGINE_CONFIG = {
18366
19034
  contextMenuY: 0,
18367
19035
  skipContextMenu: false,
18368
19036
  debugInfo: {
18369
- showObjectInfo: false,
19037
+ showObjectInfo: true,
18370
19038
  showViewportInfo: false
18371
19039
  },
18372
19040
  host: null,
@@ -18475,6 +19143,17 @@ class KritzelReviver {
18475
19143
  case 'KritzelText':
18476
19144
  revivedObj = KritzelText.create(this._core, obj.fontSize, obj.fontFamily).deserialize(obj);
18477
19145
  break;
19146
+ case 'KritzelShape':
19147
+ revivedObj = KritzelShape.create(this._core, {
19148
+ shapeType: obj.shapeType,
19149
+ fillColor: obj.fillColor,
19150
+ strokeColor: obj.strokeColor,
19151
+ strokeWidth: obj.strokeWidth,
19152
+ fontSize: obj.fontSize,
19153
+ fontFamily: obj.fontFamily,
19154
+ fontColor: obj.fontColor,
19155
+ }).deserialize(obj);
19156
+ break;
18478
19157
  case 'KritzelImage':
18479
19158
  revivedObj = KritzelImage.create(this._core).deserialize(obj);
18480
19159
  break;
@@ -18505,6 +19184,9 @@ class KritzelReviver {
18505
19184
  case 'KritzelLineTool':
18506
19185
  revivedObj = new KritzelLineTool(this._core);
18507
19186
  break;
19187
+ case 'KritzelShapeTool':
19188
+ revivedObj = new KritzelShapeTool(this._core);
19189
+ break;
18508
19190
  default:
18509
19191
  revivedObj = obj;
18510
19192
  }
@@ -18980,6 +19662,10 @@ class KritzelStore {
18980
19662
  const activeTexts = this._state.objects.filter(o => o instanceof KritzelText && o.isEditing);
18981
19663
  return activeTexts.length > 0 ? activeTexts[0] : null;
18982
19664
  }
19665
+ get activeShape() {
19666
+ const activeShapes = this._state.objects.filter(o => o instanceof KritzelShape && o.isEditing);
19667
+ return activeShapes.length > 0 ? activeShapes[0] : null;
19668
+ }
18983
19669
  get currentPath() {
18984
19670
  const drawingPaths = this._state.objects.filter(o => o instanceof KritzelPath && o.isCompleted === false);
18985
19671
  return drawingPaths.length > 0 ? drawingPaths[0] : null;
@@ -19228,6 +19914,11 @@ class KritzelCore {
19228
19914
  if (this._store.state.objects && this._store.state.objects.isReady) {
19229
19915
  this._store.state.objects.destroy();
19230
19916
  }
19917
+ // Create new ObjectMap with its own Y.Doc for this workspace
19918
+ const objectsMap = new KritzelObjectMap();
19919
+ // Assign immediately so the UI shows an empty state while loading,
19920
+ // instead of showing the old workspace's objects with the new workspace's viewport
19921
+ this._store.state.objects = objectsMap;
19231
19922
  // Set active workspace
19232
19923
  this._store.state.activeWorkspace = activeWorkspace;
19233
19924
  this._store.state.workspaces = this.loadWorkspacesFromAppState();
@@ -19236,10 +19927,7 @@ class KritzelCore {
19236
19927
  this._store.state.translateX = viewport.translateX ?? 0;
19237
19928
  this._store.state.translateY = viewport.translateY ?? 0;
19238
19929
  this._store.state.scale = viewport.scale ?? 1;
19239
- // Create new ObjectMap with its own Y.Doc for this workspace
19240
- const objectsMap = new KritzelObjectMap();
19241
19930
  await objectsMap.initialize(this, activeWorkspace.id, this._syncConfig);
19242
- this._store.state.objects = objectsMap;
19243
19931
  // Rebuild anchor index after loading objects
19244
19932
  this._anchorManager.rebuildIndex();
19245
19933
  this.engine.emitObjectsChange();
@@ -19616,6 +20304,12 @@ class KritzelCore {
19616
20304
  }
19617
20305
  }
19618
20306
  }
20307
+ resetActiveShape() {
20308
+ const activeShape = this._store.activeShape;
20309
+ if (activeShape) {
20310
+ activeShape.save();
20311
+ }
20312
+ }
19619
20313
  getObjectFromPointerEvent(event, selector = '.object') {
19620
20314
  const shadowRoot = this._store.state.host?.shadowRoot;
19621
20315
  if (!shadowRoot)
@@ -19688,6 +20382,14 @@ class KritzelCore {
19688
20382
  return { x: worldX, y: worldY };
19689
20383
  }
19690
20384
  beforeWorkspaceChange() {
20385
+ // Cancel any pending debounced viewport updates to prevent them from
20386
+ // saving the old workspace's viewport to the new workspace
20387
+ this._kritzelEngine.viewport?.cancelPendingUpdates();
20388
+ // Immediately save the current workspace's viewport before switching
20389
+ const currentWorkspace = this._store.state.activeWorkspace;
20390
+ if (currentWorkspace) {
20391
+ this.updateWorkspaceViewport(this._store.state.translateX, this._store.state.translateY, this._store.state.scale);
20392
+ }
19691
20393
  this.resetActiveText();
19692
20394
  this.clearSelection();
19693
20395
  this._store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
@@ -20113,9 +20815,9 @@ const KritzelEngine = class {
20113
20815
  height: this.core.store.state.viewportHeight / this.core.store.state.scale,
20114
20816
  depth: 100,
20115
20817
  };
20116
- const visibleObjects = this.core.store.state.objects.query(viewportBounds);
20818
+ const visibleObjects = this.core.store.state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
20117
20819
  this.core.cursorManager.applyCursor();
20118
- return (h(Host, { key: '4a7e8c72ad1f80c3c31aa37eff33e78634cb37d1' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: '3c8981ce6cafbc49f4f04189d27809c511bf45c4', class: "debug-panel" }, h("div", { key: '428d483a87b98cde24446a0a26ba790ec2a6358b' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '3ec597c219109c026f78e5018a885e88e51aa5c4' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '803c0d009b19fdc7c465ef14f3b5f278a954a912' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: '8b1aef0ae845cdf4b93afe59d8c78c84dd181a2d' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '137ead0c39a2fba55b4c4dde53fb09d0832439e1' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: 'b19f710385e4306620176551853eb2645b96ca7a' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: '943b55daada95aa37b759e45a15ffcfca76fba72' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: 'd001782dd4bc48c84c08477b26e5f3fce8df0b47' }, "Scale: ", this.core.store.state?.scale), h("div", { key: 'ed640d3905b39a995ba40a1cbf38f10a4aa1b400' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: '154888aabbb56365aaf4584b0b991be848629e3e' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: 'c0c39e988940baf20137de8a0eeda2c085da2810' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: 'abd5a79b27bc75e898877d33097597c057dc3f2b' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '9c49bf3246525c9a755bb293bec8a143ade97158' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '88ff8ab48af31bf036719f72a5a3726780ab9720' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: '9650075f855fcf91b7303d3ace14242075fc523d' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: '911acb1f90f9812d30dc0aebceb207aecf9b4248' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: 'b65db638d3cefbd74e2d03b639ee5fe86a2a858b' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: 'c53ca259dadc656762733119b4086c1f82235a7e' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: 'ae2d7a8917af450eb3bfd8e171a87530f337c0c2' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '5633cd9901ec028e4f5433c84b07321932beea66' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: '82232a8eec470562e5658dee9713c855e78ca6a3' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '5b6b04b323a21ef37ddb144721ea01dccc7aec8b' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: '3287f804515cfa31eaa755332e2043fce4feb7a9' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: '3a59fd4db60f6f05d419de39149ea10e482875d4' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: 'f91a8ea9dfc50e5f1ae89d66c7609a103e3263a4' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: '40ba8fb5530243c18e0b0c3ad68dadb2cbc615a1', id: "origin", class: "origin", style: {
20820
+ return (h(Host, { key: '209ccf8414c635c9e04ca4807c95a29c629071ec' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'df6a03d1341887960bb91cbaa0a5dca6880a9293', class: "debug-panel" }, h("div", { key: '65157757ec6549ad2af311085a9618ab661f5ac0' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '20137b6c651a9aa8df15bce66d5bfe870a7af139' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '49dd30286641236ae428c731bb181cb06269852e' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: 'd8ee431a10335e99aec40a596817765d7ee3f67b' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '34b1f31146080780433d6c1718d13bbfe397cb9b' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: '4c279a106101cf808c91a2be4987569b21ae52b3' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: '6ebd7ad3c803ef45d58a74180c52e8be8af8bca3' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '345970c8b2b54f3a0d8fe1d9bb1b7b33023db386' }, "Scale: ", this.core.store.state?.scale), h("div", { key: 'fc265ae0e71cf22311cab3006dfc29b9d24397d2' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: '9f921032a58b55e60f82d11a76f269051357d3e1' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '6d197e06cc362a9ecb8bfe00d272aecfec2b49e1' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: '5313589ba2a509c95a5730dcf29c4003d9884cbf' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '8af9f9d6e33a2897c20962048bbf2a058f9d4a45' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '81aec0050d3e2122643b61fd725e71b25d97bc27' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: 'c9956f05c01b74ee1666d9395a2715c3463fe401' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: 'ae0a01c8f8aafc08326b136720c6a75b6aec6299' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '7816d40d73248a55120440e6d721325e1894178e' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: 'c22383bacca0ada584f2e8a8577523c905d74ae2' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: '8185a7318c6cdfde558cc372b79582d58b706f9e' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '2d56b05377693ef032ab2316fa753ecedf264b9c' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: '2e2bf9fd321ef04dc0ac6dc21539eb0117c9fb23' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '3118ab970d5d37e01405c42308fc051d675d4bfa' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: 'd8a31065843f89552f2d9c03dcbca960a716fa6c' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: 'cb8d28b6ea63cbb6572f761011fccb847b748227' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: 'd112a7e833c265085246817423557bfdc5b30b74' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: 'df4f302192a8fc9644b7f883618b0a0f8a600dc8', id: "origin", class: "origin", style: {
20119
20821
  transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
20120
20822
  } }, visibleObjects?.map(object => {
20121
20823
  return (h("div", { key: object.id, style: {
@@ -20161,7 +20863,32 @@ const KritzelEngine = class {
20161
20863
  backgroundColor: object.backgroundColor,
20162
20864
  overflow: 'visible',
20163
20865
  textRendering: this.core.store.state.isScaling || this.core.store.state.isPanning ? 'optimizeSpeed' : 'auto',
20164
- } })), KritzelClassHelper.isInstanceOf(object, 'KritzelCustomElement') && (h("div", { ref: el => object.mount(el), style: {
20866
+ } })), KritzelClassHelper.isInstanceOf(object, 'KritzelShape') && (h("div", { ref: el => object.mount(el), onPointerDown: e => object.handlePointerDown(e), onPointerMove: e => object.handlePointerMove(e), onPointerUp: e => object.handlePointerUp(e), style: {
20867
+ width: '100%',
20868
+ height: '100%',
20869
+ position: 'relative',
20870
+ overflow: 'visible',
20871
+ } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", style: {
20872
+ position: 'absolute',
20873
+ top: '0',
20874
+ left: '0',
20875
+ width: '100%',
20876
+ height: '100%',
20877
+ overflow: 'visible',
20878
+ pointerEvents: 'none',
20879
+ }, viewBox: object.viewBox, preserveAspectRatio: "none" }, h("path", { d: object.getSvgPath(), fill: object.fillColor, stroke: object.strokeColor, "stroke-width": object.strokeWidth })), h("div", { ref: el => object.mountTextEditor(el), style: {
20880
+ position: 'absolute',
20881
+ top: '0',
20882
+ left: '0',
20883
+ width: '100%',
20884
+ height: '100%',
20885
+ display: 'flex',
20886
+ alignItems: 'center',
20887
+ justifyContent: 'center',
20888
+ textAlign: 'center',
20889
+ overflow: 'hidden',
20890
+ pointerEvents: object.isEditing ? 'auto' : 'none',
20891
+ } }))), KritzelClassHelper.isInstanceOf(object, 'KritzelCustomElement') && (h("div", { ref: el => object.mount(el), style: {
20165
20892
  width: '100%',
20166
20893
  height: '100%',
20167
20894
  pointerEvents: 'auto',
@@ -20177,7 +20904,13 @@ const KritzelEngine = class {
20177
20904
  borderWidth: KritzelDevicesHelper.isFirefox() ? object.borderWidth + 'px' : '0',
20178
20905
  borderStyle: KritzelDevicesHelper.isFirefox() ? 'solid' : 'none',
20179
20906
  borderColor: KritzelDevicesHelper.isFirefox() ? object.borderColor : 'transparent',
20180
- } }))), this.core.store.state.debugInfo.showObjectInfo && object.isDebugInfoVisible && (h("g", { style: { pointerEvents: 'none' } }, h("foreignObject", { x: object.totalWidth.toString(), y: "0", width: "400px", height: "160px", style: { minHeight: '0', minWidth: '0' } }, h("div", { style: { width: '100%', height: '100%' } }, h("div", { style: { whiteSpace: 'nowrap' } }, "Id: ", object.id), h("div", { style: { whiteSpace: 'nowrap' } }, "width: ", object.width), h("div", { style: { whiteSpace: 'nowrap' } }, "height: ", object.height), h("div", { style: { whiteSpace: 'nowrap' } }, "translateX: ", object.translateX), h("div", { style: { whiteSpace: 'nowrap' } }, "translateY: ", object.translateY), h("div", { style: { whiteSpace: 'nowrap' } }, "rotationDegrees: ", object.rotationDegrees)))))), h("svg", { xmlns: "http://www.w3.org/2000/svg", style: {
20907
+ } })))), this.core.store.state.debugInfo.showObjectInfo && object.isDebugInfoVisible && (h("div", { style: {
20908
+ pointerEvents: 'none',
20909
+ position: 'absolute',
20910
+ left: `${object.totalWidth}px`,
20911
+ top: '0',
20912
+ zIndex: (object.zIndex + 2).toString(),
20913
+ } }, h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "Id: ", object.id), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "width: ", object.width), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "height: ", object.height), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateX: ", object.translateX), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "translateY: ", object.translateY), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "rotationDegrees: ", object.rotationDegrees), h("div", { style: { whiteSpace: 'nowrap', fontSize: '10px' } }, "zIndex: ", object.zIndex))), h("svg", { xmlns: "http://www.w3.org/2000/svg", style: {
20181
20914
  zIndex: (object.zIndex + 1).toString(),
20182
20915
  height: object?.totalHeight.toString(),
20183
20916
  width: object?.totalWidth.toString(),
@@ -20333,7 +21066,7 @@ const KritzelEngine = class {
20333
21066
  stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
20334
21067
  strokeWidth: data.indicatorStrokeWidth,
20335
21068
  } }))));
20336
- })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '7e350c48da76c54a223f23ead9dea8018b0bc36c', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
21069
+ })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '81b44614c23f4016a1daa914ef8ea73eda0869eb', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
20337
21070
  position: 'fixed',
20338
21071
  left: `${this.core.store.state.contextMenuX}px`,
20339
21072
  top: `${this.core.store.state.contextMenuY}px`,
@@ -20344,7 +21077,7 @@ const KritzelEngine = class {
20344
21077
  y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
20345
21078
  }, this.core.store.selectionGroup?.objects);
20346
21079
  this.hideContextMenu();
20347
- }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: '7923a3f6a92fa71911c971166171317b5bc421bf', core: this.core })));
21080
+ }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: 'f748cd15c916d9fcb5fbf445167c69c1cc8aea63', core: this.core })));
20348
21081
  }
20349
21082
  static get watchers() { return {
20350
21083
  "workspace": ["onWorkspaceChange"],