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