kritzel-stencil 0.1.0 → 0.1.2

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 (176) hide show
  1. package/dist/cjs/{default-line-tool.config-7eJND6Jb.js → default-line-tool.config-MA02HCrH.js} +703 -133
  2. package/dist/cjs/{index-BeKMS-Zt.js → index-Bj0n7fQQ.js} +84 -7
  3. package/dist/cjs/index.cjs.js +1 -1
  4. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  5. package/dist/cjs/{kritzel-color_22.cjs.entry.js → kritzel-color_24.cjs.entry.js} +1018 -897
  6. package/dist/cjs/loader.cjs.js +2 -2
  7. package/dist/cjs/stencil.cjs.js +3 -3
  8. package/dist/collection/classes/core/core.class.js +2 -0
  9. package/dist/collection/classes/core/viewport.class.js +43 -3
  10. package/dist/collection/classes/handlers/move.handler.js +6 -0
  11. package/dist/collection/classes/objects/line.class.js +63 -15
  12. package/dist/collection/classes/objects/path.class.js +1 -0
  13. package/dist/collection/classes/objects/shape.class.js +1 -0
  14. package/dist/collection/classes/objects/text.class.js +4 -3
  15. package/dist/collection/classes/providers/indexeddb-sync-provider.class.js +0 -1
  16. package/dist/collection/classes/tools/brush-tool.class.js +5 -0
  17. package/dist/collection/classes/tools/line-tool.class.js +31 -1
  18. package/dist/collection/classes/tools/selection-tool.class.js +193 -0
  19. package/dist/collection/classes/tools/shape-tool.class.js +2 -0
  20. package/dist/collection/classes/tools/text-tool.class.js +3 -0
  21. package/dist/collection/collection-manifest.json +5 -3
  22. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +3 -2
  23. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +37 -19
  24. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +108 -36
  25. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  26. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.css +1 -1
  27. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +24 -2
  28. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  29. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.css +1 -1
  30. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.css +1 -1
  31. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  32. package/dist/collection/components/shared/kritzel-line-endings/kritzel-line-endings.css +60 -0
  33. package/dist/collection/components/shared/kritzel-line-endings/kritzel-line-endings.js +187 -0
  34. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +15 -8
  35. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +16 -9
  36. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.css +85 -0
  37. package/dist/collection/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.js +163 -0
  38. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  39. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.css +47 -0
  40. package/dist/collection/components/shared/kritzel-shape-fill/kritzel-shape-fill.js +93 -0
  41. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +13 -7
  42. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.css +11 -2
  43. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +2 -2
  44. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.css +1 -1
  45. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +6 -4
  46. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +6 -3
  47. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +66 -0
  48. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +153 -50
  49. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.css +38 -0
  50. package/dist/collection/components/ui/kritzel-tool-config/kritzel-tool-config.js +321 -0
  51. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +3 -2
  52. package/dist/collection/components/ui/kritzel-workspace-manager/kritzel-workspace-manager.js +6 -3
  53. package/dist/collection/configs/default-brush-tool.config.js +2 -52
  54. package/dist/collection/configs/default-line-tool.config.js +2 -26
  55. package/dist/collection/configs/default-shape-tool.config.js +2 -15
  56. package/dist/collection/configs/default-text-tool.config.js +2 -26
  57. package/dist/collection/constants/color-palette.constants.js +30 -0
  58. package/dist/collection/helpers/color.helper.js +31 -0
  59. package/dist/collection/helpers/tool-config.helper.js +65 -0
  60. package/dist/collection/interfaces/tool-config.interface.js +1 -0
  61. package/dist/components/index.d.ts +8 -4
  62. package/dist/components/index.js +1 -1
  63. package/dist/components/kritzel-brush-style.js +1 -1
  64. package/dist/components/kritzel-color-palette.js +1 -1
  65. package/dist/components/kritzel-color.js +1 -1
  66. package/dist/components/kritzel-context-menu.js +1 -1
  67. package/dist/components/kritzel-controls.js +1 -1
  68. package/dist/components/kritzel-cursor-trail.js +1 -1
  69. package/dist/components/kritzel-dropdown.js +1 -1
  70. package/dist/components/kritzel-editor.js +1 -1
  71. package/dist/components/kritzel-engine.js +1 -1
  72. package/dist/components/kritzel-font-family.js +1 -1
  73. package/dist/components/kritzel-font-size.js +1 -1
  74. package/dist/components/kritzel-font.js +1 -1
  75. package/dist/components/kritzel-icon.js +1 -1
  76. package/dist/components/kritzel-line-endings.d.ts +11 -0
  77. package/dist/components/kritzel-line-endings.js +1 -0
  78. package/dist/components/kritzel-menu-item.js +1 -1
  79. package/dist/components/kritzel-menu.js +1 -1
  80. package/dist/components/kritzel-opacity-slider.d.ts +11 -0
  81. package/dist/components/kritzel-opacity-slider.js +1 -0
  82. package/dist/components/kritzel-portal.js +1 -1
  83. package/dist/components/kritzel-shape-fill.d.ts +11 -0
  84. package/dist/components/kritzel-shape-fill.js +1 -0
  85. package/dist/components/kritzel-split-button.js +1 -1
  86. package/dist/components/kritzel-stroke-size.js +1 -1
  87. package/dist/components/kritzel-tool-config.d.ts +11 -0
  88. package/dist/components/kritzel-tool-config.js +1 -0
  89. package/dist/components/kritzel-tooltip.js +1 -1
  90. package/dist/components/kritzel-utility-panel.js +1 -1
  91. package/dist/components/kritzel-workspace-manager.js +1 -1
  92. package/dist/components/p-83YX0-FS.js +1 -0
  93. package/dist/components/p-8iEiCuEN.js +1 -0
  94. package/dist/components/p-9XZbc_qK.js +1 -0
  95. package/dist/components/p-B3P64-gH.js +9 -0
  96. package/dist/components/p-B8QjTqOY.js +1 -0
  97. package/dist/components/p-BF6MdW17.js +1 -0
  98. package/dist/components/p-BVIY50lR.js +1 -0
  99. package/dist/components/p-BbqT9o1F.js +1 -0
  100. package/dist/components/{p-CXzfYQ_u.js → p-BnidlyU0.js} +1 -1
  101. package/dist/components/{p-Bj_Og27M.js → p-BxS4Pdpz.js} +1 -1
  102. package/dist/components/{p-g0N9j_uT.js → p-CCj8nmQH.js} +1 -1
  103. package/dist/components/{p-1z-ds26_.js → p-CLOnpu42.js} +1 -1
  104. package/dist/components/{p-D1tfzpy8.js → p-CSGeDE4f.js} +1 -1
  105. package/dist/components/p-CbuHMNa9.js +1 -0
  106. package/dist/components/p-ClMFs3KI.js +1 -0
  107. package/dist/components/{p-IAqZFssU.js → p-Cnpk2hfo.js} +1 -1
  108. package/dist/components/{p-Cy77SpWt.js → p-Ctv4NAxk.js} +1 -1
  109. package/dist/components/p-CyHZWbkS.js +1 -0
  110. package/dist/components/{p-C4krHoUl.js → p-D8GeJNUv.js} +1 -1
  111. package/dist/components/{p-DB5s1NY4.js → p-DKgqzi2Y.js} +1 -1
  112. package/dist/components/p-DOF5fWDU.js +1 -0
  113. package/dist/components/{p-4FEa4ADy.js → p-DV_h5Jo2.js} +1 -1
  114. package/dist/components/{p-DTezr6w9.js → p-DgCGSL2Q.js} +1 -1
  115. package/dist/components/{p-D5ZsALCP.js → p-wRXL928z.js} +1 -1
  116. package/dist/esm/{default-line-tool.config-CD5sTKH-.js → default-line-tool.config-DLpNl6R9.js} +702 -125
  117. package/dist/esm/{index-BqhmuUH2.js → index-OLdaFN6W.js} +84 -7
  118. package/dist/esm/index.js +2 -2
  119. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  120. package/dist/esm/{kritzel-color_22.entry.js → kritzel-color_24.entry.js} +1009 -890
  121. package/dist/esm/loader.js +3 -3
  122. package/dist/esm/stencil.js +4 -4
  123. package/dist/stencil/index.esm.js +1 -1
  124. package/dist/stencil/{p-09295079.entry.js → p-802bc7cf.entry.js} +1 -1
  125. package/dist/stencil/p-DLpNl6R9.js +1 -0
  126. package/dist/stencil/p-OLdaFN6W.js +2 -0
  127. package/dist/stencil/p-caf30edb.entry.js +9 -0
  128. package/dist/stencil/stencil.esm.js +1 -1
  129. package/dist/types/classes/core/viewport.class.d.ts +6 -0
  130. package/dist/types/classes/managers/anchor.manager.d.ts +1 -1
  131. package/dist/types/classes/objects/line.class.d.ts +2 -0
  132. package/dist/types/classes/objects/shape.class.d.ts +1 -0
  133. package/dist/types/classes/tools/brush-tool.class.d.ts +1 -0
  134. package/dist/types/classes/tools/line-tool.class.d.ts +2 -1
  135. package/dist/types/classes/tools/selection-tool.class.d.ts +22 -0
  136. package/dist/types/classes/tools/shape-tool.class.d.ts +1 -0
  137. package/dist/types/classes/tools/text-tool.class.d.ts +1 -0
  138. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +2 -0
  139. package/dist/types/components/shared/kritzel-color-palette/kritzel-color-palette.d.ts +1 -0
  140. package/dist/types/components/shared/kritzel-line-endings/kritzel-line-endings.d.ts +23 -0
  141. package/dist/types/components/shared/kritzel-opacity-slider/kritzel-opacity-slider.d.ts +17 -0
  142. package/dist/types/components/shared/kritzel-shape-fill/kritzel-shape-fill.d.ts +10 -0
  143. package/dist/types/components/ui/kritzel-controls/kritzel-controls.d.ts +15 -0
  144. package/dist/types/components/ui/kritzel-tool-config/kritzel-tool-config.d.ts +25 -0
  145. package/dist/types/components.d.ts +235 -82
  146. package/dist/types/constants/color-palette.constants.d.ts +5 -0
  147. package/dist/types/helpers/color.helper.d.ts +9 -0
  148. package/dist/types/helpers/tool-config.helper.d.ts +4 -0
  149. package/dist/types/interfaces/line-options.interface.d.ts +1 -0
  150. package/dist/types/interfaces/path-options.interface.d.ts +1 -0
  151. package/dist/types/interfaces/tool-config.interface.d.ts +26 -0
  152. package/dist/types/stencil-public-runtime.d.ts +29 -0
  153. package/package.json +5 -3
  154. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.css +0 -19
  155. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +0 -134
  156. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.css +0 -19
  157. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +0 -114
  158. package/dist/components/kritzel-control-brush-config.d.ts +0 -11
  159. package/dist/components/kritzel-control-brush-config.js +0 -1
  160. package/dist/components/kritzel-control-text-config.d.ts +0 -11
  161. package/dist/components/kritzel-control-text-config.js +0 -1
  162. package/dist/components/p-B7Fdo5QJ.js +0 -1
  163. package/dist/components/p-BXaWhpO2.js +0 -1
  164. package/dist/components/p-BtuXeItZ.js +0 -1
  165. package/dist/components/p-C-d2IH4v.js +0 -1
  166. package/dist/components/p-C3UriJh7.js +0 -1
  167. package/dist/components/p-CF5L2Gdl.js +0 -1
  168. package/dist/components/p-CeKT_dTd.js +0 -1
  169. package/dist/components/p-Cp15toXH.js +0 -1
  170. package/dist/components/p-D3LRBk2t.js +0 -9
  171. package/dist/components/p-Du1vxHy8.js +0 -1
  172. package/dist/stencil/p-381c0e9c.entry.js +0 -9
  173. package/dist/stencil/p-BqhmuUH2.js +0 -2
  174. package/dist/stencil/p-CD5sTKH-.js +0 -1
  175. package/dist/types/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.d.ts +0 -15
  176. package/dist/types/components/ui/kritzel-control-text-config/kritzel-control-text-config.d.ts +0 -12
@@ -1,5 +1,5 @@
1
- import { r as registerInstance, h, H as Host, c as createEvent, g as getElement } from './index-BqhmuUH2.js';
2
- import { Z as KritzelDevicesHelper, J as KritzelBrushTool, P as KritzelTextTool, _ as KritzelMouseButton, $ as KritzelBaseObject, a0 as Schema, a1 as schema, a2 as addListNodes, a3 as EditorView, a4 as EditorState, a5 as keymap, a6 as TextSelection, a7 as KritzelKeyboardHelper, a8 as KritzelToolRegistry, a9 as KritzelGeometryHelper, aa as baseKeymap, ab as KritzelBaseTool, ac as KritzelEventHelper, R as KritzelSelectionTool, W as DEFAULT_BRUSH_CONFIG, Y as DEFAULT_LINE_TOOL_CONFIG, L as KritzelLineTool, M as KritzelEraserTool, X as DEFAULT_TEXT_CONFIG, N as KritzelImageTool, U as KritzelWorkspace, ad as KritzelIconRegistry, ae as KritzelBaseHandler, af as KritzelSelectionBox, ag as KritzelSelectionGroup, I as KritzelGroup, F as KritzelImage, K as KritzelText, G as KritzelLine, E as KritzelPath, ah as Doc, ai as DEFAULT_SYNC_CONFIG, aj as UndoManager, Q as KritzelCursorHelper, T as KritzelAppStateMap, V as KritzelAnchorManager, ak as ObjectHelper, al as KritzelClassHelper } from './default-line-tool.config-CD5sTKH-.js';
1
+ import { r as registerInstance, h, H as Host, c as createEvent, g as getElement } from './index-OLdaFN6W.js';
2
+ import { Z as KritzelBaseTool, _ as ShapeType, $ as KritzelShape, a0 as KritzelToolRegistry, a1 as KritzelEventHelper, R as KritzelSelectionTool, J as KritzelBrushTool, L as KritzelLineTool, P as KritzelTextTool, a2 as KritzelDevicesHelper, a3 as KritzelMouseButton, a4 as DEFAULT_COLOR_PALETTE, W as DEFAULT_BRUSH_CONFIG, Y as DEFAULT_LINE_TOOL_CONFIG, M as KritzelEraserTool, X as DEFAULT_TEXT_CONFIG, N as KritzelImageTool, U as KritzelWorkspace, a5 as KritzelIconRegistry, a6 as KritzelKeyboardHelper, a7 as KritzelBaseHandler, a8 as KritzelSelectionBox, a9 as KritzelSelectionGroup, aa as KritzelBaseObject, I as KritzelGroup, F as KritzelImage, K as KritzelText, G as KritzelLine, E as KritzelPath, ab as Doc, ac as DEFAULT_SYNC_CONFIG, ad as UndoManager, Q as KritzelCursorHelper, T as KritzelAppStateMap, V as KritzelAnchorManager, ae as ObjectHelper, af as KritzelClassHelper } from './default-line-tool.config-DLpNl6R9.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
 
@@ -37,13 +37,13 @@ const KritzelColor = class {
37
37
  }
38
38
  render() {
39
39
  const isColorVeryLight = this.isLightColor(this.value);
40
- return (h(Host, { key: 'edf5d94f476d72d0be74886b362fd37db789c517' }, h("div", { key: 'd8af2373616b9bc6f4ddea0822d737f073563bf9', class: "checkerboard-bg", style: {
40
+ return (h(Host, { key: '5d9fa3dd0fa30dd8e6589459efafd63a71c317bb' }, h("div", { key: 'fe35227ea4636234de62b7eec42f11a58ca788bb', class: "checkerboard-bg", style: {
41
41
  width: `${this.size}px`,
42
42
  height: `${this.size}px`,
43
43
  borderRadius: '50%',
44
44
  display: 'inline-block',
45
45
  position: 'relative',
46
- } }, h("div", { key: '6b587f7926884e7c38fe36eaef41b0c8cd213c6d', class: {
46
+ } }, h("div", { key: '37f99d30a6f238e9e1f9976d2419364d2f83bbd8', class: {
47
47
  'color-circle': true,
48
48
  'white': isColorVeryLight,
49
49
  }, style: {
@@ -60,7 +60,39 @@ const KritzelColor = class {
60
60
  };
61
61
  KritzelColor.style = kritzelColorCss();
62
62
 
63
- const kritzelColorPaletteCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:8px;width:100%;box-sizing:border-box}.color-grid{width:100%;display:grid;grid-template-columns:repeat(6, 32px);gap:8px;justify-items:center;overflow:hidden;height:40px;transition:height 0.1s ease-in-out}.color-grid.expanded{height:500px}.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}.color-container:hover{background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.color-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-color-palette-selected-background-color)}`;
63
+ class KritzelColorHelper {
64
+ /**
65
+ * Applies opacity to a hex color and returns an rgba string.
66
+ * @param hexColor - The hex color string (e.g., '#ff0000' or '#f00')
67
+ * @param opacity - The opacity value between 0 and 1
68
+ * @returns The color as an rgba string, or the original hex if opacity is 1
69
+ */
70
+ static applyOpacity(hexColor, opacity) {
71
+ if (opacity >= 1)
72
+ return hexColor;
73
+ const sanitizedHex = hexColor.startsWith('#') ? hexColor.slice(1) : hexColor;
74
+ let r, g, b;
75
+ if (sanitizedHex.length === 3) {
76
+ r = parseInt(sanitizedHex[0] + sanitizedHex[0], 16);
77
+ g = parseInt(sanitizedHex[1] + sanitizedHex[1], 16);
78
+ b = parseInt(sanitizedHex[2] + sanitizedHex[2], 16);
79
+ }
80
+ else if (sanitizedHex.length === 6) {
81
+ r = parseInt(sanitizedHex.substring(0, 2), 16);
82
+ g = parseInt(sanitizedHex.substring(2, 4), 16);
83
+ b = parseInt(sanitizedHex.substring(4, 6), 16);
84
+ }
85
+ else {
86
+ return hexColor;
87
+ }
88
+ if (isNaN(r) || isNaN(g) || isNaN(b)) {
89
+ return hexColor;
90
+ }
91
+ return `rgba(${r}, ${g}, ${b}, ${opacity})`;
92
+ }
93
+ }
94
+
95
+ const kritzelColorPaletteCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:0;width:100%;box-sizing:border-box}.color-grid{width:100%;display:grid;grid-template-columns:repeat(6, 32px);gap:8px;justify-items:center;overflow:hidden;height:40px;transition:height 0.1s ease-in-out}.color-grid.expanded{height:500px}.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}.color-container:hover{background-color:var(--kritzel-color-palette-hover-background-color, #ebebeb)}.color-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-color-palette-selected-background-color)}`;
64
96
 
65
97
  const KritzelColorPalette = class {
66
98
  constructor(hostRef) {
@@ -71,6 +103,7 @@ const KritzelColorPalette = class {
71
103
  selectedColor = null;
72
104
  isExpanded = false;
73
105
  isOpaque = false;
106
+ opacity = 1;
74
107
  colorChange;
75
108
  handleColorClick(color) {
76
109
  this.selectedColor = color;
@@ -92,7 +125,7 @@ const KritzelColorPalette = class {
92
125
  render() {
93
126
  const displayedColors = this.isExpanded ? this.colors : this.colors.slice(0, 6);
94
127
  const expandedHeight = this.isExpanded ? this.calculateHeight() : '32px';
95
- return (h(Host, { key: '83e3aa70f68e1bab337a031c2d00ac152f924fe1' }, h("div", { key: '86a6198500ea5bef3ed399fc8f7ec0275899eb79', class: {
128
+ return (h(Host, { key: '5d7861ab8510af002d4f0f4171a38bf4624d70cb' }, h("div", { key: 'd8b65ecf9aa36e0a158170e12aec27d112f5e0de', class: {
96
129
  'color-grid': true,
97
130
  'expanded': this.isExpanded,
98
131
  }, style: {
@@ -100,7 +133,7 @@ const KritzelColorPalette = class {
100
133
  } }, displayedColors.map(color => (h("div", { tabIndex: 0, class: {
101
134
  'color-container': true,
102
135
  'selected': this.selectedColor === color,
103
- }, onClick: () => this.handleColorClick(color), onKeyDown: event => this.handleKeyDown(event, color) }, h("kritzel-color", { value: color })))))));
136
+ }, onClick: () => this.handleColorClick(color), onKeyDown: event => this.handleKeyDown(event, color) }, h("kritzel-color", { value: KritzelColorHelper.applyOpacity(color, this.opacity) })))))));
104
137
  }
105
138
  };
106
139
  KritzelColorPalette.style = kritzelColorPaletteCss();
@@ -204,198 +237,450 @@ const KritzelContextMenu = class {
204
237
  };
205
238
  KritzelContextMenu.style = kritzelContextMenuCss();
206
239
 
207
- const kritzelControlBrushConfigCss = () => `:host{display:flex;flex-direction:column;width:100%}.expand-toggle{background:none;border:none;cursor:var(--kritzel-pointer-cursor, pointer);font-size:14px;line-height:1;padding:8px;color:var(--kritzel-color-palette-expand-toggle-color, #666666)}.expand-toggle:hover{color:var(--kritzel-color-palette-expand-toggle-hover-color, #333333)}`;
208
-
209
- const KritzelControlBrushConfig = class {
210
- constructor(hostRef) {
211
- registerInstance(this, hostRef);
212
- this.toolChange = createEvent(this, "toolChange");
213
- }
214
- tool;
215
- handleToolChange(newTool) {
216
- this.palette = newTool.palettes[newTool.type];
217
- }
218
- isExpanded = false;
219
- toolChange;
220
- palette = [];
221
- componentWillLoad() {
222
- this.palette = this.tool.palettes[this.tool.type];
223
- }
224
- handleToggleExpand() {
225
- this.isExpanded = !this.isExpanded;
226
- }
227
- handleTypeChange(event) {
228
- this.palette = this.tool.palettes[event.detail];
229
- this.tool.type = event.detail;
230
- this.tool.color = this.palette[0];
231
- this.toolChange.emit(this.tool);
232
- }
233
- handleColorChange(event) {
234
- this.tool.color = event.detail;
235
- this.toolChange.emit(this.tool);
236
- }
237
- handleSizeChange(event) {
238
- this.tool.size = event.detail;
239
- this.toolChange.emit(this.tool);
240
- }
241
- render() {
242
- return (h(Host, { key: 'ce2ac77db22f52239a45b62f690d54009f2c6954' }, h("div", { key: '5f6c50853e876c3612bb5c270bccc468c2c9a726', style: {
243
- display: 'flex',
244
- flexDirection: 'row',
245
- alignItems: this.isExpanded ? 'flex-start' : 'center',
246
- justifyContent: 'flex-start',
247
- width: '100%',
248
- gap: '8px',
249
- } }, h("kritzel-color-palette", { key: 'bfa24bb8683027c37f200ad65e4e6787d7224c56', colors: this.palette, selectedColor: this.tool.color, isExpanded: this.isExpanded, isOpaque: true, onColorChange: color => this.handleColorChange(color) }), h("button", { key: '8e2c55c6d16ca3911c5f068c354bdce5a4f7e844', class: "expand-toggle", onClick: () => this.handleToggleExpand(), title: this.isExpanded ? 'Collapse' : 'Expand', style: this.palette.length > 6 ? { visibillity: 'visible' } : { visibility: 'hidden' } }, h("kritzel-icon", { key: '33610ada109fe5d1d9cb2da4c70a60d672e3d6cb', name: this.isExpanded ? 'chevron-up' : 'chevron-down' }))), h("kritzel-stroke-size", { key: '9b095dd9436e46dd88a3485ceb2dafa5d06d1bc4', selectedSize: this.tool.size, onSizeChange: event => this.handleSizeChange(event) })));
250
- }
251
- static get watchers() { return {
252
- "tool": [{
253
- "handleToolChange": 0
254
- }]
255
- }; }
256
- };
257
- KritzelControlBrushConfig.style = kritzelControlBrushConfigCss();
258
-
259
- const kritzelControlTextConfigCss = () => `:host{display:block;flex-direction:column;width:100%}.expand-toggle{background:none;border:none;cursor:var(--kritzel-pointer-cursor, pointer);font-size:14px;line-height:1;padding:8px;color:var(--kritzel-color-palette-expand-toggle-color, #666666)}.expand-toggle:hover{color:var(--kritzel-color-palette-expand-toggle-hover-color, #333333)}`;
260
-
261
- const KritzelControlTextConfig = class {
262
- constructor(hostRef) {
263
- registerInstance(this, hostRef);
264
- this.toolChange = createEvent(this, "toolChange");
265
- }
266
- tool;
267
- isExpanded = false;
268
- toolChange;
269
- handleToggleExpand() {
270
- this.isExpanded = !this.isExpanded;
271
- }
272
- handleFamilyChange(event) {
273
- this.tool.fontFamily = event.detail;
274
- this.toolChange.emit(this.tool);
275
- }
276
- handleColorChange(event) {
277
- this.tool.fontColor = event.detail;
278
- this.toolChange.emit(this.tool);
279
- }
280
- handleSizeChange(event) {
281
- this.tool.fontSize = event.detail;
282
- this.toolChange.emit(this.tool);
283
- }
284
- render() {
285
- return (h(Host, { key: '4a0027969b183fef77c374eb114d9d30104e5ad7' }, h("div", { key: '9f49eb3a95080447b938fdfeb256fafbe5ff9ddc', style: {
286
- display: 'flex',
287
- flexDirection: 'row',
288
- alignItems: 'center',
289
- justifyContent: 'flex-start',
290
- width: '100%',
291
- gap: '8px',
292
- } }, h("kritzel-font-family", { key: '4e75cda048f8b8c04e6cb5568e7cdcde91e28112', selectedFontFamily: this.tool.fontFamily, onFontFamilyChange: event => this.handleFamilyChange(event) }), h("button", { key: '05c6ce98bd700e9e9d2e676cbcf191b943a2980e', class: "expand-toggle", onClick: () => this.handleToggleExpand(), title: this.isExpanded ? 'Collapse' : 'Expand', tabindex: "0" }, h("kritzel-icon", { key: 'ce30d48dd5ce5eefeee7702dff81ca8f96e0872d', name: this.isExpanded ? 'chevron-up' : 'chevron-down' }))), h("kritzel-color-palette", { key: 'a3d313bf01dafc15c8cf39a129d8c0c35741a3ca', colors: this.tool.palette, selectedColor: this.tool.fontColor, isExpanded: this.isExpanded, onColorChange: event => this.handleColorChange(event) }), h("kritzel-font-size", { key: '954db891b80fb244083de24dc87255b1239ff913', selectedSize: this.tool.fontSize, fontFamily: this.tool.fontFamily, onSizeChange: event => this.handleSizeChange(event) })));
293
- }
294
- };
295
- KritzelControlTextConfig.style = kritzelControlTextConfigCss();
296
-
297
- 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-content{display:flex;flex-direction:column;gap:var(--kritzel-submenu-gap, 4px);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{z-index:10001}`;
298
-
299
- const KritzelControls = class {
300
- constructor(hostRef) {
301
- registerInstance(this, hostRef);
302
- this.isControlsReady = createEvent(this, "isControlsReady");
303
- }
304
- get host() { return getElement(this); }
305
- controls = [];
306
- activeControl = null;
307
- isUtilityPanelVisible = true;
308
- undoState = null;
309
- isControlsReady;
310
- firstConfig = null;
311
- isTooltipVisible = false;
312
- isTouchDevice = KritzelDevicesHelper.isTouchDevice();
313
- selectedSubOptions = new Map();
314
- openSubMenuControl = null;
315
- handleDocumentClick(event) {
316
- const element = event.target;
317
- if (!this.kritzelEngine || element.closest('.kritzel-tooltip')) {
318
- return;
319
- }
320
- this.isTooltipVisible = false;
240
+ class KritzelShapeTool extends KritzelBaseTool {
241
+ shapeType = ShapeType.Rectangle;
242
+ fillColor = 'transparent';
243
+ strokeColor = '#000000';
244
+ strokeWidth = 4;
245
+ opacity = 1;
246
+ fontFamily = 'Arial';
247
+ fontSize = 16;
248
+ fontColor = '#000000';
249
+ palette = [
250
+ '#000000',
251
+ '#FFFFFF',
252
+ '#FF0000',
253
+ '#00FF00',
254
+ '#0000FF',
255
+ '#FFFF00',
256
+ '#FF00FF',
257
+ '#00FFFF',
258
+ '#808080',
259
+ '#C0C0C0',
260
+ '#800000',
261
+ '#008000',
262
+ '#000080',
263
+ '#808000',
264
+ '#800080',
265
+ ];
266
+ startX = 0;
267
+ startY = 0;
268
+ isDrawing = false;
269
+ currentShape = null;
270
+ constructor(core) {
271
+ super(core);
321
272
  }
322
- handleKeyDown(event) {
323
- if (event.key === 'Escape') {
273
+ handlePointerDown(event) {
274
+ if (event.cancelable) {
324
275
  event.preventDefault();
325
- this.closeTooltip();
326
- this.openSubMenuControl = null;
327
- this.kritzelEngine?.enable();
328
276
  }
329
- }
330
- async handleActiveToolChange(event) {
331
- this.activeControl = this.controls.find(control => control.tool === event.detail) || null;
332
- }
333
- async closeTooltip() {
334
- this.isTooltipVisible = false;
335
- }
336
- kritzelEngine = null;
337
- tooltipRef = null;
338
- get activeToolAsTextTool() {
339
- return this.activeControl?.tool;
340
- }
341
- get activeToolAsBrushTool() {
342
- return this.activeControl?.tool;
343
- }
344
- async componentWillLoad() {
345
- await this.initializeEngine();
346
- await this.initializeTools();
347
- this.isControlsReady.emit();
348
- }
349
- async initializeEngine() {
350
- await customElements.whenDefined('kritzel-engine');
351
- this.kritzelEngine = this.host.parentElement.querySelector('kritzel-engine');
352
- if (!this.kritzelEngine) {
353
- throw new Error('kritzel-engine not found in parent element.');
277
+ if (event.pointerType === 'mouse') {
278
+ const path = event.composedPath().slice(1);
279
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
280
+ const object = this._core.findObjectById(objectElement?.id);
281
+ const activeShape = this._core.store.activeShape;
282
+ if (activeShape === null && object instanceof KritzelShape) {
283
+ object.edit(event);
284
+ return;
285
+ }
286
+ if (activeShape !== null && object instanceof KritzelShape) {
287
+ activeShape.save();
288
+ object.edit(event);
289
+ return;
290
+ }
291
+ if (activeShape !== null && object instanceof KritzelShape === false) {
292
+ this._core.resetActiveShape();
293
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
294
+ return;
295
+ }
296
+ if (KritzelEventHelper.isLeftClick(event) === false) {
297
+ return;
298
+ }
299
+ this.startDrawing(event.clientX, event.clientY);
354
300
  }
355
- }
356
- async initializeTools() {
357
- for (const c of this.controls) {
358
- if (c.type === 'tool' && c.tool) {
359
- c.tool = await this.kritzelEngine.registerTool(c.name, c.tool, c.config);
301
+ if (event.pointerType === 'touch') {
302
+ const activePointers = Array.from(this._core.store.state.pointers.values());
303
+ const path = event.composedPath().slice(1);
304
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
305
+ const object = this._core.findObjectById(objectElement?.id);
306
+ const activeShape = this._core.store.activeShape;
307
+ if (activeShape === null && object instanceof KritzelShape) {
308
+ object.edit(event);
309
+ return;
360
310
  }
361
- if (c.type === 'tool' && c.isDefault && c.tool) {
362
- await this.kritzelEngine.changeActiveTool(c.tool);
363
- this.activeControl = c;
311
+ if (activeShape !== null && object instanceof KritzelShape) {
312
+ activeShape.save();
313
+ object.edit(event);
314
+ return;
364
315
  }
365
- if (c.type === 'config') {
366
- if (this.firstConfig === null) {
367
- this.firstConfig = c;
368
- }
369
- else {
370
- console.warn('Only one config control is allowed. The first one will be used.');
371
- }
316
+ if (activeShape !== null && object instanceof KritzelShape === false) {
317
+ this._core.resetActiveShape();
318
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
319
+ return;
320
+ }
321
+ if (activePointers.length > 1) {
322
+ return;
372
323
  }
324
+ const clientX = Math.round(activePointers[0].clientX);
325
+ const clientY = Math.round(activePointers[0].clientY);
326
+ this.startDrawing(clientX, clientY);
373
327
  }
374
328
  }
375
- async handleControlClick(control) {
376
- this.activeControl = control;
377
- if (this.activeControl.type === 'tool') {
378
- await this.kritzelEngine.changeActiveTool(this.activeControl.tool);
329
+ handlePointerMove(event) {
330
+ if (event.cancelable) {
331
+ event.preventDefault();
379
332
  }
380
- }
381
- handleConfigClick(event) {
382
- event.stopPropagation();
383
- this.isTooltipVisible = !this.isTooltipVisible;
384
- if (this.isTooltipVisible) {
385
- this.kritzelEngine?.disable();
333
+ if (!this.isDrawing || !this.currentShape) {
334
+ return;
386
335
  }
387
- else {
388
- this.kritzelEngine?.enable();
336
+ if (event.pointerType === 'mouse') {
337
+ this.updateShapeSize(event.clientX, event.clientY);
338
+ }
339
+ if (event.pointerType === 'touch') {
340
+ const activePointers = Array.from(this._core.store.state.pointers.values());
341
+ if (activePointers.length === 1) {
342
+ const clientX = Math.round(activePointers[0].clientX);
343
+ const clientY = Math.round(activePointers[0].clientY);
344
+ this.updateShapeSize(clientX, clientY);
345
+ }
389
346
  }
390
- setTimeout(() => {
391
- this.tooltipRef?.focusContent();
392
- }, 100);
393
- }
394
- async handleToolChange(event) {
395
- this.activeControl = { ...this.activeControl, tool: event.detail };
396
- await this.kritzelEngine.changeActiveTool(this.activeControl.tool);
397
347
  }
398
- handleTooltipClosed() {
348
+ handlePointerUp(event) {
349
+ if (event.cancelable) {
350
+ event.preventDefault();
351
+ }
352
+ if (!this.isDrawing || !this.currentShape) {
353
+ return;
354
+ }
355
+ this.finishDrawing();
356
+ }
357
+ /**
358
+ * Start drawing a shape. Following the same pattern as LineTool/BrushTool:
359
+ * - Store screen coordinates for startX, startY
360
+ * - Set translateX/Y to -viewportTranslateX/Y (viewport offset)
361
+ * - Set x, y to the actual screen position
362
+ * - Let updateDimensions() convert to world coordinates
363
+ */
364
+ startDrawing(clientX, clientY) {
365
+ // Store screen coordinates (relative to host element)
366
+ this.startX = clientX - this._core.store.offsetX;
367
+ this.startY = clientY - this._core.store.offsetY;
368
+ this.isDrawing = true;
369
+ // Create shape using screen coordinates, following Path/Line pattern
370
+ this.currentShape = KritzelShape.create(this._core, {
371
+ x: this.startX,
372
+ y: this.startY,
373
+ translateX: -this._core.store.state.translateX,
374
+ translateY: -this._core.store.state.translateY,
375
+ width: 1,
376
+ height: 1,
377
+ shapeType: this.shapeType,
378
+ fillColor: this.fillColor,
379
+ strokeColor: this.strokeColor,
380
+ strokeWidth: this.strokeWidth,
381
+ opacity: this.opacity,
382
+ fontSize: this.fontSize,
383
+ fontFamily: this.fontFamily,
384
+ fontColor: this.fontColor,
385
+ });
386
+ this._core.store.state.objects.insert(this.currentShape);
387
+ this._core.rerender();
388
+ }
389
+ /**
390
+ * Update shape size during drawing. Following the same pattern as LineTool:
391
+ * - Use screen coordinates directly
392
+ * - The shape's x, y, width, height are all in screen space
393
+ * - updateDimensions() handles conversion to world coordinates
394
+ */
395
+ updateShapeSize(clientX, clientY) {
396
+ if (!this.currentShape) {
397
+ return;
398
+ }
399
+ const currentX = clientX - this._core.store.offsetX;
400
+ const currentY = clientY - this._core.store.offsetY;
401
+ // Calculate bounding box in screen coordinates
402
+ const minX = Math.min(this.startX, currentX);
403
+ const minY = Math.min(this.startY, currentY);
404
+ const width = Math.abs(currentX - this.startX);
405
+ const height = Math.abs(currentY - this.startY);
406
+ // Update shape with screen coordinates
407
+ this.currentShape.x = minX;
408
+ this.currentShape.y = minY;
409
+ this.currentShape.width = Math.max(1, width);
410
+ this.currentShape.height = Math.max(1, height);
411
+ // Recalculate world-space translateX/Y
412
+ // Reset translateX/Y to initial value before updateDimensions
413
+ this.currentShape.translateX = -this._core.store.state.translateX;
414
+ this.currentShape.translateY = -this._core.store.state.translateY;
415
+ this.currentShape.updateDimensions();
416
+ this._core.store.state.objects.update(this.currentShape);
417
+ }
418
+ finishDrawing() {
419
+ if (!this.currentShape) {
420
+ return;
421
+ }
422
+ // Remove shape if it's too small (likely an accidental click)
423
+ // Compare in screen space
424
+ if (this.currentShape.width < 10 && this.currentShape.height < 10) {
425
+ const shapeId = this.currentShape.id;
426
+ this._core.store.state.objects.remove(o => o.id === shapeId);
427
+ }
428
+ else {
429
+ this.currentShape.zIndex = this._core.store.currentZIndex;
430
+ this._core.store.state.objects.update(this.currentShape);
431
+ this._core.engine.emitObjectsChange();
432
+ this._core.selectObjects([this.currentShape]);
433
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
434
+ }
435
+ this.isDrawing = false;
436
+ this.currentShape = null;
437
+ this._core.rerender();
438
+ }
439
+ }
440
+
441
+ class KritzelToolConfigHelper {
442
+ static getToolConfig(tool) {
443
+ if (tool instanceof KritzelSelectionTool) {
444
+ return tool.getToolConfig();
445
+ }
446
+ if (tool instanceof KritzelBrushTool) {
447
+ return {
448
+ type: 'brush',
449
+ colorProperty: 'color',
450
+ sizeProperty: 'size',
451
+ opacityProperty: 'opacity',
452
+ paletteSource: 'palettes',
453
+ controls: [
454
+ { type: 'stroke-size', propertyName: 'size' },
455
+ ],
456
+ };
457
+ }
458
+ if (tool instanceof KritzelLineTool) {
459
+ return {
460
+ type: 'line',
461
+ colorProperty: 'color',
462
+ sizeProperty: 'size',
463
+ opacityProperty: 'opacity',
464
+ paletteSource: 'palette',
465
+ controls: [
466
+ { type: 'stroke-size', propertyName: 'size' },
467
+ { type: 'line-endings', propertyName: 'arrows', additionalProps: {} },
468
+ ],
469
+ };
470
+ }
471
+ if (tool instanceof KritzelShapeTool) {
472
+ return {
473
+ type: 'shape',
474
+ colorProperty: 'strokeColor',
475
+ sizeProperty: 'strokeWidth',
476
+ opacityProperty: 'opacity',
477
+ paletteSource: 'palette',
478
+ controls: [
479
+ { type: 'stroke-size', propertyName: 'strokeWidth' },
480
+ { type: 'shape-fill', propertyName: 'fillColor', additionalProps: {} },
481
+ ],
482
+ };
483
+ }
484
+ if (tool instanceof KritzelTextTool) {
485
+ return {
486
+ type: 'text',
487
+ colorProperty: 'fontColor',
488
+ sizeProperty: 'fontSize',
489
+ opacityProperty: 'opacity',
490
+ paletteSource: 'palette',
491
+ controls: [
492
+ { type: 'font-size', propertyName: 'fontSize', additionalProps: {} },
493
+ { type: 'font-family', propertyName: 'fontFamily' },
494
+ ],
495
+ };
496
+ }
497
+ // Tool is not configurable (e.g., selection, eraser, image)
498
+ return null;
499
+ }
500
+ }
501
+
502
+ const kritzelControlsCss = () => `:host{display:flex;flex-direction:column;user-select:none;max-width:calc(100vw - 28px)}: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;max-width:100%;overflow:hidden}.kritzel-tools-scroll{display:flex;flex-direction:row;align-items:center;gap:var(--kritzel-controls-gap, 8px);overflow-x:auto;overflow-y:hidden;flex:1 1 auto;min-width:0;scrollbar-width:none;-ms-overflow-style:none}.kritzel-tools-scroll::-webkit-scrollbar{display:none}.scroll-indicator-left,.scroll-indicator-right{position:absolute;top:0;bottom:0;width:24px;pointer-events:none;opacity:0;transition:opacity 0.2s ease-out;z-index:1}.scroll-indicator-left{left:0;background:linear-gradient(to right, var(--kritzel-controls-background-color, #ffffff), transparent);border-radius:var(--kritzel-controls-border-radius, 16px) 0 0 var(--kritzel-controls-border-radius, 16px)}.scroll-indicator-right{right:0;background:linear-gradient(to left, var(--kritzel-controls-background-color, #ffffff), transparent);border-radius:0 var(--kritzel-controls-border-radius, 16px) var(--kritzel-controls-border-radius, 16px) 0}.scroll-indicator-left.visible,.scroll-indicator-right.visible{opacity:1}.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-content{display:flex;flex-direction:column;gap:var(--kritzel-submenu-gap, 4px);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;flex-shrink:0;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;overflow:visible}.config-gradient-left{position:absolute;top:0;bottom:0;left:-16px;width:16px;background:linear-gradient(to right, transparent, var(--kritzel-controls-background-color, #ffffff));pointer-events:none;z-index:1}.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{z-index:10001}`;
503
+
504
+ const KritzelControls = class {
505
+ constructor(hostRef) {
506
+ registerInstance(this, hostRef);
507
+ this.isControlsReady = createEvent(this, "isControlsReady");
508
+ }
509
+ get host() { return getElement(this); }
510
+ controls = [];
511
+ activeControl = null;
512
+ isUtilityPanelVisible = true;
513
+ undoState = null;
514
+ isControlsReady;
515
+ firstConfig = null;
516
+ isTooltipVisible = false;
517
+ isTouchDevice = KritzelDevicesHelper.isTouchDevice();
518
+ selectedSubOptions = new Map();
519
+ openSubMenuControl = null;
520
+ canScrollLeft = false;
521
+ canScrollRight = false;
522
+ displayValues = null;
523
+ handleDocumentClick(event) {
524
+ if (!this.kritzelEngine) {
525
+ return;
526
+ }
527
+ // Use composedPath to check if click is inside tooltip (works across shadow DOM boundaries)
528
+ const path = event.composedPath();
529
+ const isInsideTooltip = path.some(el => {
530
+ const element = el;
531
+ if (element.tagName) {
532
+ return element.tagName.toLowerCase() === 'kritzel-tooltip' || element.classList?.contains('kritzel-tooltip');
533
+ }
534
+ return false;
535
+ });
536
+ if (isInsideTooltip) {
537
+ return;
538
+ }
539
+ this.isTooltipVisible = false;
540
+ }
541
+ handleKeyDown(event) {
542
+ if (event.key === 'Escape') {
543
+ event.preventDefault();
544
+ this.closeTooltip();
545
+ this.openSubMenuControl = null;
546
+ this.kritzelEngine?.enable();
547
+ }
548
+ }
549
+ async handleActiveToolChange(event) {
550
+ this.activeControl = this.controls.find(control => control.tool === event.detail) || null;
551
+ if (this.activeControl?.tool) {
552
+ this.updateDisplayValues(this.activeControl.tool);
553
+ }
554
+ }
555
+ handleSelectionChange() {
556
+ if (this.activeControl?.tool instanceof KritzelSelectionTool) {
557
+ this.updateDisplayValues(this.activeControl.tool);
558
+ }
559
+ }
560
+ async closeTooltip() {
561
+ this.isTooltipVisible = false;
562
+ }
563
+ kritzelEngine = null;
564
+ tooltipRef = null;
565
+ toolsScrollRef = null;
566
+ get activeToolAsTextTool() {
567
+ return this.activeControl?.tool;
568
+ }
569
+ get activeToolAsBrushTool() {
570
+ return this.activeControl?.tool;
571
+ }
572
+ get activeToolAsLineTool() {
573
+ return this.activeControl?.tool;
574
+ }
575
+ get activeToolAsShapeTool() {
576
+ return this.activeControl?.tool;
577
+ }
578
+ handleDisplayValuesChange = (event) => {
579
+ const newVal = event.detail;
580
+ if (this.displayValues &&
581
+ this.displayValues.color === newVal.color &&
582
+ this.displayValues.size === newVal.size &&
583
+ this.displayValues.fontFamily === newVal.fontFamily) {
584
+ return;
585
+ }
586
+ this.displayValues = newVal;
587
+ };
588
+ updateDisplayValues(tool) {
589
+ const config = KritzelToolConfigHelper.getToolConfig(tool);
590
+ if (!config) {
591
+ this.displayValues = null;
592
+ return;
593
+ }
594
+ const color = tool[config.colorProperty];
595
+ const opacity = tool[config.opacityProperty] ?? 1;
596
+ const size = tool[config.sizeProperty];
597
+ const displayValues = {
598
+ color: KritzelColorHelper.applyOpacity(color, opacity),
599
+ size,
600
+ };
601
+ if (tool instanceof KritzelTextTool) {
602
+ displayValues.fontFamily = tool.fontFamily;
603
+ }
604
+ // Check for equality implementation to prevent unnecessary re-renders
605
+ if (this.displayValues &&
606
+ this.displayValues.color === displayValues.color &&
607
+ this.displayValues.size === displayValues.size &&
608
+ this.displayValues.fontFamily === displayValues.fontFamily) {
609
+ return;
610
+ }
611
+ this.displayValues = displayValues;
612
+ }
613
+ async componentWillLoad() {
614
+ await this.initializeEngine();
615
+ await this.initializeTools();
616
+ this.isControlsReady.emit();
617
+ }
618
+ componentDidLoad() {
619
+ this.updateScrollIndicators();
620
+ }
621
+ updateScrollIndicators() {
622
+ if (!this.toolsScrollRef)
623
+ return;
624
+ const { scrollLeft, scrollWidth, clientWidth } = this.toolsScrollRef;
625
+ const threshold = 2; // Small threshold to account for rounding
626
+ this.canScrollLeft = scrollLeft > threshold;
627
+ this.canScrollRight = scrollLeft + clientWidth < scrollWidth - threshold;
628
+ }
629
+ handleToolsScroll = () => {
630
+ this.updateScrollIndicators();
631
+ };
632
+ async initializeEngine() {
633
+ await customElements.whenDefined('kritzel-engine');
634
+ this.kritzelEngine = this.host.parentElement.querySelector('kritzel-engine');
635
+ if (!this.kritzelEngine) {
636
+ throw new Error('kritzel-engine not found in parent element.');
637
+ }
638
+ }
639
+ async initializeTools() {
640
+ for (const c of this.controls) {
641
+ if (c.type === 'tool' && c.tool) {
642
+ c.tool = await this.kritzelEngine.registerTool(c.name, c.tool, c.config);
643
+ }
644
+ if (c.type === 'tool' && c.isDefault && c.tool) {
645
+ await this.kritzelEngine.changeActiveTool(c.tool);
646
+ this.activeControl = c;
647
+ this.updateDisplayValues(c.tool);
648
+ }
649
+ if (c.type === 'config') {
650
+ if (this.firstConfig === null) {
651
+ this.firstConfig = c;
652
+ }
653
+ else {
654
+ console.warn('Only one config control is allowed. The first one will be used.');
655
+ }
656
+ }
657
+ }
658
+ }
659
+ async handleControlClick(control) {
660
+ this.activeControl = control;
661
+ if (this.activeControl.type === 'tool') {
662
+ this.updateDisplayValues(this.activeControl.tool);
663
+ await this.kritzelEngine.changeActiveTool(this.activeControl.tool);
664
+ }
665
+ }
666
+ handleConfigClick(event) {
667
+ event.stopPropagation();
668
+ this.isTooltipVisible = !this.isTooltipVisible;
669
+ if (this.isTooltipVisible) {
670
+ this.kritzelEngine?.disable();
671
+ }
672
+ else {
673
+ this.kritzelEngine?.enable();
674
+ }
675
+ setTimeout(() => {
676
+ this.tooltipRef?.focusContent();
677
+ }, 100);
678
+ }
679
+ async handleToolChange(event) {
680
+ this.activeControl = { ...this.activeControl, tool: event.detail };
681
+ await this.kritzelEngine.changeActiveTool(this.activeControl.tool);
682
+ }
683
+ handleTooltipClosed() {
399
684
  this.isTooltipVisible = false;
400
685
  this.kritzelEngine?.enable();
401
686
  }
@@ -439,56 +724,62 @@ const KritzelControls = class {
439
724
  }
440
725
  render() {
441
726
  const hasConfigUI = this.activeControl?.tool instanceof KritzelBrushTool ||
442
- this.activeControl?.tool instanceof KritzelTextTool;
443
- return (h(Host, { key: 'ed5816cbeb8fe8b91e14b23bf61df4753fb0da0f', class: {
727
+ this.activeControl?.tool instanceof KritzelTextTool ||
728
+ this.activeControl?.tool instanceof KritzelLineTool ||
729
+ this.activeControl?.tool instanceof KritzelShapeTool ||
730
+ (this.activeControl?.tool instanceof KritzelSelectionTool && this.activeControl.tool.hasSelection());
731
+ // Separate tool controls from config control
732
+ const toolControls = this.controls.filter(c => c.type === 'tool');
733
+ const configControl = this.controls.find(c => c.type === 'config' && c.name === this.firstConfig?.name);
734
+ return (h(Host, { key: 'db2a043a2a32d10d7f27c01123da63115781941c', class: {
444
735
  mobile: this.isTouchDevice,
445
- } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '7218fff9c89f525baf655eea46aec1698a28babd', style: {
736
+ } }, this.isUtilityPanelVisible && (h("kritzel-utility-panel", { key: '09104351c9f178b8d0dbc0636cf9cc230b2bca53', style: {
446
737
  position: 'absolute',
447
738
  bottom: '56px',
448
739
  left: '12px',
449
- }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '1dc0fa7b02a953474aae9c7822c651d5006e89e5', class: "kritzel-controls" }, this.controls.map(control => {
450
- if (control.type === 'tool') {
451
- // Check if this control has sub-options (split-button)
452
- if (control.subOptions?.length) {
453
- const selectedSubOption = this.getSelectedSubOption(control);
454
- const isActive = this.activeControl?.name === control.name;
455
- const isSubMenuOpen = this.openSubMenuControl?.name === control.name;
456
- return (h("div", { class: {
457
- 'kritzel-control-split': true,
458
- 'selected': isActive,
459
- }, key: control.name, ref: el => {
460
- if (el)
461
- control._anchorRef = el;
462
- } }, h("button", { class: "kritzel-control-main", onClick: () => this.handleControlClick(control), title: selectedSubOption?.label }, h("kritzel-icon", { name: selectedSubOption?.icon || control.icon })), h("button", { class: {
463
- 'kritzel-control-dropdown': true,
464
- 'visible': isActive,
465
- }, 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 })), h("kritzel-tooltip", { isVisible: isSubMenuOpen, anchorElement: control._anchorRef, onTooltipClosed: () => { this.openSubMenuControl = null; } }, h("div", { class: "kritzel-submenu-content" }, control.subOptions.map(option => (h("button", { class: {
466
- 'kritzel-submenu-item': true,
467
- 'active': option.id === selectedSubOption?.id,
468
- }, key: option.id, onClick: () => this.selectSubOption(control, option) }, h("kritzel-icon", { name: option.icon, size: 20 }), h("span", null, option.label))))))));
469
- }
470
- // Regular tool control (no sub-options)
471
- return (h("button", { class: {
472
- 'kritzel-control': true,
473
- 'selected': this.activeControl?.name === control?.name,
474
- }, key: control.name, onClick: _event => this.handleControlClick?.(control) }, h("kritzel-icon", { name: control.icon })));
475
- }
476
- if (control.type === 'config' && control.name === this.firstConfig?.name && this.activeControl) {
740
+ }, undoState: this.undoState, onUndo: () => this.kritzelEngine?.undo(), onRedo: () => this.kritzelEngine?.redo(), onDelete: () => this.kritzelEngine?.delete() })), h("div", { key: '22f95249dddcf05a75425f96ab5381fe8a4824d4', class: "kritzel-controls" }, h("div", { key: 'fcca99e2a56f401b28205b8806497c46b7f9a94b', class: { 'scroll-indicator-left': true, 'visible': this.canScrollLeft } }), h("div", { key: '145802334aa83b0190a70dd2a3aca4389cb8b88c', class: "kritzel-tools-scroll", ref: el => {
741
+ this.toolsScrollRef = el;
742
+ // Update indicators when ref is set
743
+ if (el)
744
+ this.updateScrollIndicators();
745
+ }, onScroll: this.handleToolsScroll }, toolControls.map(control => {
746
+ // Check if this control has sub-options (split-button)
747
+ if (control.subOptions?.length) {
748
+ const selectedSubOption = this.getSelectedSubOption(control);
749
+ const isActive = this.activeControl?.name === control.name;
750
+ const isSubMenuOpen = this.openSubMenuControl?.name === control.name;
477
751
  return (h("div", { class: {
478
- 'kritzel-config-container': true,
479
- 'visible': hasConfigUI,
480
- }, 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 => {
481
- if (event.key === 'Enter') {
482
- this.handleConfigClick?.(event);
483
- }
484
- }, style: {
485
- cursor: 'pointer',
486
- } }, this.activeControl.tool instanceof KritzelBrushTool && (h("div", { class: "color-container" }, h("kritzel-color", { value: this.activeToolAsBrushTool?.color, size: this.activeToolAsBrushTool?.size, style: {
487
- borderRadius: '50%',
488
- border: 'none',
489
- } }))), 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 }))))));
752
+ 'kritzel-control-split': true,
753
+ 'selected': isActive,
754
+ }, key: control.name, ref: el => {
755
+ if (el)
756
+ control._anchorRef = el;
757
+ } }, h("button", { class: "kritzel-control-main", onClick: () => this.handleControlClick(control), title: selectedSubOption?.label }, h("kritzel-icon", { name: selectedSubOption?.icon || control.icon })), h("button", { class: {
758
+ 'kritzel-control-dropdown': true,
759
+ 'visible': isActive,
760
+ }, 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 })), h("kritzel-tooltip", { isVisible: isSubMenuOpen, anchorElement: control._anchorRef, onTooltipClosed: () => { this.openSubMenuControl = null; } }, h("div", { class: "kritzel-submenu-content" }, control.subOptions.map(option => (h("button", { class: {
761
+ 'kritzel-submenu-item': true,
762
+ 'active': option.id === selectedSubOption?.id,
763
+ }, key: option.id, onClick: () => this.selectSubOption(control, option) }, h("kritzel-icon", { name: option.icon, size: 20 }), h("span", null, option.label))))))));
490
764
  }
491
- }))));
765
+ // Regular tool control (no sub-options)
766
+ return (h("button", { class: {
767
+ 'kritzel-control': true,
768
+ 'selected': this.activeControl?.name === control?.name,
769
+ }, key: control.name, onClick: _event => this.handleControlClick?.(control) }, h("kritzel-icon", { name: control.icon })));
770
+ })), h("div", { key: '43e7811998855f57097b743998363fd6866bfb29', class: { 'scroll-indicator-right': true, 'visible': this.canScrollRight } }), configControl && this.activeControl && (h("div", { class: {
771
+ 'kritzel-config-container': true,
772
+ 'visible': hasConfigUI,
773
+ }, key: configControl.name }, h("div", { key: 'c1ff98f16c41d45666915b96931f23e0cf12ed6f', class: "config-gradient-left" }), h("kritzel-tooltip", { key: 'eac68125daa371d751b65ac561e33848cad3bd69', ref: el => (this.tooltipRef = el), isVisible: this.isTooltipVisible, anchorElement: this.host.shadowRoot?.querySelector('.kritzel-config-container'), onTooltipClosed: () => this.handleTooltipClosed() }, h("kritzel-tool-config", { key: '83ed025386d9d1b3ec8b52e99930645c4c35b20a', tool: this.activeControl.tool, onToolChange: event => this.handleToolChange?.(event), onDisplayValuesChange: this.handleDisplayValuesChange, style: { width: '100%', height: '100%' } })), h("div", { key: 'cf18abb832c10826685fb41794a42b8b6a6a7486', tabIndex: hasConfigUI ? 0 : -1, class: "kritzel-config", onClick: event => this.handleConfigClick?.(event), onKeyDown: event => {
774
+ if (event.key === 'Enter') {
775
+ this.handleConfigClick?.(event);
776
+ }
777
+ }, style: {
778
+ cursor: 'pointer',
779
+ } }, this.activeControl.tool instanceof KritzelTextTool && this.displayValues ? (h("div", { class: "font-container" }, h("kritzel-font", { fontFamily: this.displayValues.fontFamily, size: this.displayValues.size, color: this.displayValues.color }))) : this.displayValues && (h("div", { class: "color-container" }, h("kritzel-color", { value: this.displayValues.color, size: this.displayValues.size, style: {
780
+ borderRadius: '50%',
781
+ border: 'none',
782
+ } })))))))));
492
783
  }
493
784
  static get assetsDirs() { return ["../assets"]; }
494
785
  };
@@ -562,7 +853,7 @@ const KritzelCursorTrail = class {
562
853
  }
563
854
  }
564
855
  render() {
565
- return (h(Host, { key: '556238ffd7eeda2d400039333a37853ebfcca1b6' }, this.cursorTrailPoints.length > 1 && (h("svg", { key: 'a3f451cc9f142f018d0025eb7a68c1bdff5fc0d9', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
856
+ return (h(Host, { key: '65563f09091c0014046b65d424b4cecf89d4a407' }, this.cursorTrailPoints.length > 1 && (h("svg", { key: '9634426566a9cd69cbc63f419726af16826e45f9', class: "cursor-trail-svg", xmlns: "http://www.w3.org/2000/svg", style: {
566
857
  position: 'absolute',
567
858
  left: '0',
568
859
  top: '0',
@@ -774,629 +1065,64 @@ const KritzelDropdown = class {
774
1065
  this.hasSuffixContent = newHasContent;
775
1066
  }
776
1067
  }
777
- else if (this.hasSuffixContent !== false) {
778
- this.hasSuffixContent = false;
779
- }
780
- };
781
- evaluatePrefixContent = () => {
782
- if (this.prefixSlotElement) {
783
- const newHasContent = this.prefixSlotElement.assignedNodes({ flatten: true }).length > 0;
784
- if (this.hasPrefixContent !== newHasContent) {
785
- this.hasPrefixContent = newHasContent;
786
- }
787
- }
788
- else if (this.hasPrefixContent !== false) {
789
- this.hasPrefixContent = false;
790
- }
791
- };
792
- getSelectedLabel() {
793
- const selectedOption = this.options.find(opt => opt.value === this.internalValue);
794
- return selectedOption?.label ?? '';
795
- }
796
- getSelectedStyle() {
797
- const selectedOption = this.options.find(opt => opt.value === this.internalValue);
798
- return selectedOption?.style;
799
- }
800
- render() {
801
- const triggerClasses = {
802
- 'dropdown-trigger': true,
803
- 'has-suffix-border': this.hasSuffixContent,
804
- 'has-prefix-border': this.hasPrefixContent,
805
- 'is-open': this.isOpen,
806
- 'open-up': this.openDirection === 'up',
807
- };
808
- const menuClasses = {
809
- 'dropdown-menu': true,
810
- 'is-open': this.isOpen,
811
- 'open-up': this.openDirection === 'up',
812
- 'open-down': this.openDirection === 'down',
813
- };
814
- return (h(Host, { key: '29d076eb2ef76527c0930ab82ace0d05c896ab6c' }, h("div", { key: '1afac5cc0b7f408849670b8cb62fe0bd0f3b27f1', class: "dropdown-wrapper", ref: el => (this.wrapperElement = el) }, h("slot", { key: '3cfcf787c3b3eedbd6bf30c7506a0a4912429672', name: "prefix", ref: el => (this.prefixSlotElement = el), onSlotchange: this.evaluatePrefixContent }), h("div", { key: 'b88120ed3e44871220e6ba61e8bef457651a2086', class: "dropdown-container", style: { width: this.width } }, h("button", { key: '79082189066f3d0b4f5797420ec222847792fe52', type: "button", class: triggerClasses, style: { ...this.selectStyles, ...this.getSelectedStyle() }, onClick: this.toggleMenu, onKeyDown: this.handleTriggerKeyDown, "aria-haspopup": "listbox", "aria-expanded": this.isOpen ? 'true' : 'false', ref: el => (this.triggerElement = el) }, h("span", { key: 'a490bb6689ec1820144fe82f6833bee82e6ead0e', class: "dropdown-trigger-label" }, this.getSelectedLabel()), h("span", { key: '8bd7768f6fdd3c507bbca54d7d135adabe04901f', class: "dropdown-trigger-arrow", "aria-hidden": "true" }, h("svg", { key: 'e1b854d2c93e3067181108f8d782e1393ddb615c', xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { key: '3622cbf8964f4e50aa55be5209240dfd252f1d84', points: "6 9 12 15 18 9" }))))), h("slot", { key: 'b91317736904cbeee0f87058b822bd77c6b163ca', name: "suffix", ref: el => (this.suffixSlotElement = el), onSlotchange: this.evaluateSuffixContent }), h("ul", { key: '0ca8690345974e9b7ad184f4c7ab7cf5367b183c', class: menuClasses, role: "listbox", tabindex: "-1", onKeyDown: this.handleMenuKeyDown, ref: el => (this.menuElement = el) }, this.options.map((option, index) => {
815
- const isSelected = option.value === this.internalValue;
816
- const isFocused = index === this.focusedIndex;
817
- const optionClasses = {
818
- 'dropdown-option': true,
819
- 'is-selected': isSelected,
820
- 'is-focused': isFocused,
821
- };
822
- return (h("li", { class: optionClasses, role: "option", "aria-selected": isSelected ? 'true' : 'false', style: option.style, onClick: () => this.selectOption(option), onMouseEnter: () => this.handleOptionMouseEnter(index) }, option.label, isSelected && (h("span", { class: "dropdown-option-check", "aria-hidden": "true" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "20 6 9 17 4 12" }))))));
823
- })))));
824
- }
825
- static get watchers() { return {
826
- "options": [{
827
- "optionsChanged": 0
828
- }],
829
- "value": [{
830
- "externalValueChanged": 0
831
- }]
832
- }; }
833
- };
834
- KritzelDropdown.style = kritzelDropdownCss();
835
-
836
- var ShapeType;
837
- (function (ShapeType) {
838
- ShapeType["Rectangle"] = "rectangle";
839
- ShapeType["Ellipse"] = "ellipse";
840
- ShapeType["Triangle"] = "triangle";
841
- })(ShapeType || (ShapeType = {}));
842
-
843
- class KritzelShape extends KritzelBaseObject {
844
- __class__ = 'KritzelShape';
845
- shapeType = ShapeType.Rectangle;
846
- fillColor = 'transparent';
847
- strokeColor = '#000000';
848
- strokeWidth = 4;
849
- fontFamily = 'Arial';
850
- fontSize = 16;
851
- fontColor = '#000000';
852
- /** Screen-space x coordinate of the shape's top-left corner (like Path.x) */
853
- x = 0;
854
- /** Screen-space y coordinate of the shape's top-left corner (like Path.y) */
855
- y = 0;
856
- scale = 1;
857
- scaleFactor = 1;
858
- isDebugInfoVisible = true;
859
- isEditable = true;
860
- isEditing = false;
861
- editor = null;
862
- content = null;
863
- _schema = new Schema({
864
- nodes: addListNodes(schema.spec.nodes, 'paragraph block*', 'block'),
865
- marks: schema.spec.marks,
866
- });
867
- uneditedObject = null;
868
- /**
869
- * Returns the viewBox for the shape's SVG, using screen-space coordinates.
870
- * This follows the same pattern as KritzelPath.viewBox.
871
- */
872
- get viewBox() {
873
- return `${this.x} ${this.y} ${this.width} ${this.height}`;
874
- }
875
- constructor(config) {
876
- super();
877
- if (config) {
878
- this.x = config.x ?? 0;
879
- this.y = config.y ?? 0;
880
- this.translateX = config.translateX ?? 0;
881
- this.translateY = config.translateY ?? 0;
882
- this.width = config.width ?? 100;
883
- this.height = config.height ?? 100;
884
- this.shapeType = config.shapeType ?? ShapeType.Rectangle;
885
- this.fillColor = config.fillColor ?? 'transparent';
886
- this.strokeColor = config.strokeColor ?? '#000000';
887
- this.strokeWidth = config.strokeWidth ?? 4;
888
- this.fontSize = config.fontSize ?? 16;
889
- this.fontFamily = config.fontFamily ?? 'Arial';
890
- this.fontColor = config.fontColor ?? '#000000';
891
- this.scale = config.scale ?? 1;
892
- this.scaleFactor = config.scaleX ?? 1;
893
- }
894
- }
895
- /**
896
- * Creates a new KritzelShape with screen-space coordinates.
897
- * Following the same pattern as KritzelPath.create():
898
- * - x, y are screen-space coordinates of the shape's top-left corner
899
- * - translateX, translateY should be set to -viewportTranslateX, -viewportTranslateY
900
- * - width, height are in screen-space
901
- * - scale is the viewport scale at creation time
902
- */
903
- static create(core, config) {
904
- const object = new KritzelShape();
905
- object._core = core;
906
- object.id = object.generateId();
907
- object.workspaceId = core.store.state.activeWorkspace.id;
908
- object.x = config?.x ?? 0;
909
- object.y = config?.y ?? 0;
910
- object.translateX = config?.translateX ?? 0;
911
- object.translateY = config?.translateY ?? 0;
912
- object.width = config?.width ?? 100;
913
- object.height = config?.height ?? 100;
914
- object.shapeType = config?.shapeType ?? ShapeType.Rectangle;
915
- object.fillColor = config?.fillColor ?? 'transparent';
916
- object.strokeColor = config?.strokeColor ?? '#000000';
917
- object.strokeWidth = config?.strokeWidth ?? 4;
918
- object.fontSize = config?.fontSize ?? 16;
919
- object.fontFamily = config?.fontFamily ?? 'Arial';
920
- object.fontColor = config?.fontColor ?? '#000000';
921
- object.backgroundColor = 'transparent';
922
- object.scaleFactor = 1;
923
- object.scale = core.store.state.scale;
924
- object.zIndex = core.store.currentZIndex;
925
- object.editor = object.createEditor();
926
- // Compute world-space translateX/Y from screen-space coordinates
927
- // This follows the same pattern as KritzelPath.updateDimensions()
928
- object.updateDimensions();
929
- return object;
930
- }
931
- /**
932
- * Updates the translateX/Y to world coordinates based on screen-space x, y.
933
- * This follows the same pattern as KritzelPath.updateDimensions().
934
- *
935
- * The formula: translateX = (x + initialTranslateX) / scale
936
- * where initialTranslateX was -viewportTranslateX
937
- *
938
- * This converts screen-space position to world coordinates.
939
- */
940
- updateDimensions() {
941
- this.translateX = (this.x + this.translateX) / this.scale;
942
- this.translateY = (this.y + this.translateY) / this.scale;
943
- }
944
- mount(element) {
945
- if (element === null || this.isInViewport() === false) {
946
- return;
947
- }
948
- if (this.isMounted && this.elementRef === element && this.editor.dom.parentElement === element) {
949
- return;
950
- }
951
- this.elementRef = element;
952
- this.isMounted = true;
953
- }
954
- mountTextEditor(element) {
955
- if (element === null) {
956
- return;
957
- }
958
- if (this.editor.dom.parentElement === element) {
959
- return;
960
- }
961
- element.style.fontFamily = this.fontFamily;
962
- element.style.fontSize = `${this.fontSize}pt`;
963
- element.style.color = this.fontColor;
964
- element.style.whiteSpace = 'pre-wrap';
965
- element.style.wordWrap = 'break-word';
966
- element.innerHTML = '';
967
- element.appendChild(this.editor.dom);
968
- }
969
- createEditor() {
970
- const doc = this._schema.node('doc', null, [this._schema.node('paragraph')]);
971
- return new EditorView(null, {
972
- state: EditorState.create({
973
- doc: doc,
974
- plugins: [keymap(baseKeymap)],
975
- }),
976
- editable: () => false,
977
- dispatchTransaction: transaction => {
978
- const newState = this.editor.state.apply(transaction);
979
- this.editor.updateState(newState);
980
- if (transaction.docChanged) {
981
- this.content = newState.doc.toJSON();
982
- if (!transaction.getMeta('fromRemote')) {
983
- this._core.store.state.objects.update(this, { temporary: true });
984
- }
985
- }
986
- },
987
- });
988
- }
989
- setContent(content) {
990
- this.content = content;
991
- if (this.editor && content) {
992
- const newDoc = this.editor.state.schema.nodeFromJSON(content);
993
- const tr = this.editor.state.tr.replaceWith(0, this.editor.state.doc.content.size, newDoc.content);
994
- tr.setMeta('fromRemote', true);
995
- this.editor.dispatch(tr);
996
- }
997
- }
998
- resize(x, y, width, height) {
999
- if (width <= 1 || height <= 1) {
1000
- return;
1001
- }
1002
- this.width = width;
1003
- this.height = height;
1004
- this.translateX = x;
1005
- this.translateY = y;
1006
- this._core.store.state.objects.update(this);
1007
- }
1008
- focus(coords) {
1009
- if (this.editor) {
1010
- const doc = this.editor.state.doc;
1011
- if (coords?.x && coords?.y) {
1012
- const pos = this.editor.posAtCoords({ left: coords.x, top: coords.y });
1013
- if (pos) {
1014
- this.editor.dispatch(this.editor.state.tr.setSelection(TextSelection.create(doc, pos.pos)));
1015
- this.editor.focus();
1016
- if (KritzelDevicesHelper.isIOS()) {
1017
- this.scrollIntoViewOnIOS();
1018
- }
1019
- return;
1020
- }
1021
- }
1022
- const end = Math.max(1, doc.content.size - 1);
1023
- this.editor.dispatch(this.editor.state.tr.setSelection(TextSelection.create(doc, end)));
1024
- this.editor.focus();
1025
- if (KritzelDevicesHelper.isIOS()) {
1026
- this.scrollIntoViewOnIOS();
1027
- }
1028
- }
1029
- }
1030
- scrollIntoViewOnIOS() {
1031
- setTimeout(() => {
1032
- if (this.editor && this.editor.dom) {
1033
- this.editor.dom.scrollIntoView({
1034
- behavior: 'smooth',
1035
- block: 'center',
1036
- inline: 'nearest',
1037
- });
1038
- }
1039
- }, 300);
1040
- }
1041
- edit(event) {
1042
- KritzelKeyboardHelper.disableInteractiveWidget();
1043
- this.uneditedObject = this.clone();
1044
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('shape'));
1045
- this.editor.setProps({ editable: () => true });
1046
- this.isEditing = true;
1047
- this._core.rerender();
1048
- this.focus({ x: event?.clientX, y: event?.clientY });
1049
- KritzelKeyboardHelper.enableInteractiveWidget();
1050
- }
1051
- save() {
1052
- this.content = this.editor.state.doc.toJSON();
1053
- this.editor.setProps({ editable: () => false });
1054
- this.editor.dom.blur();
1055
- this.isEditing = false;
1056
- this._core.store.state.objects.consolidateTemporaryItems();
1057
- this._core.store.state.objects.update(this);
1058
- this._core.engine.emitObjectsChange();
1059
- }
1060
- handlePointerDown(event) {
1061
- if (!this.isEditing) {
1062
- return;
1063
- }
1064
- event.stopPropagation();
1065
- }
1066
- handlePointerMove(event) {
1067
- if (!this.isEditing) {
1068
- return;
1069
- }
1070
- event.stopPropagation();
1071
- }
1072
- handlePointerUp(event) {
1073
- if (!this.isEditing) {
1074
- return;
1075
- }
1076
- event.stopPropagation();
1077
- }
1078
- copy() {
1079
- const copiedObject = super.copy();
1080
- copiedObject.editor = copiedObject.createEditor();
1081
- if (this.content) {
1082
- copiedObject.setContent(this.content);
1083
- }
1084
- return copiedObject;
1085
- }
1086
- serialize() {
1087
- const { _core, _elementRef, _schema, element, totalWidth, totalHeight, editor, uneditedObject, ...remainingProps } = this;
1088
- const clonedProps = structuredClone(remainingProps);
1089
- if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
1090
- clonedProps.element = element.cloneNode(true);
1091
- }
1092
- return clonedProps;
1093
- }
1094
- deserialize(object) {
1095
- super.deserialize(object);
1096
- if (object.content) {
1097
- this.setContent(object.content);
1098
- }
1099
- return this;
1100
- }
1101
- /**
1102
- * Returns the clipping polygon for arrow intersection.
1103
- * For ellipse: returns a many-sided polygon approximation
1104
- * For triangle: returns the 3 corners
1105
- * For rectangle: returns null (uses default rotatedPolygon)
1106
- *
1107
- * Includes padding for half the stroke width so arrow heads don't overlap the stroke.
1108
- */
1109
- getClipPolygon() {
1110
- // Calculate world-space center and dimensions
1111
- const worldWidth = this.totalWidth / this.scale;
1112
- const worldHeight = this.totalHeight / this.scale;
1113
- const centerX = this.translateX + worldWidth / 2;
1114
- const centerY = this.translateY + worldHeight / 2;
1115
- // Add padding for stroke width so arrows don't overlap the stroke
1116
- const strokePadding = (this.strokeWidth / this.scale) / 2;
1117
- switch (this.shapeType) {
1118
- case ShapeType.Ellipse:
1119
- // Return a 32-segment polygon approximation of the ellipse
1120
- // Add stroke padding to radii
1121
- return KritzelGeometryHelper.getEllipsePolygonApproximation(centerX, centerY, worldWidth / 2 + strokePadding, worldHeight / 2 + strokePadding, 32, this.rotation);
1122
- case ShapeType.Triangle:
1123
- // Return the 3 corners of the triangle in world coordinates
1124
- // Triangle: top-center, bottom-right, bottom-left
1125
- // Expand each vertex outward from center by strokePadding
1126
- const expandVertex = (vx, vy) => {
1127
- const dx = vx - centerX;
1128
- const dy = vy - centerY;
1129
- const dist = Math.sqrt(dx * dx + dy * dy);
1130
- if (dist === 0)
1131
- return { x: vx, y: vy };
1132
- const scale = (dist + strokePadding) / dist;
1133
- return {
1134
- x: centerX + dx * scale,
1135
- y: centerY + dy * scale
1136
- };
1137
- };
1138
- const topX = this.translateX + worldWidth / 2;
1139
- const topY = this.translateY;
1140
- const bottomLeftX = this.translateX;
1141
- const bottomLeftY = this.translateY + worldHeight;
1142
- const bottomRightX = this.translateX + worldWidth;
1143
- const bottomRightY = this.translateY + worldHeight;
1144
- const expandedTop = expandVertex(topX, topY);
1145
- const expandedBottomRight = expandVertex(bottomRightX, bottomRightY);
1146
- const expandedBottomLeft = expandVertex(bottomLeftX, bottomLeftY);
1147
- // Apply rotation around center if rotated
1148
- if (this.rotation !== 0) {
1149
- const cos = Math.cos(this.rotation);
1150
- const sin = Math.sin(this.rotation);
1151
- const rotate = (p) => {
1152
- const dx = p.x - centerX;
1153
- const dy = p.y - centerY;
1154
- return {
1155
- x: centerX + dx * cos - dy * sin,
1156
- y: centerY + dx * sin + dy * cos
1157
- };
1158
- };
1159
- return [
1160
- rotate(expandedTop),
1161
- rotate(expandedBottomRight),
1162
- rotate(expandedBottomLeft)
1163
- ];
1164
- }
1165
- return [expandedTop, expandedBottomRight, expandedBottomLeft];
1166
- case ShapeType.Rectangle:
1167
- default:
1168
- // For rectangles, return null to use the default rotatedPolygon
1169
- return null;
1170
- }
1171
- }
1172
- /**
1173
- * Returns the SVG path for rendering the shape.
1174
- * The path uses screen-space coordinates relative to (x, y).
1175
- */
1176
- getSvgPath() {
1177
- const w = this.width;
1178
- const h = this.height;
1179
- switch (this.shapeType) {
1180
- case ShapeType.Rectangle:
1181
- 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`;
1182
- case ShapeType.Ellipse:
1183
- const cx = this.x + w / 2;
1184
- const cy = this.y + h / 2;
1185
- const rx = w / 2;
1186
- const ry = h / 2;
1187
- return `M ${cx - rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx + rx} ${cy} A ${rx} ${ry} 0 1 0 ${cx - rx} ${cy}`;
1188
- case ShapeType.Triangle:
1189
- const topX = this.x + w / 2;
1190
- const topY = this.y;
1191
- const bottomLeftX = this.x;
1192
- const bottomLeftY = this.y + h;
1193
- const bottomRightX = this.x + w;
1194
- const bottomRightY = this.y + h;
1195
- return `M ${topX} ${topY} L ${bottomRightX} ${bottomRightY} L ${bottomLeftX} ${bottomLeftY} Z`;
1196
- default:
1197
- 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`;
1198
- }
1199
- }
1200
- }
1201
-
1202
- class KritzelShapeTool extends KritzelBaseTool {
1203
- shapeType = ShapeType.Rectangle;
1204
- fillColor = 'transparent';
1205
- strokeColor = '#000000';
1206
- strokeWidth = 4;
1207
- fontFamily = 'Arial';
1208
- fontSize = 16;
1209
- fontColor = '#000000';
1210
- palette = [
1211
- '#000000',
1212
- '#FFFFFF',
1213
- '#FF0000',
1214
- '#00FF00',
1215
- '#0000FF',
1216
- '#FFFF00',
1217
- '#FF00FF',
1218
- '#00FFFF',
1219
- '#808080',
1220
- '#C0C0C0',
1221
- '#800000',
1222
- '#008000',
1223
- '#000080',
1224
- '#808000',
1225
- '#800080',
1226
- ];
1227
- startX = 0;
1228
- startY = 0;
1229
- isDrawing = false;
1230
- currentShape = null;
1231
- constructor(core) {
1232
- super(core);
1233
- }
1234
- handlePointerDown(event) {
1235
- if (event.cancelable) {
1236
- event.preventDefault();
1237
- }
1238
- if (event.pointerType === 'mouse') {
1239
- const path = event.composedPath().slice(1);
1240
- const objectElement = path.find(element => element.classList && element.classList.contains('object'));
1241
- const object = this._core.findObjectById(objectElement?.id);
1242
- const activeShape = this._core.store.activeShape;
1243
- if (activeShape === null && object instanceof KritzelShape) {
1244
- object.edit(event);
1245
- return;
1246
- }
1247
- if (activeShape !== null && object instanceof KritzelShape) {
1248
- activeShape.save();
1249
- object.edit(event);
1250
- return;
1251
- }
1252
- if (activeShape !== null && object instanceof KritzelShape === false) {
1253
- this._core.resetActiveShape();
1254
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1255
- return;
1256
- }
1257
- if (KritzelEventHelper.isLeftClick(event) === false) {
1258
- return;
1259
- }
1260
- this.startDrawing(event.clientX, event.clientY);
1261
- }
1262
- if (event.pointerType === 'touch') {
1263
- const activePointers = Array.from(this._core.store.state.pointers.values());
1264
- const path = event.composedPath().slice(1);
1265
- const objectElement = path.find(element => element.classList && element.classList.contains('object'));
1266
- const object = this._core.findObjectById(objectElement?.id);
1267
- const activeShape = this._core.store.activeShape;
1268
- if (activeShape === null && object instanceof KritzelShape) {
1269
- object.edit(event);
1270
- return;
1271
- }
1272
- if (activeShape !== null && object instanceof KritzelShape) {
1273
- activeShape.save();
1274
- object.edit(event);
1275
- return;
1276
- }
1277
- if (activeShape !== null && object instanceof KritzelShape === false) {
1278
- this._core.resetActiveShape();
1279
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1280
- return;
1281
- }
1282
- if (activePointers.length > 1) {
1283
- return;
1284
- }
1285
- const clientX = Math.round(activePointers[0].clientX);
1286
- const clientY = Math.round(activePointers[0].clientY);
1287
- this.startDrawing(clientX, clientY);
1288
- }
1289
- }
1290
- handlePointerMove(event) {
1291
- if (event.cancelable) {
1292
- event.preventDefault();
1293
- }
1294
- if (!this.isDrawing || !this.currentShape) {
1295
- return;
1296
- }
1297
- if (event.pointerType === 'mouse') {
1298
- this.updateShapeSize(event.clientX, event.clientY);
1299
- }
1300
- if (event.pointerType === 'touch') {
1301
- const activePointers = Array.from(this._core.store.state.pointers.values());
1302
- if (activePointers.length === 1) {
1303
- const clientX = Math.round(activePointers[0].clientX);
1304
- const clientY = Math.round(activePointers[0].clientY);
1305
- this.updateShapeSize(clientX, clientY);
1306
- }
1307
- }
1308
- }
1309
- handlePointerUp(event) {
1310
- if (event.cancelable) {
1311
- event.preventDefault();
1068
+ else if (this.hasSuffixContent !== false) {
1069
+ this.hasSuffixContent = false;
1312
1070
  }
1313
- if (!this.isDrawing || !this.currentShape) {
1314
- return;
1071
+ };
1072
+ evaluatePrefixContent = () => {
1073
+ if (this.prefixSlotElement) {
1074
+ const newHasContent = this.prefixSlotElement.assignedNodes({ flatten: true }).length > 0;
1075
+ if (this.hasPrefixContent !== newHasContent) {
1076
+ this.hasPrefixContent = newHasContent;
1077
+ }
1315
1078
  }
1316
- this.finishDrawing();
1317
- }
1318
- /**
1319
- * Start drawing a shape. Following the same pattern as LineTool/BrushTool:
1320
- * - Store screen coordinates for startX, startY
1321
- * - Set translateX/Y to -viewportTranslateX/Y (viewport offset)
1322
- * - Set x, y to the actual screen position
1323
- * - Let updateDimensions() convert to world coordinates
1324
- */
1325
- startDrawing(clientX, clientY) {
1326
- // Store screen coordinates (relative to host element)
1327
- this.startX = clientX - this._core.store.offsetX;
1328
- this.startY = clientY - this._core.store.offsetY;
1329
- this.isDrawing = true;
1330
- // Create shape using screen coordinates, following Path/Line pattern
1331
- this.currentShape = KritzelShape.create(this._core, {
1332
- x: this.startX,
1333
- y: this.startY,
1334
- translateX: -this._core.store.state.translateX,
1335
- translateY: -this._core.store.state.translateY,
1336
- width: 1,
1337
- height: 1,
1338
- shapeType: this.shapeType,
1339
- fillColor: this.fillColor,
1340
- strokeColor: this.strokeColor,
1341
- strokeWidth: this.strokeWidth,
1342
- fontSize: this.fontSize,
1343
- fontFamily: this.fontFamily,
1344
- fontColor: this.fontColor,
1345
- });
1346
- this._core.store.state.objects.insert(this.currentShape);
1347
- this._core.rerender();
1348
- }
1349
- /**
1350
- * Update shape size during drawing. Following the same pattern as LineTool:
1351
- * - Use screen coordinates directly
1352
- * - The shape's x, y, width, height are all in screen space
1353
- * - updateDimensions() handles conversion to world coordinates
1354
- */
1355
- updateShapeSize(clientX, clientY) {
1356
- if (!this.currentShape) {
1357
- return;
1079
+ else if (this.hasPrefixContent !== false) {
1080
+ this.hasPrefixContent = false;
1358
1081
  }
1359
- const currentX = clientX - this._core.store.offsetX;
1360
- const currentY = clientY - this._core.store.offsetY;
1361
- // Calculate bounding box in screen coordinates
1362
- const minX = Math.min(this.startX, currentX);
1363
- const minY = Math.min(this.startY, currentY);
1364
- const width = Math.abs(currentX - this.startX);
1365
- const height = Math.abs(currentY - this.startY);
1366
- // Update shape with screen coordinates
1367
- this.currentShape.x = minX;
1368
- this.currentShape.y = minY;
1369
- this.currentShape.width = Math.max(1, width);
1370
- this.currentShape.height = Math.max(1, height);
1371
- // Recalculate world-space translateX/Y
1372
- // Reset translateX/Y to initial value before updateDimensions
1373
- this.currentShape.translateX = -this._core.store.state.translateX;
1374
- this.currentShape.translateY = -this._core.store.state.translateY;
1375
- this.currentShape.updateDimensions();
1376
- this._core.store.state.objects.update(this.currentShape);
1082
+ };
1083
+ getSelectedLabel() {
1084
+ const selectedOption = this.options.find(opt => opt.value === this.internalValue);
1085
+ return selectedOption?.label ?? '';
1377
1086
  }
1378
- finishDrawing() {
1379
- if (!this.currentShape) {
1380
- return;
1381
- }
1382
- // Remove shape if it's too small (likely an accidental click)
1383
- // Compare in screen space
1384
- if (this.currentShape.width < 10 && this.currentShape.height < 10) {
1385
- const shapeId = this.currentShape.id;
1386
- this._core.store.state.objects.remove(o => o.id === shapeId);
1387
- }
1388
- else {
1389
- this.currentShape.zIndex = this._core.store.currentZIndex;
1390
- this._core.store.state.objects.update(this.currentShape);
1391
- this._core.engine.emitObjectsChange();
1392
- this._core.selectObjects([this.currentShape]);
1393
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
1394
- }
1395
- this.isDrawing = false;
1396
- this.currentShape = null;
1397
- this._core.rerender();
1087
+ getSelectedStyle() {
1088
+ const selectedOption = this.options.find(opt => opt.value === this.internalValue);
1089
+ return selectedOption?.style;
1398
1090
  }
1399
- }
1091
+ render() {
1092
+ const triggerClasses = {
1093
+ 'dropdown-trigger': true,
1094
+ 'has-suffix-border': this.hasSuffixContent,
1095
+ 'has-prefix-border': this.hasPrefixContent,
1096
+ 'is-open': this.isOpen,
1097
+ 'open-up': this.openDirection === 'up',
1098
+ };
1099
+ const menuClasses = {
1100
+ 'dropdown-menu': true,
1101
+ 'is-open': this.isOpen,
1102
+ 'open-up': this.openDirection === 'up',
1103
+ 'open-down': this.openDirection === 'down',
1104
+ };
1105
+ return (h(Host, { key: '29d076eb2ef76527c0930ab82ace0d05c896ab6c' }, h("div", { key: '1afac5cc0b7f408849670b8cb62fe0bd0f3b27f1', class: "dropdown-wrapper", ref: el => (this.wrapperElement = el) }, h("slot", { key: '3cfcf787c3b3eedbd6bf30c7506a0a4912429672', name: "prefix", ref: el => (this.prefixSlotElement = el), onSlotchange: this.evaluatePrefixContent }), h("div", { key: 'b88120ed3e44871220e6ba61e8bef457651a2086', class: "dropdown-container", style: { width: this.width } }, h("button", { key: '79082189066f3d0b4f5797420ec222847792fe52', type: "button", class: triggerClasses, style: { ...this.selectStyles, ...this.getSelectedStyle() }, onClick: this.toggleMenu, onKeyDown: this.handleTriggerKeyDown, "aria-haspopup": "listbox", "aria-expanded": this.isOpen ? 'true' : 'false', ref: el => (this.triggerElement = el) }, h("span", { key: 'a490bb6689ec1820144fe82f6833bee82e6ead0e', class: "dropdown-trigger-label" }, this.getSelectedLabel()), h("span", { key: '8bd7768f6fdd3c507bbca54d7d135adabe04901f', class: "dropdown-trigger-arrow", "aria-hidden": "true" }, h("svg", { key: 'e1b854d2c93e3067181108f8d782e1393ddb615c', xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { key: '3622cbf8964f4e50aa55be5209240dfd252f1d84', points: "6 9 12 15 18 9" }))))), h("slot", { key: 'b91317736904cbeee0f87058b822bd77c6b163ca', name: "suffix", ref: el => (this.suffixSlotElement = el), onSlotchange: this.evaluateSuffixContent }), h("ul", { key: '0ca8690345974e9b7ad184f4c7ab7cf5367b183c', class: menuClasses, role: "listbox", tabindex: "-1", onKeyDown: this.handleMenuKeyDown, ref: el => (this.menuElement = el) }, this.options.map((option, index) => {
1106
+ const isSelected = option.value === this.internalValue;
1107
+ const isFocused = index === this.focusedIndex;
1108
+ const optionClasses = {
1109
+ 'dropdown-option': true,
1110
+ 'is-selected': isSelected,
1111
+ 'is-focused': isFocused,
1112
+ };
1113
+ return (h("li", { class: optionClasses, role: "option", "aria-selected": isSelected ? 'true' : 'false', style: option.style, onClick: () => this.selectOption(option), onMouseEnter: () => this.handleOptionMouseEnter(index) }, option.label, isSelected && (h("span", { class: "dropdown-option-check", "aria-hidden": "true" }, h("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round" }, h("polyline", { points: "20 6 9 17 4 12" }))))));
1114
+ })))));
1115
+ }
1116
+ static get watchers() { return {
1117
+ "options": [{
1118
+ "optionsChanged": 0
1119
+ }],
1120
+ "value": [{
1121
+ "externalValueChanged": 0
1122
+ }]
1123
+ }; }
1124
+ };
1125
+ KritzelDropdown.style = kritzelDropdownCss();
1400
1126
 
1401
1127
  const DEFAULT_SHAPE_CONFIG = {
1402
1128
  shapeType: ShapeType.Rectangle,
@@ -1406,21 +1132,7 @@ const DEFAULT_SHAPE_CONFIG = {
1406
1132
  fontColor: '#000000',
1407
1133
  fontSize: 16,
1408
1134
  fontFamily: 'Arial',
1409
- palette: [
1410
- '#000000',
1411
- '#ff5252',
1412
- '#ffbc00',
1413
- '#00c853',
1414
- '#0000FF',
1415
- '#d500f9',
1416
- '#fafafa',
1417
- '#a52714',
1418
- '#ee8100',
1419
- '#558b2f',
1420
- '#01579b',
1421
- '#8e24aa',
1422
- '#90a4ae',
1423
- ],
1135
+ palette: [...DEFAULT_COLOR_PALETTE],
1424
1136
  };
1425
1137
 
1426
1138
  const ABSOLUTE_SCALE_MAX = 1000;
@@ -1682,7 +1394,7 @@ const KritzelEditor = class {
1682
1394
  }
1683
1395
  }
1684
1396
  render() {
1685
- return (h(Host, { key: 'dd67bea86f807f0ccd5905183e73713a015d6bee' }, h("kritzel-workspace-manager", { key: '557edc7e3a131d338be6df91698ba6923e7bc50e', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-engine", { key: '98969d0f2bb5a9a365885fb4765e0c04c1394caa', 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: '6092f393562af07643622c07d432f6100497aaf0', 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) })));
1397
+ return (h(Host, { key: '46500c70e4c5321532eddc7f6aa96210c0fe73c8' }, h("kritzel-workspace-manager", { key: '70f780b4d42281c6c37b3fd16acab1541cc84ee1', workspaces: this.workspaces, activeWorkspace: this.activeWorkspace, onWorkspaceChange: event => (this.activeWorkspace = event.detail), onIsWorkspaceManagerReady: () => (this.isWorkspaceManagerReady = true) }), h("kritzel-engine", { key: 'a4906f8ac2d42126a4e55ad7c16697aca25763fb', 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: '4d08c06fa2c6e4f5c3c75c5e5a60bddb61083b98', 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) })));
1686
1398
  }
1687
1399
  static get watchers() { return {
1688
1400
  "isEngineReady": [{
@@ -18913,6 +18625,11 @@ class KritzelViewport {
18913
18625
  _core;
18914
18626
  _debounceUpdate;
18915
18627
  _debounceEndScaling;
18628
+ _velocityX = 0;
18629
+ _velocityY = 0;
18630
+ _panRafId = null;
18631
+ _friction = 0.92;
18632
+ _minVelocity = 0.5;
18916
18633
  initialTouchDistance = 0;
18917
18634
  startX = 0;
18918
18635
  startY = 0;
@@ -19096,12 +18813,47 @@ class KritzelViewport {
19096
18813
  this._debounceEndScaling();
19097
18814
  }
19098
18815
  handlePan(event) {
18816
+ // Normalize delta for trackpad vs mouse wheel
18817
+ // WheelEvent.deltaMode: 0=pixels, 1=lines, 2=pages
18818
+ let deltaX = event.deltaX;
18819
+ let deltaY = event.deltaY;
18820
+ if (event.deltaMode === 1) {
18821
+ deltaX *= 16;
18822
+ deltaY *= 16;
18823
+ }
18824
+ else if (event.deltaMode === 2) {
18825
+ deltaX *= window.innerWidth;
18826
+ deltaY *= window.innerHeight;
18827
+ }
19099
18828
  const panSpeed = 0.8;
19100
- this._core.store.state.translateX = this._core.store.state.translateX - event.deltaX * panSpeed;
19101
- this._core.store.state.translateY = this._core.store.state.translateY - event.deltaY * panSpeed;
18829
+ // Add to velocity for momentum effect
18830
+ this._velocityX = -deltaX * panSpeed;
18831
+ this._velocityY = -deltaY * panSpeed;
18832
+ // Start animation loop if not already running
18833
+ if (this._panRafId === null) {
18834
+ this._animatePan();
18835
+ }
18836
+ }
18837
+ _animatePan() {
18838
+ // Apply current velocity
18839
+ this._core.store.state.translateX += this._velocityX;
18840
+ this._core.store.state.translateY += this._velocityY;
19102
18841
  this._core.store.state.hasViewportChanged = true;
19103
18842
  this._core.rerender();
19104
- this._debounceUpdate();
18843
+ // Apply friction to slow down
18844
+ this._velocityX *= this._friction;
18845
+ this._velocityY *= this._friction;
18846
+ // Continue animation if velocity is still significant
18847
+ if (Math.abs(this._velocityX) > this._minVelocity || Math.abs(this._velocityY) > this._minVelocity) {
18848
+ this._panRafId = requestAnimationFrame(() => this._animatePan());
18849
+ }
18850
+ else {
18851
+ // Stop animation and finalize
18852
+ this._velocityX = 0;
18853
+ this._velocityY = 0;
18854
+ this._panRafId = null;
18855
+ this._debounceUpdate();
18856
+ }
19105
18857
  }
19106
18858
  }
19107
18859
 
@@ -20256,11 +20008,13 @@ class KritzelCore {
20256
20008
  this.removeSelectionGroup();
20257
20009
  this.removeSelectionBox();
20258
20010
  this._store.state.objects.insert(selectionGroup);
20011
+ this._kritzelEngine.triggerSelectionChange();
20259
20012
  }
20260
20013
  removeSelectionGroup() {
20261
20014
  const selectionGroup = this._store.selectionGroup;
20262
20015
  if (selectionGroup) {
20263
20016
  this._store.state.objects.remove(object => object.id === selectionGroup.id);
20017
+ this._kritzelEngine.triggerSelectionChange();
20264
20018
  }
20265
20019
  }
20266
20020
  removeSelectionBox() {
@@ -20802,11 +20556,15 @@ const KritzelEngine = class {
20802
20556
  }
20803
20557
  isEngineReady;
20804
20558
  activeToolChange;
20559
+ objectsSelectionChange;
20805
20560
  workspacesChange;
20806
20561
  longpress;
20807
20562
  objectsChange;
20808
20563
  undoStateChange;
20809
20564
  forceUpdate = 0;
20565
+ async triggerSelectionChange() {
20566
+ this.objectsSelectionChange.emit();
20567
+ }
20810
20568
  throttledWheel = lodashExports.throttle((ev) => {
20811
20569
  this.viewport.handleWheel(ev);
20812
20570
  this.core.store.state?.activeTool?.handleWheel(ev);
@@ -20922,6 +20680,9 @@ const KritzelEngine = class {
20922
20680
  return Promise.resolve(registeredTool);
20923
20681
  }
20924
20682
  async changeActiveTool(tool) {
20683
+ if (this.core.store.state.activeTool === tool) {
20684
+ return;
20685
+ }
20925
20686
  this.core.store.state.activeTool?.onDeactivate();
20926
20687
  this.core.store.setState('activeTool', tool);
20927
20688
  this.core.deselectAllObjects();
@@ -21064,6 +20825,7 @@ const KritzelEngine = class {
21064
20825
  registerInstance(this, hostRef);
21065
20826
  this.isEngineReady = createEvent(this, "isEngineReady");
21066
20827
  this.activeToolChange = createEvent(this, "activeToolChange");
20828
+ this.objectsSelectionChange = createEvent(this, "objectsSelectionChange");
21067
20829
  this.workspacesChange = createEvent(this, "workspacesChange");
21068
20830
  this.longpress = createEvent(this, "longpress");
21069
20831
  this.objectsChange = createEvent(this, "objectsChange");
@@ -21144,7 +20906,7 @@ const KritzelEngine = class {
21144
20906
  };
21145
20907
  const visibleObjects = this.core.store.state.objects.query(viewportBounds).sort((a, b) => a.zIndex - b.zIndex);
21146
20908
  this.core.cursorManager.applyCursor();
21147
- return (h(Host, { key: 'cf63f71e5ab763750f0857595a6b2d56e99b29fd' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: 'cf6d5221edb666692bbed086d1fa5bb57f591442', class: "debug-panel" }, h("div", { key: 'd1e94a59352063d08412c13bee8632b468229431' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '0eeff49b2c3bd703ef3b0fa574a323822ecef8a3' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: '884aaab5f8c10a21541139cbc1c824a80233c7bb' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: '6633c2b83830bc5091825b9c51af2aa82fbc935e' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '905bf21fed43acbada2e5d18dd9eb33979d368a4' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: 'd2abcda891791cee5386fe065de9b781c2db2e0a' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: '9f64a19ac4510a2eae4d605dfdbb9a855b001b19' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: '25634969756ec8505e2b169c4f8f77fa5260932a' }, "Scale: ", this.core.store.state?.scale), h("div", { key: '2aea36a0521a5e59154836da290e7c615e4dc36d' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: '599bafe7e87b23df191849ad23f56eea0009272f' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: 'd5e466d0b64b0f64a808dc8f08dba21530579a49' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: '6143b384caa285cd850111b3d6d3fe95f3a468db' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '5695722e2e07e024fe1b5db1be22c890fa16b7d9' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '93c2d1a6669b397637d6918055fc14c96493cc65' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: '2245f5894f7460d9d31ee33125260334920c6847' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: '57fd33a984b7975198d87285658ec4de2824aa34' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '257cc4badaa694f04c839ca68ee79d1b695a006b' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: 'c00f461af4e4b1523e7e5feccfb0abfc6d45f5b7' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: '3f1bd888dfa360153b930fc5e498ccb348879d81' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '4b33ce03143d6f96e359bc0a21697a3788fa6d3e' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: 'd603a0a226357378889c87d1bda5dd194388a81c' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: 'e0cb4c098e14c44c7c4d96a35f75a609a5d83144' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: 'd663eff93d7884aa36da56a46e06cb65f0b14c54' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: 'fe44809fc10454d605bad012300e8a46a8f180c9' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: 'd69d570660f6ec9d37d842f24f61e534d4f349e1' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: 'aa6674f91511007c52f7faba849dd82f206e20ed', id: "origin", class: "origin", style: {
20909
+ return (h(Host, { key: '49fbba9ba7044acb82cb23627bf08435f8683b80' }, this.core.store.state.debugInfo.showViewportInfo && (h("div", { key: '7e18e66bdbae71ec35a6ea14bfdcd735e537a37b', class: "debug-panel" }, h("div", { key: '562ff35d3803b80f3c02c075741f1c4b81ba0739' }, "ActiveWorkspaceId: ", this.core.store.state?.activeWorkspace?.id), h("div", { key: '82d08ac0527017c9f3674e1d551a071b0002477e' }, "ActiveWorkspaceName: ", this.core.store.state?.activeWorkspace?.name), h("div", { key: 'e4e3b8c6f18714dbf141a900768b2fb78ef8ec3a' }, "TranslateX: ", this.core.store.state?.translateX), h("div", { key: '68823a22db98800c7ec7b18770f38f03dd53f826' }, "TranslateY: ", this.core.store.state?.translateY), h("div", { key: '4683676eca0796b927ac8c10fe9ea48770366ee5' }, "ViewportWidth: ", this.core.store.state?.viewportWidth), h("div", { key: 'fcbd728c4483f06cffe1e9d581399a9619b3c172' }, "ViewportHeight: ", this.core.store.state?.viewportHeight), h("div", { key: 'f713005a414bd05b187327b5ffa021c51f227d97' }, "PointerCount: ", this.core.store.state.pointers.size), h("div", { key: 'fdb4892c0b5df0a599d4e457d656ca8e71086033' }, "Scale: ", this.core.store.state?.scale), h("div", { key: '7c1ceb6398ceccb891d5854a731467771d88e688' }, "ActiveTool: ", this.core.store.state?.activeTool?.name), h("div", { key: 'fddfb8b396ab095dd99158b32bf4c9a06745c65f' }, "HasViewportChanged: ", this.core.store.state?.hasViewportChanged ? 'true' : 'false'), h("div", { key: '8b807879e83bada98ff44a9124dddc7e7f8d8516' }, "IsEnabled: ", this.core.store.state?.isEnabled ? 'true' : 'false'), h("div", { key: '93827fa7bf4c27db8b3165a1d2be5d0cde5a65b6' }, "IsScaling: ", this.core.store.state?.isScaling ? 'true' : 'false'), h("div", { key: '923455fb261bc9aa8d6cd22c74894e41dc6a2f93' }, "IsPanning: ", this.core.store.state?.isPanning ? 'true' : 'false'), h("div", { key: '33579a767be0a80bed4a177da9577e6304d4900c' }, "IsSelecting: ", this.isSelecting ? 'true' : 'false'), h("div", { key: '950c340bf725d7c8c92a51c61066c1aa7d09aa41' }, "IsSelectionActive: ", this.isSelectionActive ? 'true' : 'false'), h("div", { key: '2a8ce1e6d68ea030f42521b25615bf60a1a8267e' }, "IsResizeHandleSelected: ", this.core.store.state.isResizeHandleSelected ? 'true' : 'false'), h("div", { key: '9fd5a4b1a842c9aa178dc98dc9a210cf7d947615' }, "IsRotationHandleSelected: ", this.core.store.state.isRotationHandleSelected ? 'true' : 'false'), h("div", { key: '81f9c6f9f5c38b48db1faaa8da08476cd747e906' }, "IsRotationHandleHovered: ", this.core.store.state.isRotationHandleHovered ? 'true' : 'false'), h("div", { key: 'bbea182105ab1f1856d8ffe7ee6dae2c96cefbae' }, "IsDrawing: ", this.core.store.state.isDrawing ? 'true' : 'false'), h("div", { key: '0e57b3b517fc6f4780a6bf8b13a18e7d595e2e91' }, "IsWriting: ", this.core.store.state.isWriting ? 'true' : 'false'), h("div", { key: '5be12fd92b4ccb6890680e725aea0f29fa16eda4' }, "IsPointerDown: ", this.core.store.isPointerDown ? 'true' : 'false'), h("div", { key: '51402f4730b8e482ee534bb83944c50462d895af' }, "PointerX: ", this.core.store.state?.pointerX), h("div", { key: 'dddf5bed76a1755aec71ae7f30eab8dd0c39fdb0' }, "PointerY: ", this.core.store.state?.pointerY), h("div", { key: '367166d068f34090eb895116d8ac1ddd3ab5aaad' }, "SelectedObjects: ", this.core.store.selectionGroup?.objects.length || 0), h("div", { key: 'e88d10e302e4ff3e8cb877a509a883e2cffcc067' }, "ViewportCenter: (", viewportCenterX.toFixed(2), ", ", viewportCenterY.toFixed(2), ")"))), h("div", { key: '12bf294a8640947230611690f71b8c8116e861f4', id: "origin", class: "origin", style: {
21148
20910
  transform: `matrix(${this.core.store.state?.scale}, 0, 0, ${this.core.store.state?.scale}, ${this.core.store.state?.translateX}, ${this.core.store.state?.translateY})`,
21149
20911
  } }, visibleObjects?.map(object => {
21150
20912
  return (h("div", { key: object.id, style: {
@@ -21393,7 +21155,7 @@ const KritzelEngine = class {
21393
21155
  stroke: 'var(--kritzel-snap-indicator-stroke, #007bff)',
21394
21156
  strokeWidth: data.indicatorStrokeWidth,
21395
21157
  } }))));
21396
- })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '95f98d34b26eca7124f973002c2689e9a90f6748', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
21158
+ })()), this.core.store.state.isContextMenuVisible && (h("kritzel-context-menu", { key: '9d17520994c59b7eca2940d1a05f4f150b790767', class: "context-menu", ref: el => (this.contextMenuElement = el), items: this.core.store.state.contextMenuItems, objects: this.core.store.selectionGroup?.objects || [], style: {
21397
21159
  position: 'fixed',
21398
21160
  left: `${this.core.store.state.contextMenuX}px`,
21399
21161
  top: `${this.core.store.state.contextMenuY}px`,
@@ -21404,7 +21166,7 @@ const KritzelEngine = class {
21404
21166
  y: (-this.core.store.state.translateY + this.core.store.state.contextMenuY) / this.core.store.state.scale,
21405
21167
  }, this.core.store.selectionGroup?.objects);
21406
21168
  this.hideContextMenu();
21407
- }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: '412f67f0a484b7f5e37a4e9fa25d9124c389f62b', core: this.core })));
21169
+ }, onClose: () => this.hideContextMenu() })), this.core.store.state?.activeTool instanceof KritzelEraserTool && !this.core.store.state.isScaling && h("kritzel-cursor-trail", { key: 'b86989f2a710324e420994f74bd8c4990cc52060', core: this.core })));
21408
21170
  }
21409
21171
  static get watchers() { return {
21410
21172
  "workspace": [{
@@ -21433,7 +21195,7 @@ const KritzelFont = class {
21433
21195
  size = 24;
21434
21196
  color = '#000000';
21435
21197
  render() {
21436
- return (h(Host, { key: '62d0314df8f62ef2cf21c17f548261b34611750f' }, h("div", { key: '269347c138aab38cb1f018e58db78cab8e087f13', class: "font-preview", style: {
21198
+ return (h(Host, { key: '47e1580b093d5c9039a5bc1a7764e345afb7700b' }, h("div", { key: '1d6d36e895ab4e1fd76383b0847f81918e14dbb0', class: "font-preview", style: {
21437
21199
  fontFamily: this.fontFamily,
21438
21200
  fontSize: `${this.size}px`,
21439
21201
  color: this.color
@@ -21442,7 +21204,7 @@ const KritzelFont = class {
21442
21204
  };
21443
21205
  KritzelFont.style = kritzelFontCss();
21444
21206
 
21445
- const kritzelFontFamilyCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:8px;box-sizing:border-box;width:100%}.font-style-button{display:flex;justify-content:center;align-items:center;width:42px;height:32px;padding:0;border:none;outline:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);border-radius:0;color:var(--control-text-color);font-weight:bold;-webkit-tap-highlight-color:transparent}.font-style-button:not(:last-child){border-right:1px solid #333333}.font-style-button:hover{background-color:var(--control-hover-bg)}.font-style-button:active{background-color:var(--control-active-bg)}.font-style-button.selected,.font-style-button.selected:hover,.font-style-button.selected:active{background-color:var(--control-selected-bg);color:var(--control-selected-color)}.font-style-button.italic-text{font-style:italic}`;
21207
+ const kritzelFontFamilyCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:0;box-sizing:border-box;width:100%}.font-style-button{display:flex;justify-content:center;align-items:center;width:42px;height:32px;padding:0;border:none;outline:none;background:none;cursor:var(--kritzel-pointer-cursor, pointer);border-radius:0;color:var(--control-text-color);font-weight:bold;-webkit-tap-highlight-color:transparent}.font-style-button:not(:last-child){border-right:1px solid #333333}.font-style-button:hover{background-color:var(--control-hover-bg)}.font-style-button:active{background-color:var(--control-active-bg)}.font-style-button.selected,.font-style-button.selected:hover,.font-style-button.selected:active{background-color:var(--control-selected-bg);color:var(--control-selected-color)}.font-style-button.italic-text{font-style:italic}`;
21446
21208
 
21447
21209
  const KritzelFontFamily = class {
21448
21210
  constructor(hostRef) {
@@ -21485,7 +21247,7 @@ const KritzelFontFamily = class {
21485
21247
  };
21486
21248
  KritzelFontFamily.style = kritzelFontFamilyCss();
21487
21249
 
21488
- const kritzelFontSizeCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:8px;box-sizing:border-box}.size-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:4px;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;border-radius:50%}.size-container:hover{background-color:var(--kritzel-font-size-hover-background-color, #ebebeb)}.size-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-font-size-selected-background-color, #e0e0e0)}`;
21250
+ const kritzelFontSizeCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:0;box-sizing:border-box}.size-container{display:flex;justify-content:center;align-items:center;width:32px;height:32px;border-radius:4px;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;border-radius:50%}.size-container:hover{background-color:var(--kritzel-font-size-hover-background-color, #ebebeb)}.size-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-font-size-selected-background-color, #e0e0e0)}`;
21489
21251
 
21490
21252
  const KritzelFontSize = class {
21491
21253
  constructor(hostRef) {
@@ -21507,7 +21269,7 @@ const KritzelFontSize = class {
21507
21269
  }
21508
21270
  }
21509
21271
  render() {
21510
- return (h(Host, { key: 'b0ee1ab0eb5e1871f7726d4393aea59f63264ec5' }, this.sizes.map(size => (h("div", { tabIndex: 0, class: {
21272
+ return (h(Host, { key: 'ea8461f3b921c6adec6360ffcca69832a30e5808' }, this.sizes.map(size => (h("div", { tabIndex: 0, class: {
21511
21273
  'size-container': true,
21512
21274
  'selected': this.selectedSize === size,
21513
21275
  }, onClick: () => this.handleSizeClick(size), onKeyDown: event => this.handleKeyDown(event, size) }, h("kritzel-font", { fontFamily: this.fontFamily, size: size }))))));
@@ -21539,6 +21301,91 @@ const KritzelIcon = class {
21539
21301
  };
21540
21302
  KritzelIcon.style = kritzelIconCss();
21541
21303
 
21304
+ const kritzelLineEndingsCss = () => `:host{display:flex;flex-direction:column;gap:12px;padding:0;box-sizing:border-box}.endings-section{display:flex;flex-direction:column;gap:6px}.section-label{font-size:12px;font-weight:500;color:var(--kritzel-line-endings-label-color, #666666);padding-left:4px}.endings-row{display:flex;align-items:center;gap:4px}.ending-option{display:flex;justify-content:center;align-items:center;width:48px;height:32px;border-radius:6px;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background:var(--kritzel-line-endings-option-background, #ffffff);padding:4px;transition:background-color 0.15s ease, border-color 0.15s ease}.ending-option:hover{background-color:var(--kritzel-line-endings-hover-background-color, #ebebeb)}.ending-option.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-line-endings-selected-background-color, #ebebeb)}.ending-option:focus{outline:none;box-shadow:0 0 0 2px var(--kritzel-focus-ring-color, rgba(0, 122, 255, 0.3))}.ending-icon{width:100%;height:100%}`;
21305
+
21306
+ const KritzelLineEndings = class {
21307
+ constructor(hostRef) {
21308
+ registerInstance(this, hostRef);
21309
+ this.valueChange = createEvent(this, "valueChange");
21310
+ }
21311
+ /** Available ending styles */
21312
+ styles = ['none', 'triangle'];
21313
+ /** Current line arrow configuration */
21314
+ value;
21315
+ /** Stroke color used for rendering the preview */
21316
+ strokeColor = '#000000';
21317
+ valueChange;
21318
+ getStartEnding() {
21319
+ if (!this.value?.start?.enabled)
21320
+ return 'none';
21321
+ return this.value.start.style ?? 'triangle';
21322
+ }
21323
+ getEndEnding() {
21324
+ if (!this.value?.end?.enabled)
21325
+ return 'none';
21326
+ return this.value.end.style ?? 'triangle';
21327
+ }
21328
+ handleStartChange(type) {
21329
+ const newValue = {
21330
+ ...this.value,
21331
+ start: type === 'none'
21332
+ ? { enabled: false }
21333
+ : { enabled: true, style: type },
21334
+ };
21335
+ this.value = newValue;
21336
+ this.valueChange.emit(newValue);
21337
+ }
21338
+ handleEndChange(type) {
21339
+ const newValue = {
21340
+ ...this.value,
21341
+ end: type === 'none'
21342
+ ? { enabled: false }
21343
+ : { enabled: true, style: type },
21344
+ };
21345
+ this.value = newValue;
21346
+ this.valueChange.emit(newValue);
21347
+ }
21348
+ getEndingPath(type) {
21349
+ switch (type) {
21350
+ case 'triangle':
21351
+ return 'M 0 0 L 10 5 L 0 10 Z';
21352
+ case 'open':
21353
+ return 'M 0 0 L 10 5 L 0 10';
21354
+ case 'diamond':
21355
+ return 'M 0 5 L 5 0 L 10 5 L 5 10 Z';
21356
+ case 'circle':
21357
+ return 'M 10 5 A 5 5 0 1 1 0 5 A 5 5 0 1 1 10 5 Z';
21358
+ default:
21359
+ return '';
21360
+ }
21361
+ }
21362
+ renderEndingIcon(type, isStart) {
21363
+ const color = '#000000';
21364
+ if (type === 'none') {
21365
+ return (h("svg", { viewBox: "0 0 24 12", class: "ending-icon" }, h("line", { x1: isStart ? 4 : 2, y1: "6", x2: isStart ? 22 : 20, y2: "6", stroke: color, "stroke-width": "2", "stroke-linecap": "round" })));
21366
+ }
21367
+ const path = this.getEndingPath(type);
21368
+ const isOpenStyle = type === 'open';
21369
+ return (h("svg", { viewBox: "0 0 24 12", class: "ending-icon" }, isStart ? (
21370
+ // Start arrow points left
21371
+ h("g", null, h("line", { x1: "12", y1: "6", x2: "22", y2: "6", stroke: color, "stroke-width": "2", "stroke-linecap": "round" }), h("g", { transform: "translate(2, 1) scale(1, 1)" }, h("path", { d: path, fill: isOpenStyle ? 'none' : color, stroke: color, "stroke-width": isOpenStyle ? 2 : 0, "stroke-linecap": "round", "stroke-linejoin": "round", transform: "scale(-1, 1) translate(-10, 0)" })))) : (
21372
+ // End arrow points right
21373
+ h("g", null, h("line", { x1: "2", y1: "6", x2: "12", y2: "6", stroke: color, "stroke-width": "2", "stroke-linecap": "round" }), h("g", { transform: "translate(12, 1)" }, h("path", { d: path, fill: isOpenStyle ? 'none' : color, stroke: color, "stroke-width": isOpenStyle ? 2 : 0, "stroke-linecap": "round", "stroke-linejoin": "round" }))))));
21374
+ }
21375
+ render() {
21376
+ const startEnding = this.getStartEnding();
21377
+ const endEnding = this.getEndEnding();
21378
+ return (h(Host, { key: '395c2c7e54986d9edffe7b1329361d9e0727d086' }, h("div", { key: '9f29cb35182b74d73fe82a89f91be42da3e43d3f', class: "endings-section" }, h("div", { key: '61f3e2bc9e3c2739e241213e68af71dd1091774e', class: "endings-row" }, this.styles.map(type => (h("button", { class: {
21379
+ 'ending-option': true,
21380
+ 'selected': startEnding === type,
21381
+ }, onClick: () => this.handleStartChange(type), title: type === 'none' ? 'No start arrow' : `${type} start arrow` }, this.renderEndingIcon(type, true)))))), h("div", { key: '1e39f687277ebdd0786f56b9be40101df42495d2', class: "endings-section" }, h("div", { key: 'fbe2ea8393cbf82b7c7aff7d6d8ca16abab1020a', class: "endings-row" }, this.styles.map(type => (h("button", { class: {
21382
+ 'ending-option': true,
21383
+ 'selected': endEnding === type,
21384
+ }, onClick: () => this.handleEndChange(type), title: type === 'none' ? 'No end arrow' : `${type} end arrow` }, this.renderEndingIcon(type, false))))))));
21385
+ }
21386
+ };
21387
+ KritzelLineEndings.style = kritzelLineEndingsCss();
21388
+
21542
21389
  const kritzelMenuCss = () => `:host{position:relative;display:flex;flex-direction:column;background-color:var(--kritzel-menu-background-color, #ffffff);width:var(--kritzel-menu-width, 200px);padding:var(--kritzel-menu-padding, 8px);border-radius:var(--kritzel-menu-border-radius, 12px);box-shadow:var(--kritzel-menu-box-shadow, 0 0 3px rgba(0, 0, 0, 0.08));border:var(--kritzel-menu-border, 1px solid #ebebeb);z-index:2;gap:var(--kritzel-menu-gap, 4px);overflow-y:auto;scrollbar-color:#ebebeb transparent;scrollbar-width:thin;max-height:var(--kritzel-portal-max-height, 300px);box-sizing:border-box}.has-open-child-overlay{position:absolute;top:0;left:0;right:0;bottom:0;z-index:3}`;
21543
21390
 
21544
21391
  const KritzelMenu = class {
@@ -21602,7 +21449,7 @@ const KritzelMenu = class {
21602
21449
  this.itemCloseChildMenu.emit(event.detail);
21603
21450
  };
21604
21451
  render() {
21605
- return (h(Host, { key: '3901d38f620a544be329e1a20b17b188a81295dc', tabIndex: 0, onClick: e => e.stopPropagation() }, this.openChildMenuItem && h("div", { key: '8804023760f43fa34c3ac3f1829439f6136112b9', class: "has-open-child-overlay", onClick: this.onOverlayClick }), this.items.map(item => (h("kritzel-menu-item", { key: item.id, item: item, parent: this.parent, style: { pointerEvents: this.editingMenuItem && !item.isEditing ? 'none' : 'auto' }, onItemSelect: this.handleItemSelect, onItemSave: this.handleSave, onItemCancel: this.handleCancel, onItemToggleChildMenu: this.handleToggleChildMenu, onItemCloseChildMenu: this.handleCloseChildMenu })))));
21452
+ return (h(Host, { key: '7143a116ad361651851491bce048901d6c0a02a0', tabIndex: 0, onClick: e => e.stopPropagation() }, this.openChildMenuItem && h("div", { key: '8a5524ca7516955e1be34d10b8495c7b40ac05dd', class: "has-open-child-overlay", onClick: this.onOverlayClick }), this.items.map(item => (h("kritzel-menu-item", { key: item.id, item: item, parent: this.parent, style: { pointerEvents: this.editingMenuItem && !item.isEditing ? 'none' : 'auto' }, onItemSelect: this.handleItemSelect, onItemSave: this.handleSave, onItemCancel: this.handleCancel, onItemToggleChildMenu: this.handleToggleChildMenu, onItemCloseChildMenu: this.handleCloseChildMenu })))));
21606
21453
  }
21607
21454
  };
21608
21455
  KritzelMenu.style = kritzelMenuCss();
@@ -21703,12 +21550,12 @@ const KritzelMenuItem = class {
21703
21550
  ];
21704
21551
  }
21705
21552
  render() {
21706
- return (h(Host, { key: '3a8119288e14c4de188663910a58e0ba67b47614', tabIndex: this.item.isDisabled ? -1 : 0, class: {
21553
+ return (h(Host, { key: '59a53dc472dd624f78dd773eb20a8722720ed465', tabIndex: this.item.isDisabled ? -1 : 0, class: {
21707
21554
  'selected': this.item.isSelected,
21708
21555
  'editing': this.item.isEditing,
21709
21556
  'disabled': this.item.isDisabled,
21710
21557
  'child-open': this.item.isChildMenuOpen,
21711
- }, onClick: this.handleItemSelect }, h("div", { key: 'b5dadbff0a915fb032f4a22207683667363eba1d', class: "menu-item-overlay" }), this.item.isEditing ? this.renderEditMode() : this.renderViewMode()));
21558
+ }, onClick: this.handleItemSelect }, h("div", { key: '22665fedfa83e2483d97480a9c9e45da9d296319', class: "menu-item-overlay" }), this.item.isEditing ? this.renderEditMode() : this.renderViewMode()));
21712
21559
  }
21713
21560
  static get watchers() { return {
21714
21561
  "item": [{
@@ -21718,6 +21565,42 @@ const KritzelMenuItem = class {
21718
21565
  };
21719
21566
  KritzelMenuItem.style = kritzelMenuItemCss();
21720
21567
 
21568
+ const kritzelOpacitySliderCss = () => `:host{display:flex;flex-direction:column;padding:0;box-sizing:border-box}.opacity-container{display:flex;align-items:center;width:232px}.slider-wrapper{flex:1;display:flex;align-items:center}.opacity-slider{-webkit-appearance:none;appearance:none;width:100%;height:6px;border-radius:3px;background:linear-gradient( to right, var(--kritzel-opacity-slider-active-color, #007AFF) 0%, var(--kritzel-opacity-slider-active-color, #007AFF) var(--slider-progress, 100%), var(--kritzel-opacity-slider-track-color, #e0e0e0) var(--slider-progress, 100%), var(--kritzel-opacity-slider-track-color, #e0e0e0) 100% );outline:none;cursor:var(--kritzel-pointer-cursor, pointer)}.opacity-slider::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;width:16px;height:16px;border-radius:50%;background:var(--kritzel-opacity-slider-thumb-color, #ffffff);border:2px solid var(--kritzel-opacity-slider-thumb-border-color, #007AFF);cursor:var(--kritzel-pointer-cursor, pointer);box-shadow:0 1px 3px rgba(0, 0, 0, 0.2);transition:transform 0.1s ease}.opacity-slider::-webkit-slider-thumb:hover{transform:scale(1.1)}.opacity-slider::-moz-range-thumb{width:16px;height:16px;border-radius:50%;background:var(--kritzel-opacity-slider-thumb-color, #ffffff);border:2px solid var(--kritzel-opacity-slider-thumb-border-color, #007AFF);cursor:var(--kritzel-pointer-cursor, pointer);box-shadow:0 1px 3px rgba(0, 0, 0, 0.2);transition:transform 0.1s ease}.opacity-slider::-moz-range-thumb:hover{transform:scale(1.1)}.opacity-slider::-moz-range-track{height:6px;border-radius:3px;background:transparent}.opacity-slider:focus{outline:none}.opacity-slider:focus::-webkit-slider-thumb{box-shadow:0 0 0 3px var(--kritzel-focus-ring-color, rgba(0, 122, 255, 0.3))}.opacity-slider:focus::-moz-range-thumb{box-shadow:0 0 0 3px var(--kritzel-focus-ring-color, rgba(0, 122, 255, 0.3))}`;
21569
+
21570
+ const KritzelOpacitySlider = class {
21571
+ constructor(hostRef) {
21572
+ registerInstance(this, hostRef);
21573
+ this.valueChange = createEvent(this, "valueChange");
21574
+ }
21575
+ /** Current opacity value (0 to 1) */
21576
+ value = 1;
21577
+ /** Minimum opacity value */
21578
+ min = 0;
21579
+ /** Maximum opacity value */
21580
+ max = 1;
21581
+ /** Step increment */
21582
+ step = 0.01;
21583
+ /** Color to display in the preview (optional) */
21584
+ previewColor = '#000000';
21585
+ valueChange;
21586
+ handleInput(event) {
21587
+ const input = event.target;
21588
+ const newValue = parseFloat(input.value);
21589
+ this.value = newValue;
21590
+ this.valueChange.emit(newValue);
21591
+ }
21592
+ getPercentage() {
21593
+ return Math.round(this.value * 100);
21594
+ }
21595
+ render() {
21596
+ const percentage = this.getPercentage();
21597
+ return (h(Host, { key: 'e1409175b038e78d2659356452c48ccc7d79b8de' }, h("div", { key: '1de138f56f699fc54623ed8d7b8b5c6d3eda3c67', class: "opacity-container" }, h("div", { key: '1dd5d0d36c8d025ba46a0623a20f040990cc749a', class: "slider-wrapper" }, h("input", { key: '7b555aa0a4a9abfdcdadc86bffa5878c9336d1e4', type: "range", class: "opacity-slider", min: this.min, max: this.max, step: this.step, value: this.value, onInput: (e) => this.handleInput(e), style: {
21598
+ '--slider-progress': `${percentage}%`,
21599
+ } })))));
21600
+ }
21601
+ };
21602
+ KritzelOpacitySlider.style = kritzelOpacitySliderCss();
21603
+
21721
21604
  class KritzelHTMLHelper {
21722
21605
  static getNumericValueFromStyle(element, property) {
21723
21606
  const value = window.getComputedStyle(element).getPropertyValue(property);
@@ -21974,7 +21857,7 @@ const KritzelPortal = class {
21974
21857
  this.portal.style.left = `${left}px`;
21975
21858
  }
21976
21859
  render() {
21977
- return (h(Host, { key: '9706a815c6f49853983a0fdb68b097d62e65e90b', style: { display: this.anchor ? 'block' : 'none' } }, h("slot", { key: '1bd76deeb440cd25e3895dc8824f5200461e4ae0' })));
21860
+ return (h(Host, { key: 'c70a1f7c4b2801346fc7a711b44e951337f42f77', style: { display: this.anchor ? 'block' : 'none' } }, h("slot", { key: '654bd6dd3ec83d77984a500d505ed1f3c04a0c26' })));
21978
21861
  }
21979
21862
  static get watchers() { return {
21980
21863
  "anchor": [{
@@ -21983,6 +21866,40 @@ const KritzelPortal = class {
21983
21866
  }; }
21984
21867
  };
21985
21868
 
21869
+ const kritzelShapeFillCss = () => `:host{display:flex;flex-direction:column;gap:12px;padding:0;box-sizing:border-box}.fill-row{display:flex;align-items:center;gap:4px}.fill-option{display:flex;justify-content:center;align-items:center;width:48px;height:32px;border-radius:6px;cursor:var(--kritzel-pointer-cursor, pointer);border:2px solid transparent;box-sizing:border-box;background:var(--kritzel-shape-fill-option-background, #ffffff);padding:4px;transition:background-color 0.15s ease, border-color 0.15s ease}.fill-option:hover{background-color:var(--kritzel-shape-fill-hover-background-color, #ebebeb)}.fill-option.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-shape-fill-selected-background-color, #ebebeb)}.fill-option:focus{outline:none;box-shadow:0 0 0 2px var(--kritzel-focus-ring-color, rgba(0, 122, 255, 0.3))}.fill-icon{width:100%;height:100%}`;
21870
+
21871
+ const KritzelShapeFill = class {
21872
+ constructor(hostRef) {
21873
+ registerInstance(this, hostRef);
21874
+ this.valueChange = createEvent(this, "valueChange");
21875
+ }
21876
+ /** Current fill type */
21877
+ value = 'transparent';
21878
+ valueChange;
21879
+ handleFillChange(type) {
21880
+ this.value = type;
21881
+ this.valueChange.emit(type);
21882
+ }
21883
+ renderFillIcon(type) {
21884
+ const strokeColor = '#000000';
21885
+ if (type === 'transparent') {
21886
+ return (h("svg", { viewBox: "0 0 24 24", class: "fill-icon" }, h("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2", fill: "none", stroke: strokeColor, "stroke-width": "2" })));
21887
+ }
21888
+ // Filled
21889
+ return (h("svg", { viewBox: "0 0 24 24", class: "fill-icon" }, h("rect", { x: "4", y: "4", width: "16", height: "16", rx: "2", fill: strokeColor, stroke: strokeColor, "stroke-width": "2" })));
21890
+ }
21891
+ render() {
21892
+ return (h(Host, { key: '7935296eb495557672cc96ab838ff4996953b220' }, h("div", { key: '69011629ff2dbdd38c7fb03b873fe5411b712de3', class: "fill-row" }, h("button", { key: '8a16a43b1ed8f316bc698a304d76667397a83015', class: {
21893
+ 'fill-option': true,
21894
+ 'selected': this.value === 'transparent',
21895
+ }, onClick: () => this.handleFillChange('transparent'), title: "Transparent background" }, this.renderFillIcon('transparent')), h("button", { key: 'f51854da5ece9f18242b4b91ac9645e13efc41b3', class: {
21896
+ 'fill-option': true,
21897
+ 'selected': this.value === 'filled',
21898
+ }, onClick: () => this.handleFillChange('filled'), title: "Filled background" }, this.renderFillIcon('filled')))));
21899
+ }
21900
+ };
21901
+ KritzelShapeFill.style = kritzelShapeFillCss();
21902
+
21986
21903
  const kritzelSplitButtonCss = () => `:host{position:relative;display:flex;align-items:center;font-family:sans-serif;z-index:1;padding:var(--kritzel-split-button-padding, 4px);background-color:var(--kritzel-split-button-background-color, #ffffff);border-radius:var(--kritzel-split-button-border-radius, 12px);box-shadow:var(--kritzel-split-button-box-shadow, 0 0 3px rgba(0, 0, 0, 0.08));border:var(--kritzel-split-button-border, 1px solid #ebebeb);gap:var(--kritzel-split-button-gap, 4px)}:host(.mobile){--kritzel-split-button-hover-background-color:transparent}button{border:none;background-color:transparent;padding:0;margin:0;font-family:inherit;font-size:inherit;color:inherit;-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:var(--kritzel-pointer-cursor, pointer);text-align:center;display:flex;align-items:center;justify-content:center;pointer-events:all;-webkit-tap-highlight-color:transparent}.split-main-button,.split-menu-button{height:auto;display:flex;align-items:center;padding:var(--kritzel-split-button-padding, 8px);background-color:var(--kritzel-split-button-background-color, #ffffff);border-radius:var(--kritzel-split-button-border-radius, 12px);font-size:var(--kritzel-split-button-font-size, 14px)}.split-main-button:hover,.split-menu-button:hover{background-color:var(--kritzel-split-button-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.split-main-button:focus,.split-menu-button:focus{background-color:var(--kritzel-split-button-hover-background-color, hsl(0, 0%, 0%, 4.3%))}.split-main-button{gap:var(--kritzel-split-button-gap, 4px)}.split-menu-button{border-left:none;justify-content:center}.split-divider{width:var(--kritzel-split-button-divider-width, 1px);height:24px;background-color:var(--kritzel-split-button-divider-background-color, hsl(0, 0%, 0%, 4.3%))}:disabled{pointer-events:none;opacity:0.5}`;
21987
21904
 
21988
21905
  const KritzelSplitButton = class {
@@ -22076,12 +21993,12 @@ const KritzelSplitButton = class {
22076
21993
  this.menuScrollTop = event.target.scrollTop;
22077
21994
  };
22078
21995
  render() {
22079
- return (h(Host, { key: 'a5587085e47c4e33148f9d0817731bfa3aa804b6', class: { mobile: this.isTouchDevice } }, h("button", { key: 'd3815785bb0919e2385bc7b9b0812466ed2b45a3', class: "split-main-button", tabIndex: 0, onClick: this.handleButtonClick, disabled: this.mainButtonDisabled }, this.buttonIcon && h("kritzel-icon", { key: 'b3225e1966bd21e8d37590cb39c0922e46afdaa9', name: this.buttonIcon })), h("div", { key: '526a88ef833cae7d2c7749f2a4bd96bcd2210a56', class: "split-divider" }), h("button", { key: '2c3ee46806bc64edc1f89a97c5e76ec493822065', ref: el => (this.splitMenuButtonRef = el), class: "split-menu-button", tabIndex: 0, onClick: this.toggleMenu, disabled: this.menuButtonDisabled }, h("kritzel-icon", { key: 'c6204d8b14d8cefe9d6eff33af95b2a7ea9a6b7c', name: this.dropdownIcon })), h("kritzel-portal", { key: '07a5e9793484f79d9b4b1f9ae09503f762cb2580', anchor: this.anchorElement, offsetY: 4, onClose: this.closeMenu }, h("kritzel-menu", { key: '0cf7601fd43b846a69152f0fc6ab950ac108ebff', ref: el => (this.menuRef = el), items: this.items, onItemSelect: this.handleItemSelect, onItemSave: this.handleItemSave, onItemCancel: this.handleItemCancel, onItemToggleChildMenu: this.handleItemToggleChildMenu, onItemCloseChildMenu: this.handleItemCloseChildMenu, onClose: this.closeMenu, onScroll: this.handleScroll }))));
21996
+ return (h(Host, { key: 'cdfb795efe513061f464c76650aa226cd7d5ac81', class: { mobile: this.isTouchDevice } }, h("button", { key: '5c38c82b57f35c2e39b0dbba71b3ccfd99ebcc5c', class: "split-main-button", tabIndex: 0, onClick: this.handleButtonClick, disabled: this.mainButtonDisabled }, this.buttonIcon && h("kritzel-icon", { key: '862d96cca7ec5aef55284ac6a65ce16b07a03dda', name: this.buttonIcon })), h("div", { key: '41849e36e756af257d10baf82d2cb0c9cd1e441f', class: "split-divider" }), h("button", { key: '0c54c40c84dcf5cf1b94b024353334e4be0e3404', ref: el => (this.splitMenuButtonRef = el), class: "split-menu-button", tabIndex: 0, onClick: this.toggleMenu, disabled: this.menuButtonDisabled }, h("kritzel-icon", { key: '15b0d07c9afa584105180fb5f0701c15974f11aa', name: this.dropdownIcon })), h("kritzel-portal", { key: '23014ad95c3ee688b9348f480cc33cdb1c970244', anchor: this.anchorElement, offsetY: 4, onClose: this.closeMenu }, h("kritzel-menu", { key: '90f4f29429a80f5700d16b1e99f3dd6315464bbb', ref: el => (this.menuRef = el), items: this.items, onItemSelect: this.handleItemSelect, onItemSave: this.handleItemSave, onItemCancel: this.handleItemCancel, onItemToggleChildMenu: this.handleItemToggleChildMenu, onItemCloseChildMenu: this.handleItemCloseChildMenu, onClose: this.closeMenu, onScroll: this.handleScroll }))));
22080
21997
  }
22081
21998
  };
22082
21999
  KritzelSplitButton.style = kritzelSplitButtonCss();
22083
22000
 
22084
- const kritzelStrokeSizeCss = () => `:host{display:flex;align-items:flex-start;gap:8px;padding:8px;box-sizing:border-box}.size-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}.size-container:hover{background-color:var(--kritzel-stroke-size-hover-background-color, #ebebeb)}.size-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-stroke-size-selected-background-color, #ebebeb)}`;
22001
+ const kritzelStrokeSizeCss = () => `:host{display:flex;align-items:flex-start;gap:0;padding:0;width:100%;box-sizing:border-box}.size-grid{width:100%;display:grid;grid-template-columns:repeat(6, 32px);gap:8px;justify-items:center}.size-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}.size-container:hover{background-color:var(--kritzel-stroke-size-hover-background-color, #ebebeb)}.size-container.selected{border-color:var(--kritzel-selection-border-color, #007AFF);background-color:var(--kritzel-stroke-size-selected-background-color, #ebebeb)}`;
22085
22002
 
22086
22003
  const KritzelStrokeSize = class {
22087
22004
  constructor(hostRef) {
@@ -22096,15 +22013,215 @@ const KritzelStrokeSize = class {
22096
22013
  this.sizeChange.emit(size);
22097
22014
  }
22098
22015
  render() {
22099
- return (h(Host, { key: 'd204d14384367ca8847ad30cab9801788c8d8fac' }, this.sizes.map(size => (h("div", { tabIndex: 0, class: {
22016
+ return (h(Host, { key: '891f899a41844bba03305e3dd7cbf428d4471ff9' }, h("div", { key: '773a756d54d0632c9ea6b3aebb743abad2274244', class: "size-grid" }, this.sizes.map(size => (h("div", { tabIndex: 0, class: {
22100
22017
  'size-container': true,
22101
22018
  'selected': this.selectedSize === size,
22102
- }, onClick: () => this.handleSizeClick(size) }, h("kritzel-color", { value: '#000000', size: size }))))));
22019
+ }, onClick: () => this.handleSizeClick(size) }, h("kritzel-color", { value: '#000000', size: size })))))));
22103
22020
  }
22104
22021
  };
22105
22022
  KritzelStrokeSize.style = kritzelStrokeSizeCss();
22106
22023
 
22107
- const kritzelTooltipCss = () => `:host{width:auto}.tooltip-content{position:relative;padding:8px 12px;border-radius:4px;width:fit-content;background-color:var(--kritzel-controls-tooltip-background-color, #ffffff);color:var(--kritzel-controls-tooltip-color, #000000);padding:var(--kritzel-controls-tooltip-padding, 8px);border-radius:var(--kritzel-controls-tooltip-border-radius, 16px);white-space:nowrap;box-shadow:var(--kritzel-controls-tooltip-box-shadow, 0 1px 6px rgba(0, 0, 0, 0.12))}`;
22024
+ const kritzelToolConfigCss = () => `.expand-toggle{background:none;border:none;cursor:pointer;padding:0;margin:0;display:flex;align-items:center;justify-content:center;width:32px;height:32px;color:var(--kritzel-icon-color, currentColor);transition:transform 0.2s ease}.expand-toggle:hover{opacity:0.7}.expand-toggle:focus{outline:none}.expand-toggle:focus-visible{outline:2px solid var(--kritzel-focus-color, #007acc);outline-offset:2px}.expand-toggle:active{transform:scale(0.95)}.divider{height:1px;background-color:var(--kritzel-divider-color, #e0e0e0);margin:4px 0;width:100%}`;
22025
+
22026
+ const KritzelToolConfig = class {
22027
+ constructor(hostRef) {
22028
+ registerInstance(this, hostRef);
22029
+ this.toolChange = createEvent(this, "toolChange");
22030
+ this.displayValuesChange = createEvent(this, "displayValuesChange");
22031
+ }
22032
+ tool;
22033
+ handleToolChange(newTool, oldTool) {
22034
+ const newConfig = KritzelToolConfigHelper.getToolConfig(newTool);
22035
+ // Maintain settings when switching between shape tools
22036
+ if (oldTool && newTool && newConfig?.type === 'shape') {
22037
+ const oldConfig = KritzelToolConfigHelper.getToolConfig(oldTool);
22038
+ if (oldConfig?.type === 'shape') {
22039
+ // Copy properties that should persist
22040
+ const propsToCopy = [
22041
+ newConfig.colorProperty, // strokeColor
22042
+ newConfig.sizeProperty, // strokeWidth
22043
+ newConfig.opacityProperty, // opacity
22044
+ 'fillColor' // shape specific
22045
+ ];
22046
+ propsToCopy.forEach(prop => {
22047
+ if (prop && oldTool[prop] !== undefined) {
22048
+ newTool[prop] = oldTool[prop];
22049
+ }
22050
+ });
22051
+ }
22052
+ }
22053
+ this.config = newConfig;
22054
+ if (this.config) {
22055
+ this.updatePalette();
22056
+ this.currentOpacity = newTool[this.config.opacityProperty] ?? 1;
22057
+ // Emit the values since they might have been updated from the old tool
22058
+ this.emitDisplayValues();
22059
+ }
22060
+ }
22061
+ isExpanded = false;
22062
+ toolChange;
22063
+ displayValuesChange;
22064
+ config;
22065
+ palette = [];
22066
+ currentOpacity = 1;
22067
+ updateTrigger = 0;
22068
+ handleSelectionChange() {
22069
+ if (this.tool instanceof KritzelSelectionTool) {
22070
+ this.config = KritzelToolConfigHelper.getToolConfig(this.tool);
22071
+ if (this.config) {
22072
+ this.updatePalette();
22073
+ this.currentOpacity = this.tool[this.config.opacityProperty] ?? 1;
22074
+ this.emitDisplayValues();
22075
+ }
22076
+ }
22077
+ }
22078
+ componentWillLoad() {
22079
+ this.config = KritzelToolConfigHelper.getToolConfig(this.tool);
22080
+ if (this.config) {
22081
+ this.updatePalette();
22082
+ this.currentOpacity = this.tool[this.config.opacityProperty] ?? 1;
22083
+ this.emitDisplayValues();
22084
+ }
22085
+ }
22086
+ emitDisplayValues() {
22087
+ if (!this.config)
22088
+ return;
22089
+ const color = this.tool[this.config.colorProperty];
22090
+ const opacity = this.currentOpacity;
22091
+ const size = this.tool[this.config.sizeProperty];
22092
+ const displayValues = {
22093
+ color: KritzelColorHelper.applyOpacity(color, opacity),
22094
+ size,
22095
+ };
22096
+ if (this.tool instanceof KritzelTextTool) {
22097
+ displayValues.fontFamily = this.tool.fontFamily;
22098
+ }
22099
+ this.displayValuesChange.emit(displayValues);
22100
+ }
22101
+ updatePalette() {
22102
+ if (!this.config)
22103
+ return;
22104
+ if (this.config.paletteSource === 'palettes') {
22105
+ // Brush tool has palettes[type]
22106
+ const brushTool = this.tool;
22107
+ this.palette = brushTool.palettes[brushTool.type] || [];
22108
+ }
22109
+ else if (this.config.paletteSource === 'palette') {
22110
+ // Line, Shape, Text tools have palette
22111
+ this.palette = this.tool.palette || [];
22112
+ }
22113
+ }
22114
+ handleToggleExpand = () => {
22115
+ this.isExpanded = !this.isExpanded;
22116
+ };
22117
+ handleColorChange = (event) => {
22118
+ this.tool[this.config.colorProperty] = event.detail;
22119
+ // Special handling for shape fill: when color (stroke) changes, update fill color if it's currently filled
22120
+ if (this.config.type === 'shape' || this.config.type === 'selection') {
22121
+ const tool = this.tool;
22122
+ if (tool.fillColor !== 'transparent') {
22123
+ tool.fillColor = event.detail;
22124
+ }
22125
+ }
22126
+ this.emitDisplayValues();
22127
+ this.toolChange.emit(this.tool);
22128
+ this.updateTrigger++;
22129
+ };
22130
+ handleSizeChange = (event) => {
22131
+ this.tool[this.config.sizeProperty] = event.detail;
22132
+ this.emitDisplayValues();
22133
+ this.toolChange.emit(this.tool);
22134
+ this.updateTrigger++;
22135
+ };
22136
+ handleOpacityChange = (event) => {
22137
+ this.tool[this.config.opacityProperty] = event.detail;
22138
+ this.currentOpacity = event.detail;
22139
+ this.emitDisplayValues();
22140
+ this.toolChange.emit(this.tool);
22141
+ };
22142
+ handlePropertyChange = (propertyName, value) => {
22143
+ // Special handling for shape fill
22144
+ if ((this.config.type === 'shape' || this.config.type === 'selection') && propertyName === 'fillColor') {
22145
+ const newFillColor = value === 'filled' ? this.tool[this.config.colorProperty] : 'transparent';
22146
+ this.tool.fillColor = newFillColor;
22147
+ // When switching to fill mode, also update stroke color to match
22148
+ if (value === 'filled') {
22149
+ this.tool[this.config.colorProperty] = newFillColor;
22150
+ }
22151
+ }
22152
+ // Special handling for brush type change
22153
+ else if (this.config.type === 'brush' && propertyName === 'type') {
22154
+ const brushTool = this.tool;
22155
+ brushTool.type = value;
22156
+ this.palette = brushTool.palettes[value];
22157
+ brushTool.color = this.palette[0];
22158
+ this.emitDisplayValues();
22159
+ }
22160
+ else {
22161
+ this.tool[propertyName] = value;
22162
+ // Emit display values for font family changes
22163
+ if (propertyName === 'fontFamily') {
22164
+ this.emitDisplayValues();
22165
+ }
22166
+ }
22167
+ this.toolChange.emit(this.tool);
22168
+ this.updateTrigger++;
22169
+ };
22170
+ getShapeFillValue() {
22171
+ const fillColor = this.tool.fillColor;
22172
+ return fillColor === 'transparent' ? 'transparent' : 'filled';
22173
+ }
22174
+ renderControl(control) {
22175
+ const value = this.tool[control.propertyName];
22176
+ switch (control.type) {
22177
+ case 'stroke-size':
22178
+ return (h("kritzel-stroke-size", { key: control.type, selectedSize: value, onSizeChange: this.handleSizeChange }));
22179
+ case 'font-size':
22180
+ return (h("kritzel-font-size", { key: control.type, selectedSize: value, fontFamily: this.tool.fontFamily, onSizeChange: this.handleSizeChange }));
22181
+ case 'line-endings':
22182
+ return (h("kritzel-line-endings", { key: control.type, value: value, strokeColor: this.tool[this.config.colorProperty], onValueChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
22183
+ case 'shape-fill':
22184
+ return (h("kritzel-shape-fill", { key: control.type, value: this.getShapeFillValue(), onValueChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
22185
+ case 'font-family':
22186
+ return (h("kritzel-font-family", { key: control.type, selectedFontFamily: value, onFontFamilyChange: (event) => this.handlePropertyChange(control.propertyName, event.detail) }));
22187
+ default:
22188
+ return null;
22189
+ }
22190
+ }
22191
+ render() {
22192
+ if (!this.config)
22193
+ return null;
22194
+ const shouldShowExpandButton = this.palette.length > 6 || this.config.type === 'text';
22195
+ // Separate size control from other controls
22196
+ const sizeControl = this.config.controls.find(c => c.type === 'stroke-size' || c.type === 'font-size');
22197
+ const otherControls = this.config.controls.filter(c => c.type !== 'stroke-size' && c.type !== 'font-size');
22198
+ return (h(Host, null, h("div", { style: {
22199
+ display: 'flex',
22200
+ flexDirection: 'row',
22201
+ gap: '8px',
22202
+ width: '100%',
22203
+ } }, h("div", { style: {
22204
+ display: 'flex',
22205
+ flexDirection: 'column',
22206
+ gap: '12px',
22207
+ flex: '1',
22208
+ } }, h("kritzel-color-palette", { colors: this.palette, selectedColor: this.tool[this.config.colorProperty], isExpanded: this.isExpanded, isOpaque: true, opacity: this.currentOpacity, onColorChange: this.handleColorChange }), sizeControl && this.renderControl(sizeControl), h("kritzel-opacity-slider", { value: this.tool[this.config.opacityProperty], previewColor: this.tool[this.config.colorProperty], onValueChange: this.handleOpacityChange }), otherControls.map((control) => [
22209
+ h("div", { class: "divider" }),
22210
+ this.renderControl(control),
22211
+ ])), shouldShowExpandButton && (h("div", { style: {
22212
+ display: 'flex',
22213
+ alignItems: 'flex-start',
22214
+ } }, h("button", { class: "expand-toggle", onClick: this.handleToggleExpand, title: this.isExpanded ? 'Collapse' : 'Expand' }, h("kritzel-icon", { name: this.isExpanded ? 'chevron-up' : 'chevron-down' })))))));
22215
+ }
22216
+ static get watchers() { return {
22217
+ "tool": [{
22218
+ "handleToolChange": 0
22219
+ }]
22220
+ }; }
22221
+ };
22222
+ KritzelToolConfig.style = kritzelToolConfigCss();
22223
+
22224
+ const kritzelTooltipCss = () => `:host{width:auto}.tooltip-content{position:relative;padding:8px 12px;border-radius:4px;width:fit-content;background-color:var(--kritzel-controls-tooltip-background-color, #ffffff);color:var(--kritzel-controls-tooltip-color, #000000);padding:var(--kritzel-controls-tooltip-padding, 12px);border-radius:var(--kritzel-controls-tooltip-border-radius, 16px);white-space:nowrap;box-shadow:var(--kritzel-controls-tooltip-box-shadow, 0 1px 6px rgba(0, 0, 0, 0.12))}`;
22108
22225
 
22109
22226
  const KritzelTooltip = class {
22110
22227
  constructor(hostRef) {
@@ -22121,8 +22238,10 @@ const KritzelTooltip = class {
22121
22238
  handleOutsideClick(event) {
22122
22239
  if (!this.isVisible)
22123
22240
  return;
22124
- const target = event.target;
22125
- if (!this.host.contains(target)) {
22241
+ // Check if click is inside the tooltip host or any of its shadow DOM content
22242
+ const path = event.composedPath();
22243
+ const isInsideTooltip = path.some(el => el === this.host);
22244
+ if (!isInsideTooltip) {
22126
22245
  this.tooltipClosed.emit();
22127
22246
  }
22128
22247
  }
@@ -22172,14 +22291,14 @@ const KritzelTooltip = class {
22172
22291
  }
22173
22292
  }
22174
22293
  render() {
22175
- return (h(Host, { key: 'c257f254b8ba6b95f251eac3136f5be282d23e30', style: {
22294
+ return (h(Host, { key: '104ad4a160e4f47454955696e5f0e7a28873c3f5', style: {
22176
22295
  position: 'fixed',
22177
22296
  zIndex: '9999',
22178
22297
  transition: 'opacity 0.3s ease-in-out, transform 0.3s ease-in-out',
22179
22298
  visibility: this.isVisible ? 'visible' : 'hidden',
22180
22299
  left: `${this.positionX}px`,
22181
22300
  bottom: `${this.positionY}px`,
22182
- } }, h("div", { key: '14f31dc995236a2309e2371ecef278a0e6374139', class: "tooltip-content", onClick: event => event.stopPropagation() }, h("slot", { key: '6dace4d6ed82878333f8771cf4670d53cea36873' }))));
22301
+ } }, h("div", { key: '283eec8d84ea29caefdb1f0c4f129c57d30bc133', class: "tooltip-content", onClick: event => event.stopPropagation(), onPointerDown: event => event.stopPropagation(), onMouseDown: event => event.stopPropagation() }, h("slot", { key: '1c6e2d9bcb69f8deec08b6c10384b4c7593babd3' }))));
22183
22302
  }
22184
22303
  };
22185
22304
  KritzelTooltip.style = kritzelTooltipCss();
@@ -22210,7 +22329,7 @@ const KritzelUtilityPanel = class {
22210
22329
  this.redo.emit();
22211
22330
  }
22212
22331
  render() {
22213
- return (h(Host, { key: '998008f3c138f18d0e3f5183c3280e646e6c8e73' }, h("button", { key: '2ea58a2df20e038e00c0090c44714efbc1221314', class: "utility-button", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event) }, h("kritzel-icon", { key: '1843d211731f83f2f828b17f0bc0cf83a76705e1', name: "undo" })), h("button", { key: '71064186570d8e849a634a4c68e96f3fba1ebdab', class: "utility-button", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event) }, h("kritzel-icon", { key: '12694a6dcddc1f893752d3edcc1deafe700cff5c', name: "redo" })), h("div", { key: '5bbfb5e535d9e0a861d227df57bd5cc7ab8c22fb', class: "utility-separator" }), h("button", { key: 'fda95410a66afa98c0abf6eb82e323f926ef596a', class: "utility-button" }, h("kritzel-icon", { key: '0d1d77f6d65481825c2edbdb38de821a2de7b043', name: "delete", onClick: () => this.delete.emit() }))));
22332
+ return (h(Host, { key: 'b0994bdfd10e1bfbad18d7df2810575b19a9e67d' }, h("button", { key: '4bcb8106f6977ff1472477b6a689942e7eedd8b7', class: "utility-button", disabled: !this.undoState?.canUndo, onClick: event => this.handleUndo(event) }, h("kritzel-icon", { key: '05c9a0b6a926eeb236cda9bf8c333dd6a0b563cb', name: "undo" })), h("button", { key: '88b008c4c37de9440da414617a0b2e144e47e830', class: "utility-button", disabled: !this.undoState?.canRedo, onClick: event => this.handleRedo(event) }, h("kritzel-icon", { key: 'ec921af2eca35fd1d1aad88d88d5f0a0804e4c44', name: "redo" })), h("div", { key: '94b7eec2edee8a41296726ab79d61bccba1940d8', class: "utility-separator" }), h("button", { key: '50be446ae7510d5133b7767e0dd382b7e1c87c3f', class: "utility-button" }, h("kritzel-icon", { key: 'd11d988dafc42c250b7361f5bba532fa9f81d97e', name: "delete", onClick: () => this.delete.emit() }))));
22214
22333
  }
22215
22334
  };
22216
22335
  KritzelUtilityPanel.style = kritzelUtilityPanelCss();
@@ -22355,4 +22474,4 @@ const KritzelWorkspaceManager = class {
22355
22474
  };
22356
22475
  KritzelWorkspaceManager.style = kritzelWorkspaceManagerCss();
22357
22476
 
22358
- export { KritzelColor as kritzel_color, KritzelColorPalette as kritzel_color_palette, KritzelContextMenu as kritzel_context_menu, KritzelControlBrushConfig as kritzel_control_brush_config, KritzelControlTextConfig as kritzel_control_text_config, KritzelControls as kritzel_controls, KritzelCursorTrail as kritzel_cursor_trail, KritzelDropdown as kritzel_dropdown, KritzelEditor as kritzel_editor, KritzelEngine as kritzel_engine, KritzelFont as kritzel_font, KritzelFontFamily as kritzel_font_family, KritzelFontSize as kritzel_font_size, KritzelIcon as kritzel_icon, KritzelMenu as kritzel_menu, KritzelMenuItem as kritzel_menu_item, KritzelPortal as kritzel_portal, KritzelSplitButton as kritzel_split_button, KritzelStrokeSize as kritzel_stroke_size, KritzelTooltip as kritzel_tooltip, KritzelUtilityPanel as kritzel_utility_panel, KritzelWorkspaceManager as kritzel_workspace_manager };
22477
+ export { KritzelColor as kritzel_color, KritzelColorPalette as kritzel_color_palette, KritzelContextMenu as kritzel_context_menu, KritzelControls as kritzel_controls, KritzelCursorTrail as kritzel_cursor_trail, KritzelDropdown as kritzel_dropdown, KritzelEditor as kritzel_editor, KritzelEngine as kritzel_engine, KritzelFont as kritzel_font, KritzelFontFamily as kritzel_font_family, KritzelFontSize as kritzel_font_size, KritzelIcon as kritzel_icon, KritzelLineEndings as kritzel_line_endings, KritzelMenu as kritzel_menu, KritzelMenuItem as kritzel_menu_item, KritzelOpacitySlider as kritzel_opacity_slider, KritzelPortal as kritzel_portal, KritzelShapeFill as kritzel_shape_fill, KritzelSplitButton as kritzel_split_button, KritzelStrokeSize as kritzel_stroke_size, KritzelToolConfig as kritzel_tool_config, KritzelTooltip as kritzel_tooltip, KritzelUtilityPanel as kritzel_utility_panel, KritzelWorkspaceManager as kritzel_workspace_manager };