kritzel-stencil 0.0.161 → 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 (155) hide show
  1. package/dist/cjs/{default-text-tool.config-zB3FPuXq.js → default-line-tool.config-DJMYrkSu.js} +6301 -3866
  2. package/dist/cjs/default-line-tool.config-DJMYrkSu.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +131 -127
  4. package/dist/cjs/index.cjs.js.map +1 -1
  5. package/dist/cjs/kritzel-color_22.cjs.entry.js +1233 -167
  6. package/dist/cjs/loader.cjs.js +1 -1
  7. package/dist/cjs/stencil.cjs.js +1 -1
  8. package/dist/collection/classes/core/core.class.js +159 -6
  9. package/dist/collection/classes/core/core.class.js.map +1 -1
  10. package/dist/collection/classes/core/reviver.class.js +24 -0
  11. package/dist/collection/classes/core/reviver.class.js.map +1 -1
  12. package/dist/collection/classes/core/store.class.js +10 -0
  13. package/dist/collection/classes/core/store.class.js.map +1 -1
  14. package/dist/collection/classes/core/viewport.class.js +8 -0
  15. package/dist/collection/classes/core/viewport.class.js.map +1 -1
  16. package/dist/collection/classes/handlers/line-handle.handler.js +383 -0
  17. package/dist/collection/classes/handlers/line-handle.handler.js.map +1 -0
  18. package/dist/collection/classes/handlers/move.handler.js +2 -2
  19. package/dist/collection/classes/handlers/move.handler.js.map +1 -1
  20. package/dist/collection/classes/managers/anchor.manager.js +1052 -0
  21. package/dist/collection/classes/managers/anchor.manager.js.map +1 -0
  22. package/dist/collection/classes/managers/cursor.manager.js +117 -0
  23. package/dist/collection/classes/managers/cursor.manager.js.map +1 -0
  24. package/dist/collection/classes/objects/base-object.class.js +4 -2
  25. package/dist/collection/classes/objects/base-object.class.js.map +1 -1
  26. package/dist/collection/classes/objects/line.class.js +564 -0
  27. package/dist/collection/classes/objects/line.class.js.map +1 -0
  28. package/dist/collection/classes/objects/path.class.js +85 -0
  29. package/dist/collection/classes/objects/path.class.js.map +1 -1
  30. package/dist/collection/classes/objects/selection-group.class.js +4 -0
  31. package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
  32. package/dist/collection/classes/objects/shape.class.js +372 -0
  33. package/dist/collection/classes/objects/shape.class.js.map +1 -0
  34. package/dist/collection/classes/registries/icon-registry.class.js +5 -0
  35. package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
  36. package/dist/collection/classes/tools/brush-tool.class.js +2 -2
  37. package/dist/collection/classes/tools/brush-tool.class.js.map +1 -1
  38. package/dist/collection/classes/tools/line-tool.class.js +172 -0
  39. package/dist/collection/classes/tools/line-tool.class.js.map +1 -0
  40. package/dist/collection/classes/tools/selection-tool.class.js +41 -8
  41. package/dist/collection/classes/tools/selection-tool.class.js.map +1 -1
  42. package/dist/collection/classes/tools/shape-tool.class.js +204 -0
  43. package/dist/collection/classes/tools/shape-tool.class.js.map +1 -0
  44. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +26 -6
  45. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  46. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +168 -60
  47. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  48. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +143 -5
  49. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +76 -12
  50. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js.map +1 -1
  51. package/dist/collection/configs/default-engine-config.js +5 -1
  52. package/dist/collection/configs/default-engine-config.js.map +1 -1
  53. package/dist/collection/configs/default-line-tool.config.js +34 -0
  54. package/dist/collection/configs/default-line-tool.config.js.map +1 -0
  55. package/dist/collection/configs/default-shape-tool.config.js +26 -0
  56. package/dist/collection/configs/default-shape-tool.config.js.map +1 -0
  57. package/dist/collection/enums/shape-type.enum.js +7 -0
  58. package/dist/collection/enums/shape-type.enum.js.map +1 -0
  59. package/dist/collection/helpers/geometry.helper.js +97 -0
  60. package/dist/collection/helpers/geometry.helper.js.map +1 -1
  61. package/dist/collection/index.js +5 -0
  62. package/dist/collection/index.js.map +1 -1
  63. package/dist/collection/interfaces/anchor.interface.js +2 -0
  64. package/dist/collection/interfaces/anchor.interface.js.map +1 -0
  65. package/dist/collection/interfaces/arrow-head.interface.js +2 -0
  66. package/dist/collection/interfaces/arrow-head.interface.js.map +1 -0
  67. package/dist/collection/interfaces/engine-state.interface.js.map +1 -1
  68. package/dist/collection/interfaces/line-options.interface.js +2 -0
  69. package/dist/collection/interfaces/line-options.interface.js.map +1 -0
  70. package/dist/collection/interfaces/toolbar-control.interface.js.map +1 -1
  71. package/dist/components/index.js +4 -4
  72. package/dist/components/kritzel-brush-style.js +1 -1
  73. package/dist/components/kritzel-context-menu.js +1 -1
  74. package/dist/components/kritzel-control-brush-config.js +1 -1
  75. package/dist/components/kritzel-control-text-config.js +1 -1
  76. package/dist/components/kritzel-controls.js +1 -1
  77. package/dist/components/kritzel-editor.js +91 -17
  78. package/dist/components/kritzel-editor.js.map +1 -1
  79. package/dist/components/kritzel-engine.js +1 -1
  80. package/dist/components/kritzel-icon.js +1 -1
  81. package/dist/components/kritzel-menu-item.js +1 -1
  82. package/dist/components/kritzel-menu.js +1 -1
  83. package/dist/components/kritzel-split-button.js +1 -1
  84. package/dist/components/kritzel-utility-panel.js +1 -1
  85. package/dist/components/kritzel-workspace-manager.js +1 -1
  86. package/dist/components/{p-DxNbcUzt.js → p-5OECjGHq.js} +3 -3
  87. package/dist/components/{p-DxNbcUzt.js.map → p-5OECjGHq.js.map} +1 -1
  88. package/dist/components/{p-i0IlGLv2.js → p-BSBMBjhq.js} +3 -3
  89. package/dist/components/{p-i0IlGLv2.js.map → p-BSBMBjhq.js.map} +1 -1
  90. package/dist/components/{p-BdZKPKnx.js → p-BuS7MM1j.js} +4 -4
  91. package/dist/components/{p-BdZKPKnx.js.map → p-BuS7MM1j.js.map} +1 -1
  92. package/dist/components/{p-D7BLVRXX.js → p-CnRfQsIC.js} +3716 -554
  93. package/dist/components/p-CnRfQsIC.js.map +1 -0
  94. package/dist/components/{p-DbKKCHKd.js → p-Cv4BGNPb.js} +7 -2
  95. package/dist/components/p-Cv4BGNPb.js.map +1 -0
  96. package/dist/components/{p-D_ygcWSz.js → p-D1YAsWrL.js} +3 -3
  97. package/dist/components/{p-D_ygcWSz.js.map → p-D1YAsWrL.js.map} +1 -1
  98. package/dist/components/{p-Doixm8-N.js → p-D8L0t-Ro.js} +3 -3
  99. package/dist/components/{p-Doixm8-N.js.map → p-D8L0t-Ro.js.map} +1 -1
  100. package/dist/components/{p-CC8KFHSe.js → p-DguzZn_x.js} +3 -3
  101. package/dist/components/{p-CC8KFHSe.js.map → p-DguzZn_x.js.map} +1 -1
  102. package/dist/components/{p-CBYBurdY.js → p-Dz2XHHqa.js} +191 -7
  103. package/dist/components/p-Dz2XHHqa.js.map +1 -0
  104. package/dist/components/{p-58y59Acb.js → p-I3iPEDpx.js} +5 -5
  105. package/dist/components/{p-58y59Acb.js.map → p-I3iPEDpx.js.map} +1 -1
  106. package/dist/components/{p-BpXgwgnV.js → p-tp96UZ0l.js} +83 -19
  107. package/dist/components/p-tp96UZ0l.js.map +1 -0
  108. package/dist/esm/{default-text-tool.config-BvCgOiKA.js → default-line-tool.config-C35P3XfD.js} +6288 -3867
  109. package/dist/esm/default-line-tool.config-C35P3XfD.js.map +1 -0
  110. package/dist/esm/index.js +2 -2
  111. package/dist/esm/kritzel-color_22.entry.js +1154 -88
  112. package/dist/esm/loader.js +1 -1
  113. package/dist/esm/stencil.js +1 -1
  114. package/dist/stencil/index.esm.js +1 -1
  115. package/dist/stencil/p-9d43b708.entry.js +10 -0
  116. package/dist/stencil/p-9d43b708.entry.js.map +1 -0
  117. package/dist/stencil/p-C35P3XfD.js +2 -0
  118. package/dist/stencil/p-C35P3XfD.js.map +1 -0
  119. package/dist/stencil/stencil.esm.js +1 -1
  120. package/dist/types/classes/core/core.class.d.ts +19 -0
  121. package/dist/types/classes/core/store.class.d.ts +4 -0
  122. package/dist/types/classes/core/viewport.class.d.ts +6 -0
  123. package/dist/types/classes/handlers/line-handle.handler.d.ts +34 -0
  124. package/dist/types/classes/managers/anchor.manager.d.ts +180 -0
  125. package/dist/types/classes/managers/cursor.manager.d.ts +43 -0
  126. package/dist/types/classes/objects/line.class.d.ts +98 -0
  127. package/dist/types/classes/objects/path.class.d.ts +7 -0
  128. package/dist/types/classes/objects/shape.class.d.ts +116 -0
  129. package/dist/types/classes/tools/line-tool.class.d.ts +17 -0
  130. package/dist/types/classes/tools/selection-tool.class.d.ts +4 -0
  131. package/dist/types/classes/tools/shape-tool.class.d.ts +37 -0
  132. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -4
  133. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +16 -1
  134. package/dist/types/components.d.ts +5 -5
  135. package/dist/types/configs/default-line-tool.config.d.ts +2 -0
  136. package/dist/types/configs/default-shape-tool.config.d.ts +2 -0
  137. package/dist/types/enums/shape-type.enum.d.ts +5 -0
  138. package/dist/types/helpers/geometry.helper.d.ts +31 -0
  139. package/dist/types/index.d.ts +5 -0
  140. package/dist/types/interfaces/anchor.interface.d.ts +137 -0
  141. package/dist/types/interfaces/arrow-head.interface.d.ts +26 -0
  142. package/dist/types/interfaces/engine-state.interface.d.ts +8 -0
  143. package/dist/types/interfaces/line-options.interface.d.ts +21 -0
  144. package/dist/types/interfaces/toolbar-control.interface.d.ts +36 -2
  145. package/package.json +1 -1
  146. package/dist/cjs/default-text-tool.config-zB3FPuXq.js.map +0 -1
  147. package/dist/components/p-BpXgwgnV.js.map +0 -1
  148. package/dist/components/p-CBYBurdY.js.map +0 -1
  149. package/dist/components/p-D7BLVRXX.js.map +0 -1
  150. package/dist/components/p-DbKKCHKd.js.map +0 -1
  151. package/dist/esm/default-text-tool.config-BvCgOiKA.js.map +0 -1
  152. package/dist/stencil/p-6d9756d9.entry.js +0 -10
  153. package/dist/stencil/p-6d9756d9.entry.js.map +0 -1
  154. package/dist/stencil/p-BvCgOiKA.js +0 -2
  155. package/dist/stencil/p-BvCgOiKA.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 { U as KritzelDevicesHelper, G as KritzelBrushTool, L as KritzelTextTool, V as KritzelMouseButton, N as KritzelSelectionTool, S as DEFAULT_BRUSH_CONFIG, I as KritzelEraserTool, T as DEFAULT_TEXT_CONFIG, J as KritzelImageTool, R as KritzelWorkspace, W as KritzelIconRegistry, X as KritzelKeyboardHelper, Y as KritzelBaseHandler, Z as KritzelToolRegistry, _ as KritzelSelectionBox, $ as KritzelSelectionGroup, a0 as KritzelBaseObject, F as KritzelImage, K as KritzelText, E as KritzelPath, a1 as Doc, a2 as DEFAULT_SYNC_CONFIG, a3 as UndoManager, Q as KritzelAppStateMap, a4 as ObjectHelper, a5 as KritzelEventHelper, a6 as KritzelBaseTool, M as KritzelCursorHelper } from './default-text-tool.config-BvCgOiKA.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
 
@@ -603,6 +1255,13 @@ const KritzelEditor = class {
603
1255
  icon: 'pen',
604
1256
  config: DEFAULT_BRUSH_CONFIG,
605
1257
  },
1258
+ {
1259
+ name: 'line',
1260
+ type: 'tool',
1261
+ tool: KritzelLineTool,
1262
+ icon: 'arrow',
1263
+ config: DEFAULT_LINE_TOOL_CONFIG,
1264
+ },
606
1265
  {
607
1266
  name: 'eraser',
608
1267
  type: 'tool',
@@ -616,16 +1275,24 @@ const KritzelEditor = class {
616
1275
  icon: 'type',
617
1276
  config: DEFAULT_TEXT_CONFIG,
618
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
+ },
619
1290
  {
620
1291
  name: 'image',
621
1292
  type: 'tool',
622
1293
  tool: KritzelImageTool,
623
1294
  icon: 'image',
624
1295
  },
625
- {
626
- name: 'divider',
627
- type: 'divider',
628
- },
629
1296
  {
630
1297
  name: 'config',
631
1298
  type: 'config',
@@ -802,7 +1469,7 @@ const KritzelEditor = class {
802
1469
  }
803
1470
  }
804
1471
  render() {
805
- return (h(Host, { key: 'c824014ce85ef5c94436211df5241d23f11caed4' }, h("kritzel-workspace-manager", { key: '8ce43afa37896897674b5df943bef3fe1ffba32b', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-engine", { key: '12f80313bb145d9065a6222d5a8b503f0eca177a', 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: '0a1e63580afcbe65e5cf42833d86c3b34bb686c7', 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) })));
806
1473
  }
807
1474
  static get watchers() { return {
808
1475
  "isEngineReady": ["onIsEngineReady"],
@@ -18047,6 +18714,14 @@ class KritzelViewport {
18047
18714
  this._core.rerender();
18048
18715
  }, 100);
18049
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
+ }
18050
18725
  handleResize() {
18051
18726
  this._core.store.state.viewportWidth = this._core.store.state.host.clientWidth;
18052
18727
  this._core.store.state.viewportHeight = this._core.store.state.host.clientHeight;
@@ -18325,18 +19000,14 @@ class KritzelContextMenuHandler extends KritzelBaseHandler {
18325
19000
  }
18326
19001
  }
18327
19002
 
18328
- class KritzelClassHelper {
18329
- static isInstanceOf(object, className) {
18330
- return !!object && object.__class__ === className;
18331
- }
18332
- }
18333
-
18334
19003
  const DEFAULT_ENGINE_CONFIG = {
18335
19004
  activeWorkspace: null,
18336
19005
  activeTool: null,
18337
19006
  copiedObjects: null,
18338
19007
  objects: null,
19008
+ snapCandidate: null,
18339
19009
  resizeHandleType: null,
19010
+ lineHandleType: null,
18340
19011
  hasViewportChanged: false,
18341
19012
  hasObjectsChanged: false,
18342
19013
  isReady: false,
@@ -18350,6 +19021,8 @@ const DEFAULT_ENGINE_CONFIG = {
18350
19021
  isRotating: false,
18351
19022
  isRotationHandleHovered: false,
18352
19023
  isRotationHandleSelected: false,
19024
+ isLineHandleSelected: false,
19025
+ isLineHandleDragging: false,
18353
19026
  isDragging: false,
18354
19027
  isDrawing: false,
18355
19028
  isErasing: false,
@@ -18361,7 +19034,7 @@ const DEFAULT_ENGINE_CONFIG = {
18361
19034
  contextMenuY: 0,
18362
19035
  skipContextMenu: false,
18363
19036
  debugInfo: {
18364
- showObjectInfo: false,
19037
+ showObjectInfo: true,
18365
19038
  showViewportInfo: false
18366
19039
  },
18367
19040
  host: null,
@@ -18464,9 +19137,23 @@ class KritzelReviver {
18464
19137
  case 'KritzelPath':
18465
19138
  revivedObj = KritzelPath.create(this._core).deserialize(obj);
18466
19139
  break;
19140
+ case 'KritzelLine':
19141
+ revivedObj = KritzelLine.create(this._core).deserialize(obj);
19142
+ break;
18467
19143
  case 'KritzelText':
18468
19144
  revivedObj = KritzelText.create(this._core, obj.fontSize, obj.fontFamily).deserialize(obj);
18469
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;
18470
19157
  case 'KritzelImage':
18471
19158
  revivedObj = KritzelImage.create(this._core).deserialize(obj);
18472
19159
  break;
@@ -18494,6 +19181,12 @@ class KritzelReviver {
18494
19181
  case 'KritzelTextTool':
18495
19182
  revivedObj = new KritzelTextTool(this._core);
18496
19183
  break;
19184
+ case 'KritzelLineTool':
19185
+ revivedObj = new KritzelLineTool(this._core);
19186
+ break;
19187
+ case 'KritzelShapeTool':
19188
+ revivedObj = new KritzelShapeTool(this._core);
19189
+ break;
18497
19190
  default:
18498
19191
  revivedObj = obj;
18499
19192
  }
@@ -18969,10 +19662,18 @@ class KritzelStore {
18969
19662
  const activeTexts = this._state.objects.filter(o => o instanceof KritzelText && o.isEditing);
18970
19663
  return activeTexts.length > 0 ? activeTexts[0] : null;
18971
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
+ }
18972
19669
  get currentPath() {
18973
19670
  const drawingPaths = this._state.objects.filter(o => o instanceof KritzelPath && o.isCompleted === false);
18974
19671
  return drawingPaths.length > 0 ? drawingPaths[0] : null;
18975
19672
  }
19673
+ get currentLine() {
19674
+ const drawingLines = this._state.objects.filter(o => o instanceof KritzelLine && o.isCompleted === false);
19675
+ return drawingLines.length > 0 ? drawingLines[0] : null;
19676
+ }
18976
19677
  get offsetX() {
18977
19678
  return this._state.host.getBoundingClientRect().left;
18978
19679
  }
@@ -19009,11 +19710,129 @@ class KritzelStore {
19009
19710
  }
19010
19711
  }
19011
19712
 
19713
+ /**
19714
+ * Manages cursor state and updates across the application.
19715
+ * Handles cursor changes based on handle hovers, tool states, and interactions.
19716
+ */
19717
+ class KritzelCursorManager {
19718
+ _core;
19719
+ _targetElement = null;
19720
+ _shadowRoot = null;
19721
+ constructor(core) {
19722
+ this._core = core;
19723
+ }
19724
+ /**
19725
+ * Sets the target element where cursor styles will be applied.
19726
+ * Also sets the pointer cursor CSS variable for child components.
19727
+ */
19728
+ setTargetElement(element) {
19729
+ // Reset cursor on old target
19730
+ if (this._targetElement) {
19731
+ this._targetElement.style.cursor = '';
19732
+ this._targetElement.style.removeProperty('--kritzel-pointer-cursor');
19733
+ }
19734
+ this._targetElement = element;
19735
+ // Set the pointer cursor CSS variable for child components to use
19736
+ if (this._targetElement) {
19737
+ this._targetElement.style.setProperty('--kritzel-pointer-cursor', KritzelCursorHelper.getPointerCursor());
19738
+ }
19739
+ }
19740
+ /**
19741
+ * Gets the current cursor target element.
19742
+ */
19743
+ getTargetElement() {
19744
+ return this._targetElement;
19745
+ }
19746
+ /**
19747
+ * Sets the shadow root for element detection.
19748
+ */
19749
+ setShadowRoot(shadowRoot) {
19750
+ this._shadowRoot = shadowRoot;
19751
+ }
19752
+ /**
19753
+ * Resets cursor to default state.
19754
+ * Should be called when all pointers are released.
19755
+ */
19756
+ resetToDefault() {
19757
+ this._core.store.state.cursor = { icon: 'default', iconActive: 'default' };
19758
+ }
19759
+ /**
19760
+ * Detects handle hover states using elementsFromPoint() to work with overlapped elements.
19761
+ * This approach finds all elements at the pointer position instead of relying on pointerenter/pointerleave.
19762
+ */
19763
+ updateHoverState(ev) {
19764
+ if (this._core.store.isPointerDown)
19765
+ return;
19766
+ if (!this._shadowRoot)
19767
+ return;
19768
+ const elementsAtPoint = this._shadowRoot.elementsFromPoint(ev.clientX, ev.clientY);
19769
+ if (!elementsAtPoint || elementsAtPoint.length === 0)
19770
+ return;
19771
+ // Check for resize handle overlays (selection group)
19772
+ const resizeHandleOverlay = elementsAtPoint.find(el => el.classList.contains('resize-handle-overlay'));
19773
+ if (resizeHandleOverlay) {
19774
+ const selectionGroup = this._core.store.selectionGroup;
19775
+ const rotationDegrees = selectionGroup?.rotationDegrees ?? 0;
19776
+ // Determine rotation offset based on handle position
19777
+ const isTopLeft = resizeHandleOverlay.classList.contains('top-left');
19778
+ const isBottomRight = resizeHandleOverlay.classList.contains('bottom-right');
19779
+ const rotationOffset = (isTopLeft || isBottomRight) ? -45 : 45;
19780
+ this._core.store.state.cursor = { icon: 'move-vertical', rotation: rotationDegrees + rotationOffset };
19781
+ return;
19782
+ }
19783
+ // Check for rotation handle overlay
19784
+ const rotationHandleOverlay = elementsAtPoint.find(el => el.classList.contains('rotation-handle-overlay'));
19785
+ if (rotationHandleOverlay) {
19786
+ this._core.store.state.cursor = { icon: 'hand', iconActive: 'hand-grab' };
19787
+ return;
19788
+ }
19789
+ // Check for line handle overlays (selection line)
19790
+ const lineHandleOverlay = elementsAtPoint.find(el => el.classList.contains('selection-line-handle-overlay'));
19791
+ if (lineHandleOverlay) {
19792
+ this._core.store.state.cursor = { icon: 'hand', iconActive: 'hand-grab' };
19793
+ return;
19794
+ }
19795
+ // Reset cursor if not hovering any handle
19796
+ this._core.store.state.cursor = { icon: 'default', iconActive: 'default' };
19797
+ }
19798
+ /**
19799
+ * Applies the current cursor state to the target element.
19800
+ * Should be called in the render loop.
19801
+ */
19802
+ applyCursor() {
19803
+ const state = this._core.store.state;
19804
+ const isPointerDown = this._core.store.isPointerDown;
19805
+ const icon = state.cursor?.icon;
19806
+ const iconActive = state.cursor?.iconActive ?? icon;
19807
+ const rotation = state.cursor?.rotation;
19808
+ const cursor = KritzelCursorHelper.getCursor({
19809
+ iconName: isPointerDown ? iconActive : icon,
19810
+ rotation: rotation,
19811
+ });
19812
+ if (this._targetElement) {
19813
+ this._targetElement.style.cursor = cursor;
19814
+ }
19815
+ }
19816
+ /**
19817
+ * Cleans up cursor state when the component is disconnected.
19818
+ */
19819
+ cleanup() {
19820
+ if (this._targetElement) {
19821
+ this._targetElement.style.cursor = '';
19822
+ this._targetElement.style.removeProperty('--kritzel-pointer-cursor');
19823
+ }
19824
+ this._targetElement = null;
19825
+ this._shadowRoot = null;
19826
+ }
19827
+ }
19828
+
19012
19829
  class KritzelCore {
19013
19830
  _kritzelEngine;
19014
19831
  _store;
19015
19832
  _syncConfig;
19016
19833
  _appStateMap;
19834
+ _anchorManager;
19835
+ _cursorManager;
19017
19836
  get engine() {
19018
19837
  return this._kritzelEngine;
19019
19838
  }
@@ -19023,10 +19842,18 @@ class KritzelCore {
19023
19842
  get appStateMap() {
19024
19843
  return this._appStateMap;
19025
19844
  }
19845
+ get anchorManager() {
19846
+ return this._anchorManager;
19847
+ }
19848
+ get cursorManager() {
19849
+ return this._cursorManager;
19850
+ }
19026
19851
  constructor(kritzelEngine) {
19027
19852
  this._kritzelEngine = kritzelEngine;
19028
19853
  this._store = new KritzelStore(DEFAULT_ENGINE_CONFIG);
19029
19854
  this._appStateMap = new KritzelAppStateMap();
19855
+ this._anchorManager = new KritzelAnchorManager(this);
19856
+ this._cursorManager = new KritzelCursorManager(this);
19030
19857
  }
19031
19858
  setSyncConfig(config) {
19032
19859
  this._syncConfig = config;
@@ -19087,6 +19914,11 @@ class KritzelCore {
19087
19914
  if (this._store.state.objects && this._store.state.objects.isReady) {
19088
19915
  this._store.state.objects.destroy();
19089
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;
19090
19922
  // Set active workspace
19091
19923
  this._store.state.activeWorkspace = activeWorkspace;
19092
19924
  this._store.state.workspaces = this.loadWorkspacesFromAppState();
@@ -19095,10 +19927,9 @@ class KritzelCore {
19095
19927
  this._store.state.translateX = viewport.translateX ?? 0;
19096
19928
  this._store.state.translateY = viewport.translateY ?? 0;
19097
19929
  this._store.state.scale = viewport.scale ?? 1;
19098
- // Create new ObjectMap with its own Y.Doc for this workspace
19099
- const objectsMap = new KritzelObjectMap();
19100
19930
  await objectsMap.initialize(this, activeWorkspace.id, this._syncConfig);
19101
- this._store.state.objects = objectsMap;
19931
+ // Rebuild anchor index after loading objects
19932
+ this._anchorManager.rebuildIndex();
19102
19933
  this.engine.emitObjectsChange();
19103
19934
  this.rerender();
19104
19935
  }
@@ -19159,6 +19990,15 @@ class KritzelCore {
19159
19990
  this._store.state.objects.insert(object);
19160
19991
  }
19161
19992
  removeObject(object) {
19993
+ // Handle anchor cleanup
19994
+ if (object instanceof KritzelLine) {
19995
+ // If removing a line, clean up its anchor index entries
19996
+ this._anchorManager.handleLineDeleted(object.id);
19997
+ }
19998
+ else {
19999
+ // If removing a non-line object, detach any lines anchored to it
20000
+ this._anchorManager.handleObjectDeleted(object.id);
20001
+ }
19162
20002
  object.isMounted = false;
19163
20003
  this._store.state.objects.remove(o => o.id === object.id);
19164
20004
  }
@@ -19216,8 +20056,18 @@ class KritzelCore {
19216
20056
  copy() {
19217
20057
  const selectionGroup = this._store.selectionGroup;
19218
20058
  if (selectionGroup) {
19219
- // Copy each object and store them in an array
19220
- this._store.state.copiedObjects = selectionGroup.objects.sort((a, b) => a.zIndex - b.zIndex).map(obj => obj.copy());
20059
+ // Copy each object and store them in an array, also track the ID mapping
20060
+ const idMapping = new Map();
20061
+ const copiedObjects = selectionGroup.objects
20062
+ .sort((a, b) => a.zIndex - b.zIndex)
20063
+ .map(obj => {
20064
+ const copiedObj = obj.copy();
20065
+ // Map: newId -> originalId
20066
+ idMapping.set(copiedObj.id, obj.id);
20067
+ return copiedObj;
20068
+ });
20069
+ this._store.state.copiedObjects = copiedObjects;
20070
+ this._store.state.copiedObjectIdMapping = idMapping;
19221
20071
  }
19222
20072
  }
19223
20073
  paste(x, y) {
@@ -19226,6 +20076,7 @@ class KritzelCore {
19226
20076
  return;
19227
20077
  }
19228
20078
  const activeWorkspace = this._store.state.activeWorkspace;
20079
+ const originalIdMapping = this._store.state.copiedObjectIdMapping;
19229
20080
  // Check if we're pasting from a different workspace
19230
20081
  const isDifferentWorkspace = copiedObjects.some(obj => obj.workspaceId !== activeWorkspace.id);
19231
20082
  // Calculate the bounding box of all copied objects
@@ -19257,6 +20108,14 @@ class KritzelCore {
19257
20108
  this.removeSelectionBox();
19258
20109
  // Create a new selection group for the pasted objects
19259
20110
  const selectionGroup = KritzelSelectionGroup.create(this);
20111
+ // Build a reverse mapping: originalId -> newCopiedObjectId
20112
+ // This is used to remap anchor references from original IDs to the copied object IDs
20113
+ const originalToNewIdMap = new Map();
20114
+ if (originalIdMapping) {
20115
+ originalIdMapping.forEach((originalId, copiedId) => {
20116
+ originalToNewIdMap.set(originalId, copiedId);
20117
+ });
20118
+ }
19260
20119
  // First add all copied objects to the objectsMap with updated positions
19261
20120
  copiedObjects.forEach((obj, i) => {
19262
20121
  // Update workspace if pasting to a different workspace
@@ -19272,6 +20131,27 @@ class KritzelCore {
19272
20131
  // Add to selection group
19273
20132
  selectionGroup.addOrRemove(obj);
19274
20133
  });
20134
+ // Update line anchors to point to the newly pasted objects
20135
+ // Only remap if the anchor target was also part of the copied selection
20136
+ copiedObjects.forEach(obj => {
20137
+ if (obj instanceof KritzelLine) {
20138
+ let updated = false;
20139
+ if (obj.startAnchor && originalToNewIdMap.has(obj.startAnchor.objectId)) {
20140
+ obj.startAnchor = { objectId: originalToNewIdMap.get(obj.startAnchor.objectId) };
20141
+ updated = true;
20142
+ }
20143
+ if (obj.endAnchor && originalToNewIdMap.has(obj.endAnchor.objectId)) {
20144
+ obj.endAnchor = { objectId: originalToNewIdMap.get(obj.endAnchor.objectId) };
20145
+ updated = true;
20146
+ }
20147
+ // If anchors were updated, rebuild the anchor index and persist the change
20148
+ if (updated) {
20149
+ this._store.state.objects.update(obj);
20150
+ }
20151
+ }
20152
+ });
20153
+ // Rebuild anchor index after all anchor updates
20154
+ this._anchorManager.rebuildIndex();
19275
20155
  // Mark selection group as selected
19276
20156
  selectionGroup.isSelected = true;
19277
20157
  // Set rotation for single object
@@ -19297,7 +20177,17 @@ class KritzelCore {
19297
20177
  // Update copiedObjects to the newly created objects for potential future pastes
19298
20178
  const newSelectionGroup = this._store.selectionGroup;
19299
20179
  if (newSelectionGroup) {
19300
- this._store.state.copiedObjects = newSelectionGroup.objects.sort((a, b) => a.zIndex - b.zIndex).map(obj => obj.copy());
20180
+ // Create new copies and track the ID mapping for future pastes
20181
+ const newIdMapping = new Map();
20182
+ const newCopiedObjects = newSelectionGroup.objects
20183
+ .sort((a, b) => a.zIndex - b.zIndex)
20184
+ .map(obj => {
20185
+ const copiedObj = obj.copy();
20186
+ newIdMapping.set(copiedObj.id, obj.id);
20187
+ return copiedObj;
20188
+ });
20189
+ this._store.state.copiedObjects = newCopiedObjects;
20190
+ this._store.state.copiedObjectIdMapping = newIdMapping;
19301
20191
  }
19302
20192
  this._store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
19303
20193
  this.engine.emitObjectsChange();
@@ -19398,6 +20288,9 @@ class KritzelCore {
19398
20288
  this._store.state.isSelecting = false;
19399
20289
  this._store.state.isResizeHandleSelected = false;
19400
20290
  this._store.state.isRotationHandleSelected = false;
20291
+ this._store.state.isLineHandleSelected = false;
20292
+ this._store.state.isLineHandleDragging = false;
20293
+ this._store.state.lineHandleType = null;
19401
20294
  this.rerender();
19402
20295
  }
19403
20296
  resetActiveText() {
@@ -19411,6 +20304,12 @@ class KritzelCore {
19411
20304
  }
19412
20305
  }
19413
20306
  }
20307
+ resetActiveShape() {
20308
+ const activeShape = this._store.activeShape;
20309
+ if (activeShape) {
20310
+ activeShape.save();
20311
+ }
20312
+ }
19414
20313
  getObjectFromPointerEvent(event, selector = '.object') {
19415
20314
  const shadowRoot = this._store.state.host?.shadowRoot;
19416
20315
  if (!shadowRoot)
@@ -19449,6 +20348,26 @@ class KritzelCore {
19449
20348
  }
19450
20349
  return [];
19451
20350
  }
20351
+ /**
20352
+ * Get all elements at a pointer position that match a given selector.
20353
+ * Uses elementsFromPoint to find overlapped elements.
20354
+ */
20355
+ getElementsAtPoint(event, selector) {
20356
+ const shadowRoot = this._store.state.host?.shadowRoot;
20357
+ if (!shadowRoot)
20358
+ return [];
20359
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
20360
+ if (!elementsAtPoint || elementsAtPoint.length === 0)
20361
+ return [];
20362
+ return elementsAtPoint.filter(el => el.matches(selector));
20363
+ }
20364
+ /**
20365
+ * Check if any element at the pointer position matches the given selector.
20366
+ * Useful for detecting hover states on overlapped elements.
20367
+ */
20368
+ isPointerOverElement(event, selector) {
20369
+ return this.getElementsAtPoint(event, selector).length > 0;
20370
+ }
19452
20371
  getCanvasPoint(event) {
19453
20372
  if (!this._store.state.host) {
19454
20373
  return { x: 0, y: 0 };
@@ -19463,10 +20382,58 @@ class KritzelCore {
19463
20382
  return { x: worldX, y: worldY };
19464
20383
  }
19465
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
+ }
19466
20393
  this.resetActiveText();
19467
20394
  this.clearSelection();
19468
20395
  this._store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
19469
20396
  }
20397
+ displaySelectionGroupUI(object) {
20398
+ if (!object.isSelected) {
20399
+ return false;
20400
+ }
20401
+ const selectionGroup = this._store.selectionGroup;
20402
+ if (!selectionGroup) {
20403
+ // During selection phase (no group yet), hide UI for KritzelLine objects
20404
+ return !(object instanceof KritzelLine);
20405
+ }
20406
+ // Show UI if selection contains more than one object
20407
+ if (selectionGroup.objects.length > 1) {
20408
+ return true;
20409
+ }
20410
+ // Hide UI if selection contains a single KritzelLine
20411
+ if (selectionGroup.objects.length === 1) {
20412
+ const selectedObject = selectionGroup.objects[0];
20413
+ return !(selectedObject instanceof KritzelLine);
20414
+ }
20415
+ return true;
20416
+ }
20417
+ displaySelectionLineUI(object) {
20418
+ // Only show line UI on KritzelLine objects, not on selection groups
20419
+ if (!(object instanceof KritzelLine)) {
20420
+ return false;
20421
+ }
20422
+ // During selection phase (no group yet), show line UI if line is selected
20423
+ const selectionGroup = this._store.selectionGroup;
20424
+ if (!selectionGroup) {
20425
+ return object.isSelected;
20426
+ }
20427
+ if (!selectionGroup.isSelected) {
20428
+ return false;
20429
+ }
20430
+ // Show UI only if selection contains exactly one KritzelLine and it's this object
20431
+ if (selectionGroup.objects.length === 1) {
20432
+ const selectedObject = selectionGroup.objects[0];
20433
+ return selectedObject instanceof KritzelLine && selectedObject.id === object.id;
20434
+ }
20435
+ return false;
20436
+ }
19470
20437
  }
19471
20438
 
19472
20439
  const kritzelEngineCss = ":host{display:block;position:relative;height:100%;width:100%;overflow:hidden;background-color:var(--kritzel-engine-background-color, #ffffff)}:host,:host *{touch-action:none;user-select:none}.ProseMirror{outline:none}p,h1,h2,h3,h4,h5,h6,blockquote,pre{margin:0;padding:0}.debug-panel{position:absolute;pointer-events:none;top:0;right:0}.origin{position:relative;top:0;left:0;height:0;width:0;pointer-events:none;-webkit-transform-origin:top left;-moz-transform-origin:top left;transform-origin:top left;overflow:visible}.object{overflow:visible}.PlaygroundEditorTheme__quote{margin:0;margin-left:20px;margin-bottom:10px;font-size:15px;color:rgb(101, 103, 107);border-left-color:rgb(206, 208, 212);border-left-width:4px;border-left-style:solid;padding-left:16px}";
@@ -19510,12 +20477,7 @@ const KritzelEngine = class {
19510
20477
  }
19511
20478
  cursorTarget;
19512
20479
  onCursorTargetChange(newValue) {
19513
- // Reset cursor on old target
19514
- if (this.cursorTargetElement) {
19515
- this.cursorTargetElement.style.cursor = '';
19516
- }
19517
- // Set new target (defaults to document.body)
19518
- this.cursorTargetElement = newValue || document.body;
20480
+ this.core.cursorManager.setTargetElement(newValue || document.body);
19519
20481
  }
19520
20482
  isEngineReady;
19521
20483
  activeToolChange;
@@ -19563,6 +20525,8 @@ const KritzelEngine = class {
19563
20525
  if (this.core.store.state.pointers.size > 1) {
19564
20526
  this.throttledPointerMoveMulti(ev);
19565
20527
  }
20528
+ // Update cursor for handle hover states using elementsFromPoint
20529
+ this.core.cursorManager.updateHoverState(ev);
19566
20530
  this.viewport.handlePointerMove(ev);
19567
20531
  this.core.store.state?.activeTool?.handlePointerMove(ev);
19568
20532
  }
@@ -19574,7 +20538,7 @@ const KritzelEngine = class {
19574
20538
  this.host.releasePointerCapture(ev.pointerId);
19575
20539
  // Reset cursor to default when all pointers are released
19576
20540
  if (this.core.store.state.pointers.size === 0) {
19577
- this.core.store.state.cursor = { icon: 'default', iconActive: 'default' };
20541
+ this.core.cursorManager.resetToDefault();
19578
20542
  }
19579
20543
  this.viewport.handlePointerUp(ev);
19580
20544
  this.core.store.state?.activeTool?.handlePointerUp(ev);
@@ -19587,7 +20551,7 @@ const KritzelEngine = class {
19587
20551
  this.core.store.state.pointers.delete(ev.pointerId);
19588
20552
  // Reset cursor to default when all pointers are released
19589
20553
  if (this.core.store.state.pointers.size === 0) {
19590
- this.core.store.state.cursor = { icon: 'default', iconActive: 'default' };
20554
+ this.core.cursorManager.resetToDefault();
19591
20555
  }
19592
20556
  this.viewport.handlePointerUp(ev);
19593
20557
  this.core.store.state?.activeTool?.handlePointerUp(ev);
@@ -19763,7 +20727,6 @@ const KritzelEngine = class {
19763
20727
  contextMenuHandler;
19764
20728
  keyHandler;
19765
20729
  contextMenuElement = null;
19766
- cursorTargetElement = null;
19767
20730
  get isSelecting() {
19768
20731
  return this.core.store.state.activeTool instanceof KritzelSelectionTool && this.core.store.state.isSelecting;
19769
20732
  }
@@ -19783,12 +20746,7 @@ const KritzelEngine = class {
19783
20746
  disconnectedCallback() {
19784
20747
  this.throttledWheel.cancel();
19785
20748
  this.throttledPointerMoveMulti.cancel();
19786
- // Reset cursor on target element
19787
- if (this.cursorTargetElement) {
19788
- this.cursorTargetElement.style.cursor = '';
19789
- this.cursorTargetElement.style.removeProperty('--kritzel-pointer-cursor');
19790
- this.cursorTargetElement = null;
19791
- }
20749
+ this.core.cursorManager.cleanup();
19792
20750
  }
19793
20751
  componentWillLoad() {
19794
20752
  this.validateScaleMax(this.scaleMax);
@@ -19798,10 +20756,9 @@ const KritzelEngine = class {
19798
20756
  this.contextMenuHandler = new KritzelContextMenuHandler(this.core, this.globalContextMenuItems, this.objectContextMenuItems);
19799
20757
  this.keyHandler = new KritzelKeyHandler(this.core);
19800
20758
  this.viewport = new KritzelViewport(this.core, this.host);
19801
- // Set cursor target element (use prop value or default to document.body)
19802
- this.cursorTargetElement = this.cursorTarget || document.body;
19803
- // Set the pointer cursor CSS variable for child components to use
19804
- this.cursorTargetElement.style.setProperty('--kritzel-pointer-cursor', KritzelCursorHelper.getPointerCursor());
20759
+ // Initialize cursor manager with target element and shadow root
20760
+ this.core.cursorManager.setTargetElement(this.cursorTarget || document.body);
20761
+ this.core.cursorManager.setShadowRoot(this.host.shadowRoot);
19805
20762
  // Set sync configuration if provided
19806
20763
  if (this.syncConfig) {
19807
20764
  this.core.setSyncConfig(this.syncConfig);
@@ -19843,20 +20800,6 @@ const KritzelEngine = class {
19843
20800
  KritzelKeyboardHelper.forceHideKeyboard();
19844
20801
  this.core.rerender();
19845
20802
  }
19846
- updateCursor() {
19847
- const state = this.core.store.state;
19848
- const isPointerDown = this.core.store.isPointerDown;
19849
- const icon = state.cursor?.icon;
19850
- const iconActive = state.cursor?.iconActive ?? icon;
19851
- const rotation = state.cursor?.rotation;
19852
- const cursor = KritzelCursorHelper.getCursor({
19853
- iconName: isPointerDown ? iconActive : icon,
19854
- rotation: rotation,
19855
- });
19856
- if (this.cursorTargetElement) {
19857
- this.cursorTargetElement.style.cursor = cursor;
19858
- }
19859
- }
19860
20803
  render() {
19861
20804
  const computedStyle = window.getComputedStyle(this.host);
19862
20805
  const baseHandleSizePx = computedStyle.getPropertyValue('--kritzel-selection-handle-size').trim() || '6px';
@@ -19872,18 +20815,18 @@ const KritzelEngine = class {
19872
20815
  height: this.core.store.state.viewportHeight / this.core.store.state.scale,
19873
20816
  depth: 100,
19874
20817
  };
19875
- const visibleObjects = this.core.store.state.objects.query(viewportBounds);
19876
- this.updateCursor();
19877
- return (h(Host, { key: '192c356c5476b2b3cf370b05efd5742776423200' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'fae1b053c1eda01726f6b583a8dd167bb1c34aa1', class: "debug-panel" }, h("div", { key: '0e0fab7c39c1c8116831cf7b228914b0c40fb338' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '770d284f5104919c5c6a36e653971d3662e9428b' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '4937256ccf074a0a2fffb9c8c32a8c6ba41d3fb4' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: '447d95aed0dda6e212e7c67ff4bc0a2e574131fd' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '548a04b16f68873e5fc0a2767146b52e83b9f0f8' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: '16a92abf89f1b438f14f311dd248ae82e1ac1982' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: '92a2abc5a757c64d764f3e1ee525556eb6a88852' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '7677d4fd149ef2507626e50502d7c2b74fb294af' }, "Scale: ", this.core.store.state?.scale), h("div", { key: '3120c7ff85fd9f8ef14fb313b9aae6f2fc817f75' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: '100cd8a8c221d66046d9ff869b23809ca2568d12' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '2efec039f635eec81d172113d55284eb9002f339' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: 'f1df2fbf86a1944831c26a23e8c0baadd5ba1c56' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '10098cd2ffd07200fc8a12941ffd91fc1fac4de8' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '75043ccdac3ef3b02dae17b758813132174c2034' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: '59217b67d572cc60fc8381bcc930a952fea2faaa' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: 'c4a7d64031d3b430673adb6adcb7f525f7a47ddc' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: 'd62bef9cab424e77781a3d5639552b8122e92238' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: 'ab5dbbc947b335a1396a945328a5939d690d8cd0' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: 'fb2c19a0cdc070ecbf28e51b08112a73ffcf2817' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '2d5519541aeb9a6423524463a03d1626f5eaf6ca' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: 'f86b578263c68ac194d6b3d606a1cb31717b1792' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '52ed5379ef3e9a18cbabe14be8106055a8f71ede' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: '6b97e5f6c90666283fb5cfed0cf0e3b7bc0fffe3' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: '5f80b9d4121974ee1e8afe89925b6b38b840e90f' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: '8a280b4454677f5dc29e4a2a39fd4f7cfc8c26df' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: '660dd95852906f5c049b9527b26ea0af3fa04274', id: "origin", class: "origin", style: {
20818
+ const visibleObjects = this.core.store.state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
20819
+ this.core.cursorManager.applyCursor();
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: {
19878
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})`,
19879
20822
  } }, visibleObjects?.map(object => {
19880
20823
  return (h("div", { key: object.id, style: {
19881
20824
  transform: object?.transformationMatrix,
19882
20825
  transformOrigin: 'top left',
19883
- zIndex: object.zIndex.toString(),
19884
20826
  position: 'absolute',
19885
20827
  pointerEvents: this.core.store.state.isScaling ? 'none' : 'auto',
19886
20828
  } }, h("svg", { xmlns: "http://www.w3.org/2000/svg", id: object.id, class: "object", style: {
20829
+ zIndex: object.zIndex.toString(),
19887
20830
  height: object?.totalHeight.toString(),
19888
20831
  width: object?.totalWidth.toString(),
19889
20832
  left: '0',
@@ -19893,7 +20836,7 @@ const KritzelEngine = class {
19893
20836
  transformOrigin: `${object.totalWidth / 2}px ${object.totalHeight / 2}px`,
19894
20837
  opacity: object.markedForRemoval ? '0.5' : object.opacity.toString(),
19895
20838
  pointerEvents: object.markedForRemoval ? 'none' : 'auto',
19896
- } }, h("foreignObject", { x: "0", y: "0", width: object.totalWidth.toString(), height: object.totalHeight.toString(), style: {
20839
+ } }, KritzelClassHelper.isInstanceOf(object, 'KritzelPath') && (h("svg", { ref: el => object.mount(el), xmlns: "http://www.w3.org/2000/svg", style: { overflow: 'visible' }, viewBox: object?.viewBox }, h("path", { d: object?.d, fill: object.fill, stroke: object?.stroke, "shape-rendering": object.isLowRes() ? 'optimizeSpeed' : 'auto' }))), KritzelClassHelper.isInstanceOf(object, 'KritzelLine') && (h("svg", { ref: el => object.mount(el), xmlns: "http://www.w3.org/2000/svg", style: { overflow: 'visible' }, viewBox: object?.viewBox }, h("defs", null, object.hasStartArrow && (h("marker", { id: object.startMarkerId, markerWidth: object.getArrowSize('start'), markerHeight: object.getArrowSize('start'), refX: 0, refY: object.getArrowSize('start') / 2, orient: "auto-start-reverse", markerUnits: "userSpaceOnUse" }, h("path", { d: object.getArrowPath(object.arrows?.start?.style), fill: object.getArrowFill('start'), transform: `scale(${object.getArrowSize('start') / 10})` }))), object.hasEndArrow && (h("marker", { id: object.endMarkerId, markerWidth: object.getArrowSize('end'), markerHeight: object.getArrowSize('end'), refX: 0, refY: object.getArrowSize('end') / 2, orient: "auto", markerUnits: "userSpaceOnUse" }, h("path", { d: object.getArrowPath(object.arrows?.end?.style), fill: object.getArrowFill('end'), transform: `scale(${object.getArrowSize('end') / 10})` })))), h("path", { d: this.core.anchorManager.computeClippedLinePath(object), fill: "none", stroke: object?.stroke, "stroke-width": object?.strokeWidth, "stroke-linecap": "round", "marker-start": object.hasStartArrow ? `url(#${object.startMarkerId})` : undefined, "marker-end": object.hasEndArrow ? `url(#${object.endMarkerId})` : undefined }))), h("foreignObject", { x: "0", y: "0", width: object.totalWidth.toString(), height: object.totalHeight.toString(), style: {
19897
20840
  minHeight: '0',
19898
20841
  minWidth: '0',
19899
20842
  backgroundColor: object.backgroundColor,
@@ -19902,18 +20845,14 @@ const KritzelEngine = class {
19902
20845
  borderStyle: 'solid',
19903
20846
  padding: object.padding + 'px',
19904
20847
  overflow: 'visible',
19905
- } }, KritzelClassHelper.isInstanceOf(object, 'KritzelPath') && (h("svg", { ref: el => object.mount(el), xmlns: "http://www.w3.org/2000/svg", style: {
19906
- height: object?.height.toString(),
19907
- width: object?.width.toString(),
19908
- position: 'absolute',
19909
- overflow: 'visible',
19910
- }, viewBox: object?.viewBox }, h("path", { d: object?.d, fill: object.fill, stroke: object?.stroke, "shape-rendering": object.isLowRes() ? 'optimizeSpeed' : 'auto' }))), KritzelClassHelper.isInstanceOf(object, 'KritzelImage') && (h("img", { ref: el => object.mount(el), src: object.src, style: {
20848
+ display: KritzelClassHelper.isInstanceOf(object, 'KritzelLine') || KritzelClassHelper.isInstanceOf(object, 'KritzelPath') ? 'none' : 'block'
20849
+ } }, KritzelClassHelper.isInstanceOf(object, 'KritzelImage') && (h("img", { ref: el => object.mount(el), src: object.src, style: {
19911
20850
  width: '100%',
19912
20851
  height: '100%',
19913
20852
  userSelect: 'none',
19914
20853
  pointerEvents: 'none',
19915
20854
  imageRendering: this.core.store.state.isScaling || this.core.store.state.isPanning ? 'pixelated' : 'auto',
19916
- }, draggable: false, onDragStart: e => e.preventDefault() })), KritzelClassHelper.isInstanceOf(object, 'KritzelText') && (h("div", { ref: el => object.mount(el), onPointerDown: e => object.handlePointerDown(e), onPointerMove: e => object.handlePointerMove(e), onPointerUp: e => object.handlePointerUp(e), style: {
20855
+ }, draggable: false, onDragStart: e => e.preventDefault() })), KritzelClassHelper.isInstanceOf(object, 'KritzelText') && (h("div", { id: "text-object", ref: el => object.mount(el), onPointerDown: e => object.handlePointerDown(e), onPointerMove: e => object.handlePointerMove(e), onPointerUp: e => object.handlePointerUp(e), style: {
19917
20856
  minWidth: object.initialWidth + 'px',
19918
20857
  minHeight: object.initialHeight + 'px',
19919
20858
  maxWidth: '500px',
@@ -19924,13 +20863,38 @@ const KritzelEngine = class {
19924
20863
  backgroundColor: object.backgroundColor,
19925
20864
  overflow: 'visible',
19926
20865
  textRendering: this.core.store.state.isScaling || this.core.store.state.isPanning ? 'optimizeSpeed' : 'auto',
19927
- } })), 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: {
19928
20892
  width: '100%',
19929
20893
  height: '100%',
19930
20894
  pointerEvents: 'auto',
19931
20895
  overflow: 'hidden',
19932
20896
  display: 'block',
19933
- } })), KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && (h("div", { ref: el => object.mount(el), style: {
20897
+ } })), KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionGroup') && !this.core.displaySelectionLineUI(object) && (h("div", { ref: el => object.mount(el), style: {
19934
20898
  width: '100%',
19935
20899
  height: '100%',
19936
20900
  } })), KritzelClassHelper.isInstanceOf(object, 'KritzelSelectionBox') && (h("div", { ref: el => object.mount(el), style: {
@@ -19940,7 +20904,14 @@ const KritzelEngine = class {
19940
20904
  borderWidth: KritzelDevicesHelper.isFirefox() ? object.borderWidth + 'px' : '0',
19941
20905
  borderStyle: KritzelDevicesHelper.isFirefox() ? 'solid' : 'none',
19942
20906
  borderColor: KritzelDevicesHelper.isFirefox() ? object.borderColor : 'transparent',
19943
- } }))), 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("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: {
20914
+ zIndex: (object.zIndex + 1).toString(),
19944
20915
  height: object?.totalHeight.toString(),
19945
20916
  width: object?.totalWidth.toString(),
19946
20917
  left: '0',
@@ -19950,7 +20921,7 @@ const KritzelEngine = class {
19950
20921
  transformOrigin: `${object.totalWidth / 2}px ${object.totalHeight / 2}px`,
19951
20922
  overflow: 'visible',
19952
20923
  pointerEvents: 'none',
19953
- } }, object.isSelected && (h("g", { class: "selection-borders", style: { pointerEvents: 'none' } }, h("line", { x1: "0", y1: "0", x2: object.totalWidth, y2: "0", style: {
20924
+ } }, this.core.displaySelectionGroupUI(object) && (h("g", { class: "selection-group-ui", style: { pointerEvents: 'none' } }, h("g", { class: "selection-group-borders" }, h("line", { x1: "0", y1: "0", x2: object.totalWidth, y2: "0", style: {
19954
20925
  stroke: 'var(--kritzel-selection-border-color, #007AFF)',
19955
20926
  strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
19956
20927
  strokeLinecap: 'square',
@@ -19966,31 +20937,31 @@ const KritzelEngine = class {
19966
20937
  stroke: 'var(--kritzel-selection-border-color, #007AFF)',
19967
20938
  strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
19968
20939
  strokeLinecap: 'square',
19969
- } }))), object.isSelected && !this.isSelecting && (h("g", { class: "selection-handles", style: { pointerEvents: 'auto' } }, h("circle", { class: "resize-handle top-left", cx: "0", cy: "0", r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20940
+ } })), !this.isSelecting && (h("g", { class: "selection-group-handles", style: { pointerEvents: 'auto' } }, h("circle", { class: "resize-handle top-left", cx: "0", cy: "0", r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
19970
20941
  fill: 'var(--kritzel-selection-handle-color, #000000)',
19971
20942
  paintOrder: 'fill',
19972
20943
  } }), h("circle", { class: "resize-handle-overlay top-left", cx: "0", cy: "0", r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
19973
20944
  fill: 'transparent',
19974
20945
  paintOrder: 'fill',
19975
- }, onPointerEnter: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'move-vertical', rotation: object.rotationDegrees - 45 }), onPointerLeave: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'default', iconActive: 'default' }) }), h("circle", { class: "resize-handle top-right", cx: object.totalWidth, cy: "0", r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20946
+ } }), h("circle", { class: "resize-handle top-right", cx: object.totalWidth, cy: "0", r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
19976
20947
  fill: 'var(--kritzel-selection-handle-color, #000000)',
19977
20948
  paintOrder: 'fill',
19978
20949
  } }), h("circle", { class: "resize-handle-overlay top-right", cx: object.totalWidth, cy: "0", r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
19979
20950
  fill: 'transparent',
19980
20951
  paintOrder: 'fill',
19981
- }, onPointerEnter: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'move-vertical', rotation: object.rotationDegrees + 45 }), onPointerLeave: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'default', iconActive: 'default' }) }), h("circle", { class: "resize-handle bottom-left", cx: "0", cy: object.totalHeight, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20952
+ } }), h("circle", { class: "resize-handle bottom-left", cx: "0", cy: object.totalHeight, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
19982
20953
  fill: 'var(--kritzel-selection-handle-color, #000000)',
19983
20954
  paintOrder: 'fill',
19984
20955
  } }), h("circle", { class: "resize-handle-overlay bottom-left", cx: "0", cy: object.totalHeight, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
19985
20956
  fill: 'transparent',
19986
20957
  paintOrder: 'fill',
19987
- }, onPointerEnter: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'move-vertical', rotation: object.rotationDegrees + 45 }), onPointerLeave: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'default', iconActive: 'default' }) }), h("circle", { class: "resize-handle bottom-right", cx: object.totalWidth, cy: object.totalHeight, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20958
+ } }), h("circle", { class: "resize-handle bottom-right", cx: object.totalWidth, cy: object.totalHeight, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
19988
20959
  fill: 'var(--kritzel-selection-handle-color, #000000)',
19989
20960
  paintOrder: 'fill',
19990
20961
  } }), h("circle", { class: "resize-handle-overlay bottom-right", cx: object.totalWidth, cy: object.totalHeight, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
19991
20962
  fill: 'transparent',
19992
20963
  paintOrder: 'fill',
19993
- }, onPointerEnter: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'move-vertical', rotation: object.rotationDegrees - 45 }), onPointerLeave: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'default', iconActive: 'default' }) }), h("line", { x1: object.totalWidth / 2, y1: "0", x2: object.totalWidth / 2, y2: -((15 * object.scale) / this.core.store.state?.scale), style: {
20964
+ } }), h("line", { x1: object.totalWidth / 2, y1: "0", x2: object.totalWidth / 2, y2: -((15 * object.scale) / this.core.store.state?.scale), style: {
19994
20965
  stroke: 'var(--kritzel-selection-border-color, #007AFF)',
19995
20966
  strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
19996
20967
  } }), h("circle", { class: "rotation-handle", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
@@ -19999,8 +20970,103 @@ const KritzelEngine = class {
19999
20970
  } }), h("circle", { class: "rotation-handle-overlay", cx: object.totalWidth / 2, cy: -((15 * object.scale) / this.core.store.state?.scale), r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
20000
20971
  fill: 'transparent',
20001
20972
  paintOrder: 'fill',
20002
- }, onPointerEnter: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'hand', iconActive: 'hand-grab' }), onPointerLeave: () => !this.core.store.isPointerDown && (this.core.store.state.cursor = { icon: 'default', iconActive: 'default' }) }))))));
20003
- })), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '0da13416f8f2cf2f0eb6fcbb8eacb942dda0efb5', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
20973
+ } }))))), this.core.displaySelectionLineUI(object) && KritzelClassHelper.isInstanceOf(object, 'KritzelLine') && (h("g", { class: "selection-line-ui", style: { pointerEvents: 'none' } }, h("g", { class: "selection-line-borders" }, h("path", { class: "selection-line-border", d: this.core.anchorManager.computeClippedLinePath(object, true), style: {
20974
+ stroke: 'var(--kritzel-selection-border-color, #007AFF)',
20975
+ strokeWidth: `calc(var(--kritzel-selection-border-width, 2px) * ${object.scale} / ${this.core.store.state?.scale})`,
20976
+ strokeLinecap: 'round',
20977
+ fill: 'none',
20978
+ } })), !this.isSelecting && (h("g", { class: "selection-line-handles", style: { pointerEvents: 'auto' } }, h("circle", { class: "selection-line-handle start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20979
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
20980
+ paintOrder: 'fill',
20981
+ } }), h("circle", { class: "selection-line-handle-overlay start", cx: object.startX - object.x, cy: object.startY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
20982
+ fill: 'transparent',
20983
+ paintOrder: 'fill',
20984
+ } }), h("circle", { class: "selection-line-handle center", cx: object.controlX !== undefined ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y : (object.startY - object.y + object.endY - object.y) / 2, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20985
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
20986
+ paintOrder: 'fill',
20987
+ } }), h("circle", { class: "selection-line-handle-overlay center", cx: object.controlX !== undefined ? (object.startX + 2 * object.controlX + object.endX) / 4 - object.x : (object.startX - object.x + object.endX - object.x) / 2, cy: object.controlY !== undefined ? (object.startY + 2 * object.controlY + object.endY) / 4 - object.y : (object.startY - object.y + object.endY - object.y) / 2, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
20988
+ fill: 'transparent',
20989
+ paintOrder: 'fill',
20990
+ } }), h("circle", { class: "selection-line-handle end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${(baseHandleSize * object.scale) / this.core.store.state?.scale}`, style: {
20991
+ fill: 'var(--kritzel-selection-handle-color, #000000)',
20992
+ paintOrder: 'fill',
20993
+ } }), h("circle", { class: "selection-line-handle-overlay end", cx: object.endX - object.x, cy: object.endY - object.y, r: `${(baseHandleTouchSize * object.scale) / this.core.store.state?.scale}`, style: {
20994
+ fill: 'transparent',
20995
+ paintOrder: 'fill',
20996
+ } }))))))));
20997
+ }), (() => {
20998
+ const data = this.core.anchorManager.getAnchorLinesRenderData();
20999
+ if (!data)
21000
+ return null;
21001
+ return (h("svg", { xmlns: "http://www.w3.org/2000/svg", class: "anchor-lines", style: {
21002
+ position: 'absolute',
21003
+ left: '0',
21004
+ top: '0',
21005
+ width: '1px',
21006
+ height: '1px',
21007
+ pointerEvents: 'none',
21008
+ zIndex: '9998',
21009
+ overflow: 'visible',
21010
+ } }, data.startAnchorViz && (h("g", { class: "anchor-line-start" }, data.startAnchorViz.pathD ? (h("path", { d: data.startAnchorViz.pathD, style: {
21011
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.3))',
21012
+ strokeWidth: `${data.lineStrokeWidth}`,
21013
+ strokeDasharray: data.dashArray,
21014
+ strokeLinecap: 'round',
21015
+ fill: 'none',
21016
+ } })) : (h("line", { x1: data.startAnchorViz.edgeX, y1: data.startAnchorViz.edgeY, x2: data.startAnchorViz.centerX, y2: data.startAnchorViz.centerY, style: {
21017
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.3))',
21018
+ strokeWidth: `${data.lineStrokeWidth}`,
21019
+ strokeDasharray: data.dashArray,
21020
+ strokeLinecap: 'round',
21021
+ } })), h("circle", { cx: data.startAnchorViz.centerX, cy: data.startAnchorViz.centerY, r: data.indicatorRadius, style: {
21022
+ fill: 'var(--kritzel-snap-indicator-fill, rgba(0, 0, 0))',
21023
+ stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
21024
+ strokeWidth: data.indicatorStrokeWidth,
21025
+ } }))), data.endAnchorViz && (h("g", { class: "anchor-line-end" }, data.endAnchorViz.pathD ? (h("path", { d: data.endAnchorViz.pathD, style: {
21026
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.2))',
21027
+ strokeWidth: `${data.lineStrokeWidth}`,
21028
+ strokeDasharray: data.dashArray,
21029
+ strokeLinecap: 'round',
21030
+ fill: 'none',
21031
+ } })) : (h("line", { x1: data.endAnchorViz.edgeX, y1: data.endAnchorViz.edgeY, x2: data.endAnchorViz.centerX, y2: data.endAnchorViz.centerY, style: {
21032
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.2))',
21033
+ strokeWidth: `${data.lineStrokeWidth}`,
21034
+ strokeDasharray: data.dashArray,
21035
+ strokeLinecap: 'round',
21036
+ } })), h("circle", { cx: data.endAnchorViz.centerX, cy: data.endAnchorViz.centerY, r: data.indicatorRadius, style: {
21037
+ fill: 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))',
21038
+ stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
21039
+ strokeWidth: data.indicatorStrokeWidth,
21040
+ } })))));
21041
+ })(), (() => {
21042
+ const data = this.core.anchorManager.getSnapIndicatorRenderData();
21043
+ if (!data)
21044
+ return null;
21045
+ return (h("svg", { xmlns: "http://www.w3.org/2000/svg", class: "snap-indicator", style: {
21046
+ position: 'absolute',
21047
+ left: '0',
21048
+ top: '0',
21049
+ width: '1px',
21050
+ height: '1px',
21051
+ pointerEvents: 'none',
21052
+ zIndex: '9999',
21053
+ overflow: 'visible',
21054
+ } }, h("g", null, data.snapLinePath ? (h("path", { d: data.snapLinePath, fill: "none", style: {
21055
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.2))',
21056
+ strokeWidth: data.lineStrokeWidth,
21057
+ strokeDasharray: data.dashArray,
21058
+ strokeLinecap: 'round',
21059
+ } })) : (data.edgeX !== undefined && data.edgeY !== undefined && (h("line", { x1: data.edgeX, y1: data.edgeY, x2: data.centerX, y2: data.centerY, style: {
21060
+ stroke: 'var(--kritzel-snap-line-stroke, rgba(0, 0, 0, 0.2))',
21061
+ strokeWidth: data.lineStrokeWidth,
21062
+ strokeDasharray: data.dashArray,
21063
+ strokeLinecap: 'round',
21064
+ } }))), h("circle", { cx: data.centerX, cy: data.centerY, r: data.indicatorRadius, style: {
21065
+ fill: 'var(--kritzel-snap-indicator-fill, rgba(59, 130, 246, 0.3))',
21066
+ stroke: 'var(--kritzel-snap-indicator-stroke, #3b82f6)',
21067
+ strokeWidth: data.indicatorStrokeWidth,
21068
+ } }))));
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: {
20004
21070
  position: 'fixed',
20005
21071
  left: `${this.core.store.state.contextMenuX}px`,
20006
21072
  top: `${this.core.store.state.contextMenuY}px`,
@@ -20011,7 +21077,7 @@ const KritzelEngine = class {
20011
21077
  y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
20012
21078
  }, this.core.store.selectionGroup?.objects);
20013
21079
  this.hideContextMenu();
20014
- }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: '609dd8438a2678eaa3ce0a73858d9236c9371f96', 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 })));
20015
21081
  }
20016
21082
  static get watchers() { return {
20017
21083
  "workspace": ["onWorkspaceChange"],