kritzel-stencil 0.0.160 → 0.0.162

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 (220) hide show
  1. package/dist/cjs/{default-text-tool.config-D10FksvZ.js → default-line-tool.config-D1Ns0NmM.js} +3156 -929
  2. package/dist/cjs/default-line-tool.config-D1Ns0NmM.js.map +1 -0
  3. package/dist/cjs/index.cjs.js +131 -126
  4. package/dist/cjs/index.cjs.js.map +1 -1
  5. package/dist/cjs/kritzel-brush-style.cjs.entry.js +1 -1
  6. package/dist/cjs/kritzel-brush-style.entry.cjs.js.map +1 -1
  7. package/dist/cjs/kritzel-color_22.cjs.entry.js +538 -198
  8. package/dist/cjs/loader.cjs.js +1 -1
  9. package/dist/cjs/stencil.cjs.js +1 -1
  10. package/dist/collection/classes/core/core.class.js +140 -3
  11. package/dist/collection/classes/core/core.class.js.map +1 -1
  12. package/dist/collection/classes/core/reviver.class.js +8 -0
  13. package/dist/collection/classes/core/reviver.class.js.map +1 -1
  14. package/dist/collection/classes/core/store.class.js +8 -0
  15. package/dist/collection/classes/core/store.class.js.map +1 -1
  16. package/dist/collection/classes/handlers/line-handle.handler.js +383 -0
  17. package/dist/collection/classes/handlers/line-handle.handler.js.map +1 -0
  18. package/dist/collection/classes/handlers/move.handler.js +2 -2
  19. package/dist/collection/classes/handlers/move.handler.js.map +1 -1
  20. package/dist/collection/classes/handlers/resize.handler.js +42 -34
  21. package/dist/collection/classes/handlers/resize.handler.js.map +1 -1
  22. package/dist/collection/classes/handlers/rotation.handler.js +12 -8
  23. package/dist/collection/classes/handlers/rotation.handler.js.map +1 -1
  24. package/dist/collection/classes/managers/anchor.manager.js +874 -0
  25. package/dist/collection/classes/managers/anchor.manager.js.map +1 -0
  26. package/dist/collection/classes/managers/cursor.manager.js +117 -0
  27. package/dist/collection/classes/managers/cursor.manager.js.map +1 -0
  28. package/dist/collection/classes/objects/base-object.class.js +4 -2
  29. package/dist/collection/classes/objects/base-object.class.js.map +1 -1
  30. package/dist/collection/classes/objects/line.class.js +564 -0
  31. package/dist/collection/classes/objects/line.class.js.map +1 -0
  32. package/dist/collection/classes/objects/selection-group.class.js +4 -0
  33. package/dist/collection/classes/objects/selection-group.class.js.map +1 -1
  34. package/dist/collection/classes/registries/icon-registry.class.js +7 -1
  35. package/dist/collection/classes/registries/icon-registry.class.js.map +1 -1
  36. package/dist/collection/classes/tools/line-tool.class.js +172 -0
  37. package/dist/collection/classes/tools/line-tool.class.js.map +1 -0
  38. package/dist/collection/classes/tools/selection-tool.class.js +41 -8
  39. package/dist/collection/classes/tools/selection-tool.class.js.map +1 -1
  40. package/dist/collection/components/core/kritzel-cursor-trail/kritzel-cursor-trail.js +1 -1
  41. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js +11 -2
  42. package/dist/collection/components/core/kritzel-editor/kritzel-editor.js.map +1 -1
  43. package/dist/collection/components/core/kritzel-engine/kritzel-engine.css +0 -14
  44. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js +163 -21
  45. package/dist/collection/components/core/kritzel-engine/kritzel-engine.js.map +1 -1
  46. package/dist/collection/components/shared/kritzel-brush-style/kritzel-brush-style.css +0 -1
  47. package/dist/collection/components/shared/kritzel-color/kritzel-color.js +2 -2
  48. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.css +1 -1
  49. package/dist/collection/components/shared/kritzel-color-palette/kritzel-color-palette.js +1 -1
  50. package/dist/collection/components/shared/kritzel-dropdown/kritzel-dropdown.css +1 -1
  51. package/dist/collection/components/shared/kritzel-font/kritzel-font.js +1 -1
  52. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.css +1 -1
  53. package/dist/collection/components/shared/kritzel-font-family/kritzel-font-family.js +1 -1
  54. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.css +1 -1
  55. package/dist/collection/components/shared/kritzel-font-size/kritzel-font-size.js +1 -1
  56. package/dist/collection/components/shared/kritzel-menu/kritzel-menu.js +1 -1
  57. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.css +1 -2
  58. package/dist/collection/components/shared/kritzel-menu-item/kritzel-menu-item.js +2 -2
  59. package/dist/collection/components/shared/kritzel-portal/kritzel-portal.js +1 -1
  60. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.css +1 -1
  61. package/dist/collection/components/shared/kritzel-split-button/kritzel-split-button.js +1 -1
  62. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.css +1 -1
  63. package/dist/collection/components/shared/kritzel-stroke-size/kritzel-stroke-size.js +1 -1
  64. package/dist/collection/components/shared/kritzel-tooltip/kritzel-tooltip.js +4 -4
  65. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.css +1 -2
  66. package/dist/collection/components/ui/kritzel-context-menu/kritzel-context-menu.js +1 -1
  67. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.css +1 -1
  68. package/dist/collection/components/ui/kritzel-control-brush-config/kritzel-control-brush-config.js +2 -2
  69. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.css +1 -1
  70. package/dist/collection/components/ui/kritzel-control-text-config/kritzel-control-text-config.js +2 -2
  71. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.css +4 -4
  72. package/dist/collection/components/ui/kritzel-controls/kritzel-controls.js +3 -3
  73. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.css +1 -1
  74. package/dist/collection/components/ui/kritzel-utility-panel/kritzel-utility-panel.js +1 -1
  75. package/dist/collection/configs/default-engine-config.js +10 -0
  76. package/dist/collection/configs/default-engine-config.js.map +1 -1
  77. package/dist/collection/configs/default-line-tool.config.js +34 -0
  78. package/dist/collection/configs/default-line-tool.config.js.map +1 -0
  79. package/dist/collection/helpers/cursor.helper.js +58 -0
  80. package/dist/collection/helpers/cursor.helper.js.map +1 -0
  81. package/dist/collection/helpers/geometry.helper.js +42 -0
  82. package/dist/collection/helpers/geometry.helper.js.map +1 -1
  83. package/dist/collection/index.js +6 -0
  84. package/dist/collection/index.js.map +1 -1
  85. package/dist/collection/interfaces/anchor.interface.js +2 -0
  86. package/dist/collection/interfaces/anchor.interface.js.map +1 -0
  87. package/dist/collection/interfaces/arrow-head.interface.js +2 -0
  88. package/dist/collection/interfaces/arrow-head.interface.js.map +1 -0
  89. package/dist/collection/interfaces/engine-state.interface.js.map +1 -1
  90. package/dist/collection/interfaces/line-options.interface.js +2 -0
  91. package/dist/collection/interfaces/line-options.interface.js.map +1 -0
  92. package/dist/collection/interfaces/toolbar-control.interface.js.map +1 -1
  93. package/dist/components/index.js +4 -4
  94. package/dist/components/kritzel-brush-style.js +3 -3
  95. package/dist/components/kritzel-brush-style.js.map +1 -1
  96. package/dist/components/kritzel-color-palette.js +1 -1
  97. package/dist/components/kritzel-color.js +1 -1
  98. package/dist/components/kritzel-context-menu.js +1 -1
  99. package/dist/components/kritzel-control-brush-config.js +1 -1
  100. package/dist/components/kritzel-control-text-config.js +1 -1
  101. package/dist/components/kritzel-controls.js +1 -1
  102. package/dist/components/kritzel-cursor-trail.js +1 -1
  103. package/dist/components/kritzel-dropdown.js +1 -1
  104. package/dist/components/kritzel-editor.js +64 -23
  105. package/dist/components/kritzel-editor.js.map +1 -1
  106. package/dist/components/kritzel-engine.js +1 -1
  107. package/dist/components/kritzel-font-family.js +1 -1
  108. package/dist/components/kritzel-font-size.js +1 -1
  109. package/dist/components/kritzel-font.js +1 -1
  110. package/dist/components/kritzel-icon.js +1 -1
  111. package/dist/components/kritzel-menu-item.js +1 -1
  112. package/dist/components/kritzel-menu.js +1 -1
  113. package/dist/components/kritzel-portal.js +1 -1
  114. package/dist/components/kritzel-split-button.js +1 -1
  115. package/dist/components/kritzel-stroke-size.js +1 -1
  116. package/dist/components/kritzel-tooltip.js +1 -1
  117. package/dist/components/kritzel-utility-panel.js +1 -1
  118. package/dist/components/kritzel-workspace-manager.js +1 -1
  119. package/dist/components/{p-TdCTkEu0.js → p-7_lwv0zQ.js} +7 -7
  120. package/dist/components/p-7_lwv0zQ.js.map +1 -0
  121. package/dist/components/{p-CTP479Lf.js → p-9Fzdviju.js} +6 -6
  122. package/dist/components/{p-CTP479Lf.js.map → p-9Fzdviju.js.map} +1 -1
  123. package/dist/components/{p-DDmSxM5f.js → p-B6r22FSC.js} +5 -5
  124. package/dist/components/p-B6r22FSC.js.map +1 -0
  125. package/dist/components/{p-CLt3HMl6.js → p-B_3OZeom.js} +3 -3
  126. package/dist/components/{p-CLt3HMl6.js.map → p-B_3OZeom.js.map} +1 -1
  127. package/dist/components/{p-CIXPLjCu.js → p-BdwB-S9G.js} +3 -3
  128. package/dist/components/p-BdwB-S9G.js.map +1 -0
  129. package/dist/components/p-BixlbUD7.js +104 -0
  130. package/dist/components/p-BixlbUD7.js.map +1 -0
  131. package/dist/components/{p-D1uj4A4F.js → p-Brd9SxWS.js} +5 -5
  132. package/dist/components/p-Brd9SxWS.js.map +1 -0
  133. package/dist/components/{p-Ddh40W3x.js → p-CDteBYm9.js} +9 -9
  134. package/dist/components/p-CDteBYm9.js.map +1 -0
  135. package/dist/components/{p-BgznZoBH.js → p-CFH6XRL5.js} +5 -5
  136. package/dist/components/p-CFH6XRL5.js.map +1 -0
  137. package/dist/components/{p-B4kxkVe-.js → p-CRGwaUcp.js} +5 -5
  138. package/dist/components/p-CRGwaUcp.js.map +1 -0
  139. package/dist/components/{p-uuRJU2R1.js → p-Ck4lGnmt.js} +3 -3
  140. package/dist/components/{p-uuRJU2R1.js.map → p-Ck4lGnmt.js.map} +1 -1
  141. package/dist/components/{p-CK6no3mi.js → p-CkD1PQQX.js} +6 -6
  142. package/dist/components/{p-CK6no3mi.js.map → p-CkD1PQQX.js.map} +1 -1
  143. package/dist/components/{p-BQg4YML7.js → p-Cqr0Bah5.js} +12 -12
  144. package/dist/components/p-Cqr0Bah5.js.map +1 -0
  145. package/dist/components/{p-DTHqEUDc.js → p-CuhOrcET.js} +2861 -390
  146. package/dist/components/p-CuhOrcET.js.map +1 -0
  147. package/dist/components/{p-DAfkuR8U.js → p-CvLFRlQU.js} +5 -5
  148. package/dist/components/p-CvLFRlQU.js.map +1 -0
  149. package/dist/components/{p-1lIHoOlH.js → p-DKwJJuFb.js} +19 -19
  150. package/dist/components/p-DKwJJuFb.js.map +1 -0
  151. package/dist/components/{p-CsA9M6me.js → p-DZ7kxJUx.js} +8 -8
  152. package/dist/components/p-DZ7kxJUx.js.map +1 -0
  153. package/dist/components/{p-D4yvhd1d.js → p-LAsVgL2e.js} +4 -4
  154. package/dist/components/{p-D4yvhd1d.js.map → p-LAsVgL2e.js.map} +1 -1
  155. package/dist/components/{p-D5Wq4x4r.js → p-OFrACpZf.js} +3 -3
  156. package/dist/components/{p-D5Wq4x4r.js.map → p-OFrACpZf.js.map} +1 -1
  157. package/dist/components/{p-C2sWlNsJ.js → p-dMCB4tJA.js} +5 -5
  158. package/dist/components/p-dMCB4tJA.js.map +1 -0
  159. package/dist/components/{p-CBYBurdY.js → p-sokRZ7Vn.js} +49 -5
  160. package/dist/components/p-sokRZ7Vn.js.map +1 -0
  161. package/dist/esm/{default-text-tool.config-DzqpOikl.js → default-line-tool.config-C35m-d1Y.js} +3150 -930
  162. package/dist/esm/default-line-tool.config-C35m-d1Y.js.map +1 -0
  163. package/dist/esm/index.js +2 -2
  164. package/dist/esm/kritzel-brush-style.entry.js +1 -1
  165. package/dist/esm/kritzel-brush-style.entry.js.map +1 -1
  166. package/dist/esm/kritzel-color_22.entry.js +457 -117
  167. package/dist/esm/loader.js +1 -1
  168. package/dist/esm/stencil.js +1 -1
  169. package/dist/stencil/index.esm.js +1 -1
  170. package/dist/stencil/kritzel-brush-style.entry.esm.js.map +1 -1
  171. package/dist/stencil/p-385bab97.entry.js +2 -0
  172. package/dist/stencil/{p-d702c5af.entry.js.map → p-385bab97.entry.js.map} +1 -1
  173. package/dist/stencil/p-C35m-d1Y.js +2 -0
  174. package/dist/stencil/p-C35m-d1Y.js.map +1 -0
  175. package/dist/stencil/p-d142ef46.entry.js +10 -0
  176. package/dist/stencil/p-d142ef46.entry.js.map +1 -0
  177. package/dist/stencil/stencil.esm.js +1 -1
  178. package/dist/types/classes/core/core.class.d.ts +18 -0
  179. package/dist/types/classes/core/store.class.d.ts +3 -0
  180. package/dist/types/classes/handlers/line-handle.handler.d.ts +34 -0
  181. package/dist/types/classes/managers/anchor.manager.d.ts +160 -0
  182. package/dist/types/classes/managers/cursor.manager.d.ts +43 -0
  183. package/dist/types/classes/objects/line.class.d.ts +98 -0
  184. package/dist/types/classes/tools/line-tool.class.d.ts +17 -0
  185. package/dist/types/classes/tools/selection-tool.class.d.ts +4 -0
  186. package/dist/types/components/core/kritzel-engine/kritzel-engine.d.ts +4 -2
  187. package/dist/types/components.d.ts +7 -5
  188. package/dist/types/configs/default-line-tool.config.d.ts +2 -0
  189. package/dist/types/helpers/cursor.helper.d.ts +22 -0
  190. package/dist/types/helpers/geometry.helper.d.ts +10 -0
  191. package/dist/types/index.d.ts +6 -0
  192. package/dist/types/interfaces/anchor.interface.d.ts +137 -0
  193. package/dist/types/interfaces/arrow-head.interface.d.ts +26 -0
  194. package/dist/types/interfaces/engine-state.interface.d.ts +15 -0
  195. package/dist/types/interfaces/line-options.interface.d.ts +21 -0
  196. package/dist/types/interfaces/toolbar-control.interface.d.ts +17 -1
  197. package/package.json +1 -1
  198. package/dist/cjs/default-text-tool.config-D10FksvZ.js.map +0 -1
  199. package/dist/components/p-1lIHoOlH.js.map +0 -1
  200. package/dist/components/p-B4kxkVe-.js.map +0 -1
  201. package/dist/components/p-BQg4YML7.js.map +0 -1
  202. package/dist/components/p-BgznZoBH.js.map +0 -1
  203. package/dist/components/p-Bhtn9qay.js +0 -98
  204. package/dist/components/p-Bhtn9qay.js.map +0 -1
  205. package/dist/components/p-C2sWlNsJ.js.map +0 -1
  206. package/dist/components/p-CBYBurdY.js.map +0 -1
  207. package/dist/components/p-CIXPLjCu.js.map +0 -1
  208. package/dist/components/p-CsA9M6me.js.map +0 -1
  209. package/dist/components/p-D1uj4A4F.js.map +0 -1
  210. package/dist/components/p-DAfkuR8U.js.map +0 -1
  211. package/dist/components/p-DDmSxM5f.js.map +0 -1
  212. package/dist/components/p-DTHqEUDc.js.map +0 -1
  213. package/dist/components/p-Ddh40W3x.js.map +0 -1
  214. package/dist/components/p-TdCTkEu0.js.map +0 -1
  215. package/dist/esm/default-text-tool.config-DzqpOikl.js.map +0 -1
  216. package/dist/stencil/p-5475442e.entry.js +0 -10
  217. package/dist/stencil/p-5475442e.entry.js.map +0 -1
  218. package/dist/stencil/p-DzqpOikl.js +0 -2
  219. package/dist/stencil/p-DzqpOikl.js.map +0 -1
  220. package/dist/stencil/p-d702c5af.entry.js +0 -2
@@ -81,6 +81,48 @@ class KritzelGeometryHelper {
81
81
  const u = -((p1a.x - p2a.x) * (p1b.y - p1a.y) - (p1a.y - p2a.y) * (p1b.x - p1a.x)) / det;
82
82
  return t >= 0 && t <= 1 && u >= 0 && u <= 1;
83
83
  }
84
+ /**
85
+ * Finds the intersection point between a line segment and a line segment.
86
+ * Returns the intersection point or null if no intersection.
87
+ */
88
+ static getLineIntersectionPoint(p1a, p1b, p2a, p2b) {
89
+ const det = (p1b.x - p1a.x) * (p2b.y - p2a.y) - (p1b.y - p1a.y) * (p2b.x - p2a.x);
90
+ if (det === 0) {
91
+ return null; // Lines are parallel
92
+ }
93
+ const t = ((p2a.x - p1a.x) * (p2b.y - p2a.y) - (p2a.y - p1a.y) * (p2b.x - p2a.x)) / det;
94
+ const u = -((p1a.x - p2a.x) * (p1b.y - p1a.y) - (p1a.y - p2a.y) * (p1b.x - p1a.x)) / det;
95
+ if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
96
+ return {
97
+ x: p1a.x + t * (p1b.x - p1a.x),
98
+ y: p1a.y + t * (p1b.y - p1a.y)
99
+ };
100
+ }
101
+ return null;
102
+ }
103
+ /**
104
+ * Finds the closest intersection point between a line segment (from lineStart to lineEnd)
105
+ * and a polygon. Returns the intersection point closest to lineStart, or null if no intersection.
106
+ */
107
+ static getLinePolygonIntersection(lineStart, lineEnd, polygon) {
108
+ const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
109
+ let closestIntersection = null;
110
+ let closestDistance = Infinity;
111
+ for (let i = 0; i < points.length; i++) {
112
+ const edgeStart = points[i];
113
+ const edgeEnd = points[(i + 1) % points.length];
114
+ const intersection = this.getLineIntersectionPoint(lineStart, lineEnd, edgeStart, edgeEnd);
115
+ if (intersection) {
116
+ const distance = Math.sqrt(Math.pow(intersection.x - lineStart.x, 2) +
117
+ Math.pow(intersection.y - lineStart.y, 2));
118
+ if (distance < closestDistance) {
119
+ closestDistance = distance;
120
+ closestIntersection = intersection;
121
+ }
122
+ }
123
+ }
124
+ return closestIntersection;
125
+ }
84
126
  }
85
127
 
86
128
  class KritzelBaseObject {
@@ -189,10 +231,10 @@ class KritzelBaseObject {
189
231
  return this.rotation * (180 / Math.PI);
190
232
  }
191
233
  get centerX() {
192
- return this.translateX + this.totalWidth / 2;
234
+ return this.translateX + this.totalWidth / 2 / this.scale;
193
235
  }
194
236
  get centerY() {
195
- return this.translateY + this.totalHeight / 2;
237
+ return this.translateY + this.totalHeight / 2 / this.scale;
196
238
  }
197
239
  constructor() {
198
240
  this.id = this.generateId();
@@ -262,6 +304,8 @@ class KritzelBaseObject {
262
304
  this.translateX = x;
263
305
  this.translateY = y;
264
306
  this._core.store.state.objects.update(this);
307
+ // Update any lines that are anchored to this object (after position is updated)
308
+ this._core.anchorManager.updateAnchorsForObject(this.id);
265
309
  }
266
310
  rotate(value) {
267
311
  this.rotation = value;
@@ -14454,6 +14498,568 @@ class KritzelImage extends KritzelBaseObject {
14454
14498
  }
14455
14499
  }
14456
14500
 
14501
+ class KritzelLine extends KritzelBaseObject {
14502
+ __class__ = 'KritzelLine';
14503
+ startX;
14504
+ startY;
14505
+ endX;
14506
+ endY;
14507
+ controlX;
14508
+ controlY;
14509
+ stroke;
14510
+ strokeWidth;
14511
+ scale = 1;
14512
+ options;
14513
+ /** Anchor connection for the start point of the line */
14514
+ startAnchor;
14515
+ /** Anchor connection for the end point of the line */
14516
+ endAnchor;
14517
+ /** Arrow head configuration for start and end of line */
14518
+ arrows;
14519
+ isVisible = true;
14520
+ isDebugInfoVisible = true;
14521
+ isCompleted = false;
14522
+ _adjustedPoints = null;
14523
+ get d() {
14524
+ if (this.controlX !== undefined && this.controlY !== undefined) {
14525
+ return `M ${this.startX} ${this.startY} Q ${this.controlX} ${this.controlY} ${this.endX} ${this.endY}`;
14526
+ }
14527
+ return `M ${this.startX} ${this.startY} L ${this.endX} ${this.endY}`;
14528
+ }
14529
+ get viewBox() {
14530
+ return `${this.x} ${this.y} ${this.width} ${this.height}`;
14531
+ }
14532
+ constructor(config) {
14533
+ super();
14534
+ this.options = config;
14535
+ this.startX = config?.startX ?? 0;
14536
+ this.startY = config?.startY ?? 0;
14537
+ this.endX = config?.endX ?? 0;
14538
+ this.endY = config?.endY ?? 0;
14539
+ this.controlX = config?.controlX;
14540
+ this.controlY = config?.controlY;
14541
+ this.translateX = config?.translateX ?? 0;
14542
+ this.translateY = config?.translateY ?? 0;
14543
+ this.scale = config?.scale ?? 1;
14544
+ this.strokeWidth = config?.strokeWidth ?? 4;
14545
+ this.stroke = config?.stroke ?? '#000000';
14546
+ this.startAnchor = config?.startAnchor;
14547
+ this.endAnchor = config?.endAnchor;
14548
+ this.arrows = config?.arrows;
14549
+ this.updateDimensions();
14550
+ }
14551
+ static create(core, options) {
14552
+ const object = new KritzelLine();
14553
+ object._core = core;
14554
+ object.id = object.generateId();
14555
+ object.workspaceId = core.store.state.activeWorkspace.id;
14556
+ object.options = options;
14557
+ object.startX = options?.startX ?? 0;
14558
+ object.startY = options?.startY ?? 0;
14559
+ object.endX = options?.endX ?? 0;
14560
+ object.endY = options?.endY ?? 0;
14561
+ object.controlX = options?.controlX;
14562
+ object.controlY = options?.controlY;
14563
+ object.translateX = options?.translateX ?? 0;
14564
+ object.translateY = options?.translateY ?? 0;
14565
+ object.scale = options?.scale ?? 1;
14566
+ object.strokeWidth = options?.strokeWidth ?? 4;
14567
+ object.stroke = options?.stroke ?? '#000000';
14568
+ object.startAnchor = options?.startAnchor;
14569
+ object.endAnchor = options?.endAnchor;
14570
+ object.arrows = options?.arrows;
14571
+ object.zIndex = core.store.currentZIndex;
14572
+ object.updateDimensions();
14573
+ return object;
14574
+ }
14575
+ resize(x, y, width, height) {
14576
+ if (width <= 1 || height <= 1) {
14577
+ return;
14578
+ }
14579
+ const scaleX = width / this.width;
14580
+ const scaleY = height / this.height;
14581
+ // Scale the line endpoints
14582
+ this.startX = this.startX * scaleX;
14583
+ this.startY = this.startY * scaleY;
14584
+ this.endX = this.endX * scaleX;
14585
+ this.endY = this.endY * scaleY;
14586
+ if (this.controlX !== undefined && this.controlY !== undefined) {
14587
+ this.controlX = this.controlX * scaleX;
14588
+ this.controlY = this.controlY * scaleY;
14589
+ }
14590
+ // Recalculate dimensions from scaled endpoints
14591
+ const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
14592
+ this.width = maxX - minX;
14593
+ this.height = maxY - minY;
14594
+ this.x = minX;
14595
+ this.y = minY;
14596
+ // Set the new position
14597
+ this.translateX = x;
14598
+ this.translateY = y;
14599
+ this._adjustedPoints = null;
14600
+ this._core.store.state.objects.update(this);
14601
+ // Update anchors after the line is updated
14602
+ this._core.anchorManager.updateAnchorsForObject(this.id);
14603
+ if (this.startAnchor) {
14604
+ this._core.anchorManager.updateAnchorsForObject(this.startAnchor.objectId);
14605
+ }
14606
+ if (this.endAnchor) {
14607
+ this._core.anchorManager.updateAnchorsForObject(this.endAnchor.objectId);
14608
+ }
14609
+ }
14610
+ rotate(value) {
14611
+ this.rotation = value;
14612
+ this._adjustedPoints = null;
14613
+ this._core.store.state.objects.update(this);
14614
+ }
14615
+ move(startX, startY, endX, endY) {
14616
+ const deltaX = (startX - endX) / this._core.store.state.scale;
14617
+ const deltaY = (startY - endY) / this._core.store.state.scale;
14618
+ this.translateX += deltaX;
14619
+ this.translateY += deltaY;
14620
+ // If the line is anchored, we need to ensure the anchored endpoints stay at the target
14621
+ if (this.startAnchor) {
14622
+ this._core.anchorManager.updateAnchorsForObject(this.startAnchor.objectId);
14623
+ }
14624
+ if (this.endAnchor) {
14625
+ this._core.anchorManager.updateAnchorsForObject(this.endAnchor.objectId);
14626
+ }
14627
+ this._adjustedPoints = null;
14628
+ this._core.store.state.objects.update(this);
14629
+ }
14630
+ hitTest(x, y) {
14631
+ const halfStroke = this.strokeWidth / this.scale / 2;
14632
+ if (this._adjustedPoints === null) {
14633
+ this._adjustedPoints = this.computeAdjustedPoints();
14634
+ }
14635
+ // For curved lines, use distance to the Bezier curve
14636
+ if (this.controlX !== undefined && this.controlY !== undefined) {
14637
+ const distance = this.pointToBezierDistance(x, y);
14638
+ return distance <= halfStroke + 2;
14639
+ }
14640
+ // For straight lines, use distance to line segment
14641
+ const p1 = this._adjustedPoints[0];
14642
+ const p2 = this._adjustedPoints[1];
14643
+ const distance = this.pointToLineSegmentDistance(x, y, p1[0], p1[1], p2[0], p2[1]);
14644
+ return distance <= halfStroke + 2; // Add a small tolerance for easier selection
14645
+ }
14646
+ hitTestPolygon(polygon) {
14647
+ const halfStroke = this.strokeWidth / this.scale / 2;
14648
+ if (this._adjustedPoints === null) {
14649
+ this._adjustedPoints = this.computeAdjustedPoints();
14650
+ }
14651
+ const polyPoints = [
14652
+ { x: polygon.bottomLeft.x, y: polygon.bottomLeft.y },
14653
+ { x: polygon.bottomRight.x, y: polygon.bottomRight.y },
14654
+ { x: polygon.topRight.x, y: polygon.topRight.y },
14655
+ { x: polygon.topLeft.x, y: polygon.topLeft.y },
14656
+ ];
14657
+ // Check if any endpoint is inside the polygon
14658
+ for (const [px, py] of this._adjustedPoints) {
14659
+ if (KritzelGeometryHelper.isPointInPolygon({ x: px, y: py }, polyPoints)) {
14660
+ return true;
14661
+ }
14662
+ }
14663
+ // Check if any polygon vertex is on the line/curve
14664
+ for (const pt of polyPoints) {
14665
+ if (this.hitTest(pt.x, pt.y)) {
14666
+ return true;
14667
+ }
14668
+ }
14669
+ // For curved lines, sample points along the curve and check if any are inside the polygon
14670
+ if (this.controlX !== undefined && this.controlY !== undefined) {
14671
+ const p0 = this._adjustedPoints[0];
14672
+ const p2 = this._adjustedPoints[1];
14673
+ const controlAdjusted = this.computeAdjustedControlPoint();
14674
+ const samples = 20;
14675
+ for (let i = 1; i < samples; i++) {
14676
+ const t = i / samples;
14677
+ const oneMinusT = 1 - t;
14678
+ const bx = oneMinusT * oneMinusT * p0[0] + 2 * oneMinusT * t * controlAdjusted[0] + t * t * p2[0];
14679
+ const by = oneMinusT * oneMinusT * p0[1] + 2 * oneMinusT * t * controlAdjusted[1] + t * t * p2[1];
14680
+ if (KritzelGeometryHelper.isPointInPolygon({ x: bx, y: by }, polyPoints)) {
14681
+ return true;
14682
+ }
14683
+ // Also check if curve point is within stroke distance of polygon edges
14684
+ for (let j = 0; j < polyPoints.length; j++) {
14685
+ const q1 = polyPoints[j];
14686
+ const q2 = polyPoints[(j + 1) % polyPoints.length];
14687
+ const d = this.pointToLineSegmentDistance(bx, by, q1.x, q1.y, q2.x, q2.y);
14688
+ if (d <= halfStroke) {
14689
+ return true;
14690
+ }
14691
+ }
14692
+ }
14693
+ return false;
14694
+ }
14695
+ // For straight lines, check if line intersects any polygon edge
14696
+ const p1 = { x: this._adjustedPoints[0][0], y: this._adjustedPoints[0][1] };
14697
+ const p2 = { x: this._adjustedPoints[1][0], y: this._adjustedPoints[1][1] };
14698
+ for (let j = 0; j < polyPoints.length; j++) {
14699
+ const q1 = polyPoints[j];
14700
+ const q2 = polyPoints[(j + 1) % polyPoints.length];
14701
+ if (KritzelGeometryHelper.intersectLines(p1, p2, q1, q2)) {
14702
+ return true;
14703
+ }
14704
+ // Check distance from polygon edges to line segment
14705
+ const d1 = this.pointToLineSegmentDistance(q1.x, q1.y, p1.x, p1.y, p2.x, p2.y);
14706
+ const d2 = this.pointToLineSegmentDistance(q2.x, q2.y, p1.x, p1.y, p2.x, p2.y);
14707
+ const d3 = this.pointToLineSegmentDistance(p1.x, p1.y, q1.x, q1.y, q2.x, q2.y);
14708
+ const d4 = this.pointToLineSegmentDistance(p2.x, p2.y, q1.x, q1.y, q2.x, q2.y);
14709
+ const minD = Math.min(d1, d2, d3, d4);
14710
+ if (minD <= halfStroke) {
14711
+ return true;
14712
+ }
14713
+ }
14714
+ return false;
14715
+ }
14716
+ updatePosition(x, y) {
14717
+ this.translateX = x;
14718
+ this.translateY = y;
14719
+ this._adjustedPoints = null;
14720
+ this._core.store.state.objects.update(this);
14721
+ }
14722
+ /**
14723
+ * Updates a specific endpoint of the line (start or end).
14724
+ * The coordinates are in viewBox-local space (relative to x, y origin).
14725
+ * This method recalculates the bounding box dimensions.
14726
+ *
14727
+ * @param handleType - Which endpoint to update: 'start' or 'end'
14728
+ * @param newX - New X coordinate in viewBox-local space
14729
+ * @param newY - New Y coordinate in viewBox-local space
14730
+ */
14731
+ updateEndpoint(handleType, newX, newY) {
14732
+ // Update the appropriate endpoint
14733
+ if (handleType === 'start') {
14734
+ this.startX = newX;
14735
+ this.startY = newY;
14736
+ }
14737
+ else {
14738
+ this.endX = newX;
14739
+ this.endY = newY;
14740
+ }
14741
+ const oldWidth = this.width;
14742
+ const oldHeight = this.height;
14743
+ // Recalculate the bounding box (viewBox dimensions)
14744
+ const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
14745
+ // Calculate the change in origin (in local coordinates)
14746
+ const deltaX = minX - this.x;
14747
+ const deltaY = minY - this.y;
14748
+ const newWidth = maxX - minX;
14749
+ const newHeight = maxY - minY;
14750
+ const deltaWidth = newWidth - oldWidth;
14751
+ const deltaHeight = newHeight - oldHeight;
14752
+ const deltaCx = deltaWidth / 2;
14753
+ const deltaCy = deltaHeight / 2;
14754
+ // Update viewBox dimensions
14755
+ this.x = minX;
14756
+ this.y = minY;
14757
+ this.width = newWidth;
14758
+ this.height = newHeight;
14759
+ // Rotate the delta to world coordinates before applying to translate
14760
+ const cos = Math.cos(this.rotation);
14761
+ const sin = Math.sin(this.rotation);
14762
+ // We need to compensate for:
14763
+ // 1. The shift of the top-left corner (deltaX, deltaY)
14764
+ // 2. The shift of the center point (deltaCx, deltaCy) which affects rotation pivot
14765
+ // Formula: DeltaT = R(DeltaPos + DeltaCenter) - DeltaCenter
14766
+ const vx = deltaX + deltaCx;
14767
+ const vy = deltaY + deltaCy;
14768
+ const rotatedVx = vx * cos - vy * sin;
14769
+ const rotatedVy = vx * sin + vy * cos;
14770
+ const correctionX = rotatedVx - deltaCx;
14771
+ const correctionY = rotatedVy - deltaCy;
14772
+ // Adjust translateX/Y to compensate for the origin shift
14773
+ // so the line stays visually in the same position
14774
+ this.translateX += correctionX / this.scale;
14775
+ this.translateY += correctionY / this.scale;
14776
+ // Clear cached adjusted points
14777
+ this._adjustedPoints = null;
14778
+ this._core.store.state.objects.update(this);
14779
+ }
14780
+ updateControlPoint(newX, newY) {
14781
+ this.controlX = newX;
14782
+ this.controlY = newY;
14783
+ const oldWidth = this.width;
14784
+ const oldHeight = this.height;
14785
+ // Recalculate the bounding box (viewBox dimensions)
14786
+ const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
14787
+ // Calculate the change in origin (in local coordinates)
14788
+ const deltaX = minX - this.x;
14789
+ const deltaY = minY - this.y;
14790
+ const newWidth = maxX - minX;
14791
+ const newHeight = maxY - minY;
14792
+ const deltaWidth = newWidth - oldWidth;
14793
+ const deltaHeight = newHeight - oldHeight;
14794
+ const deltaCx = deltaWidth / 2;
14795
+ const deltaCy = deltaHeight / 2;
14796
+ // Update viewBox dimensions
14797
+ this.x = minX;
14798
+ this.y = minY;
14799
+ this.width = newWidth;
14800
+ this.height = newHeight;
14801
+ // Rotate the delta to world coordinates before applying to translate
14802
+ const cos = Math.cos(this.rotation);
14803
+ const sin = Math.sin(this.rotation);
14804
+ // We need to compensate for:
14805
+ // 1. The shift of the top-left corner (deltaX, deltaY)
14806
+ // 2. The shift of the center point (deltaCx, deltaCy) which affects rotation pivot
14807
+ // Formula: DeltaT = R(DeltaPos + DeltaCenter) - DeltaCenter
14808
+ const vx = deltaX + deltaCx;
14809
+ const vy = deltaY + deltaCy;
14810
+ const rotatedVx = vx * cos - vy * sin;
14811
+ const rotatedVy = vx * sin + vy * cos;
14812
+ const correctionX = rotatedVx - deltaCx;
14813
+ const correctionY = rotatedVy - deltaCy;
14814
+ // Adjust translateX/Y to compensate for the origin shift
14815
+ // so the line stays visually in the same position
14816
+ this.translateX += correctionX / this.scale;
14817
+ this.translateY += correctionY / this.scale;
14818
+ // Clear cached adjusted points
14819
+ this._adjustedPoints = null;
14820
+ this._core.store.state.objects.update(this);
14821
+ }
14822
+ computeAdjustedPoints() {
14823
+ const points = [
14824
+ [this.startX, this.startY],
14825
+ [this.endX, this.endY],
14826
+ ];
14827
+ const angle = this.rotation;
14828
+ const cos = Math.cos(angle);
14829
+ const sin = Math.sin(angle);
14830
+ const xs = points.map(p => p[0]);
14831
+ const ys = points.map(p => p[1]);
14832
+ const pivot = {
14833
+ x: (Math.min(...xs) + Math.max(...xs)) / 2,
14834
+ y: (Math.min(...ys) + Math.max(...ys)) / 2,
14835
+ };
14836
+ const { x: cx, y: cy } = pivot;
14837
+ const rotatedPoints = points.map(([x, y]) => {
14838
+ const dx = x - cx;
14839
+ const dy = y - cy;
14840
+ return [cx + dx * cos - dy * sin, cy + dx * sin + dy * cos];
14841
+ });
14842
+ return rotatedPoints.map(([px, py]) => [(px - this.x) / this.scale + this.translateX, (py - this.y) / this.scale + this.translateY]);
14843
+ }
14844
+ pointToLineSegmentDistance(x, y, x1, y1, x2, y2) {
14845
+ const A = x - x1;
14846
+ const B = y - y1;
14847
+ const C = x2 - x1;
14848
+ const D = y2 - y1;
14849
+ const dot = A * C + B * D;
14850
+ const len_sq = C * C + D * D;
14851
+ let param = -1;
14852
+ if (len_sq !== 0) {
14853
+ param = dot / len_sq;
14854
+ }
14855
+ let xx, yy;
14856
+ if (param < 0) {
14857
+ xx = x1;
14858
+ yy = y1;
14859
+ }
14860
+ else if (param > 1) {
14861
+ xx = x2;
14862
+ yy = y2;
14863
+ }
14864
+ else {
14865
+ xx = x1 + param * C;
14866
+ yy = y1 + param * D;
14867
+ }
14868
+ const dx = x - xx;
14869
+ const dy = y - yy;
14870
+ return Math.sqrt(dx * dx + dy * dy);
14871
+ }
14872
+ /**
14873
+ * Calculates the minimum distance from a point to a quadratic Bezier curve.
14874
+ * Uses sampling along the curve to find the closest point.
14875
+ */
14876
+ pointToBezierDistance(x, y) {
14877
+ if (this._adjustedPoints === null) {
14878
+ this._adjustedPoints = this.computeAdjustedPoints();
14879
+ }
14880
+ const p0 = this._adjustedPoints[0];
14881
+ const p2 = this._adjustedPoints[1];
14882
+ // Calculate the adjusted control point
14883
+ const controlAdjusted = this.computeAdjustedControlPoint();
14884
+ let minDistance = Infinity;
14885
+ const samples = 20; // Number of samples along the curve
14886
+ for (let i = 0; i <= samples; i++) {
14887
+ const t = i / samples;
14888
+ const oneMinusT = 1 - t;
14889
+ // Quadratic Bezier: B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
14890
+ const bx = oneMinusT * oneMinusT * p0[0] + 2 * oneMinusT * t * controlAdjusted[0] + t * t * p2[0];
14891
+ const by = oneMinusT * oneMinusT * p0[1] + 2 * oneMinusT * t * controlAdjusted[1] + t * t * p2[1];
14892
+ const dx = x - bx;
14893
+ const dy = y - by;
14894
+ const distance = Math.sqrt(dx * dx + dy * dy);
14895
+ if (distance < minDistance) {
14896
+ minDistance = distance;
14897
+ }
14898
+ }
14899
+ return minDistance;
14900
+ }
14901
+ /**
14902
+ * Computes the adjusted control point in world coordinates, accounting for rotation and translation.
14903
+ */
14904
+ computeAdjustedControlPoint() {
14905
+ if (this.controlX === undefined || this.controlY === undefined) {
14906
+ // Return midpoint if no control point
14907
+ return [(this.startX + this.endX) / 2, (this.startY + this.endY) / 2];
14908
+ }
14909
+ const angle = this.rotation;
14910
+ const cos = Math.cos(angle);
14911
+ const sin = Math.sin(angle);
14912
+ // Calculate pivot (center of bounding box for endpoints)
14913
+ const xs = [this.startX, this.endX];
14914
+ const ys = [this.startY, this.endY];
14915
+ const pivot = {
14916
+ x: (Math.min(...xs) + Math.max(...xs)) / 2,
14917
+ y: (Math.min(...ys) + Math.max(...ys)) / 2,
14918
+ };
14919
+ const { x: cx, y: cy } = pivot;
14920
+ // Rotate control point around pivot
14921
+ const dx = this.controlX - cx;
14922
+ const dy = this.controlY - cy;
14923
+ const rotatedX = cx + dx * cos - dy * sin;
14924
+ const rotatedY = cy + dx * sin + dy * cos;
14925
+ // Transform to world coordinates
14926
+ return [(rotatedX - this.x) / this.scale + this.translateX, (rotatedY - this.y) / this.scale + this.translateY];
14927
+ }
14928
+ get rotatedPolygon() {
14929
+ const padding = this.padding;
14930
+ // Use the bounding box which accounts for curve extrema
14931
+ const { minX, minY, maxX, maxY } = KritzelLine.calculateBoundingBox(this.startX, this.startY, this.endX, this.endY, this.controlX, this.controlY, this.strokeWidth);
14932
+ // Convert to local coordinates (relative to this.x, this.y)
14933
+ const localMinX = minX - this.x + padding;
14934
+ const localMinY = minY - this.y + padding;
14935
+ const localMaxX = maxX - this.x + padding;
14936
+ const localMaxY = maxY - this.y + padding;
14937
+ const c1 = { x: localMinX, y: localMinY }; // top-left
14938
+ const c2 = { x: localMaxX, y: localMinY }; // top-right
14939
+ const c3 = { x: localMaxX, y: localMaxY }; // bottom-right
14940
+ const c4 = { x: localMinX, y: localMaxY }; // bottom-left
14941
+ const cx = this.totalWidth / 2;
14942
+ const cy = this.totalHeight / 2;
14943
+ const angle = this.rotation;
14944
+ const cos = Math.cos(angle);
14945
+ const sin = Math.sin(angle);
14946
+ const transform = (p) => {
14947
+ const rx = (p.x - cx) * cos - (p.y - cy) * sin + cx;
14948
+ const ry = (p.x - cx) * sin + (p.y - cy) * cos + cy;
14949
+ return {
14950
+ x: rx / this.scale + this.translateX,
14951
+ y: ry / this.scale + this.translateY,
14952
+ };
14953
+ };
14954
+ return {
14955
+ topLeft: transform(c1),
14956
+ topRight: transform(c2),
14957
+ bottomRight: transform(c3),
14958
+ bottomLeft: transform(c4),
14959
+ };
14960
+ }
14961
+ /**
14962
+ * Calculates the tight bounding box for a quadratic Bezier curve or straight line.
14963
+ * For curves, it finds the extrema points where the derivative equals zero.
14964
+ */
14965
+ static calculateBoundingBox(startX, startY, endX, endY, controlX, controlY, strokeWidth) {
14966
+ let minX = Math.min(startX, endX);
14967
+ let minY = Math.min(startY, endY);
14968
+ let maxX = Math.max(startX, endX);
14969
+ let maxY = Math.max(startY, endY);
14970
+ if (controlX !== undefined && controlY !== undefined) {
14971
+ // For a quadratic Bezier: B(t) = (1-t)²P₀ + 2(1-t)tP₁ + t²P₂
14972
+ // Derivative: B'(t) = 2(1-t)(P₁-P₀) + 2t(P₂-P₁) = 0
14973
+ // Solving for t: t = (P₀ - P₁) / (P₀ - 2P₁ + P₂)
14974
+ // Find extrema for X
14975
+ const aX = startX - 2 * controlX + endX;
14976
+ if (aX !== 0) {
14977
+ const tX = (startX - controlX) / aX;
14978
+ if (tX > 0 && tX < 1) {
14979
+ const extremaX = (1 - tX) * (1 - tX) * startX + 2 * (1 - tX) * tX * controlX + tX * tX * endX;
14980
+ minX = Math.min(minX, extremaX);
14981
+ maxX = Math.max(maxX, extremaX);
14982
+ }
14983
+ }
14984
+ // Find extrema for Y
14985
+ const aY = startY - 2 * controlY + endY;
14986
+ if (aY !== 0) {
14987
+ const tY = (startY - controlY) / aY;
14988
+ if (tY > 0 && tY < 1) {
14989
+ const extremaY = (1 - tY) * (1 - tY) * startY + 2 * (1 - tY) * tY * controlY + tY * tY * endY;
14990
+ minY = Math.min(minY, extremaY);
14991
+ maxY = Math.max(maxY, extremaY);
14992
+ }
14993
+ }
14994
+ }
14995
+ const halfStroke = strokeWidth / 2;
14996
+ return {
14997
+ minX: minX - halfStroke,
14998
+ minY: minY - halfStroke,
14999
+ maxX: maxX + halfStroke,
15000
+ maxY: maxY + halfStroke,
15001
+ };
15002
+ }
15003
+ calculateBoundingBox() {
15004
+ return KritzelLine.calculateBoundingBox(this.startX, this.startY, this.endX, this.endY, this.controlX, this.controlY, this.strokeWidth);
15005
+ }
15006
+ updateDimensions() {
15007
+ const { minX, minY, maxX, maxY } = this.calculateBoundingBox();
15008
+ this.width = maxX - minX;
15009
+ this.height = maxY - minY;
15010
+ this.x = minX;
15011
+ this.y = minY;
15012
+ this.translateX = (this.x + this.translateX) / this.scale;
15013
+ this.translateY = (this.y + this.translateY) / this.scale;
15014
+ }
15015
+ /** Get unique marker ID for SVG defs at the start of the line */
15016
+ get startMarkerId() {
15017
+ return `arrow-start-${this.id}`;
15018
+ }
15019
+ /** Get unique marker ID for SVG defs at the end of the line */
15020
+ get endMarkerId() {
15021
+ return `arrow-end-${this.id}`;
15022
+ }
15023
+ /** Get the arrow size for start or end, defaulting to strokeWidth * 3 */
15024
+ getArrowSize(end) {
15025
+ const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
15026
+ return config?.size ?? this.strokeWidth * 3;
15027
+ }
15028
+ /** Get the arrow fill color for start or end, defaulting to stroke color */
15029
+ getArrowFill(end) {
15030
+ const config = end === 'start' ? this.arrows?.start : this.arrows?.end;
15031
+ return config?.fill ?? this.stroke;
15032
+ }
15033
+ /**
15034
+ * Generate SVG path data for an arrow head based on the given style.
15035
+ * The path is designed to fit in a 10x10 viewBox with the tip at (10, 5).
15036
+ * @param style The arrow head style
15037
+ * @returns SVG path data string
15038
+ */
15039
+ getArrowPath(style = 'triangle') {
15040
+ switch (style) {
15041
+ case 'triangle':
15042
+ return 'M 0 0 L 10 5 L 0 10 z';
15043
+ case 'open':
15044
+ return 'M 0 0 L 10 5 L 0 10';
15045
+ case 'diamond':
15046
+ return 'M 0 5 L 5 0 L 10 5 L 5 10 z';
15047
+ case 'circle':
15048
+ return 'M 5,0 A 5,5 0 1,1 5,10 A 5,5 0 1,1 5,0';
15049
+ default:
15050
+ return 'M 0 0 L 10 5 L 0 10 z';
15051
+ }
15052
+ }
15053
+ /** Check if start arrow is enabled */
15054
+ get hasStartArrow() {
15055
+ return this.arrows?.start?.enabled === true;
15056
+ }
15057
+ /** Check if end arrow is enabled */
15058
+ get hasEndArrow() {
15059
+ return this.arrows?.end?.enabled === true;
15060
+ }
15061
+ }
15062
+
14457
15063
  exports.KritzelMouseButton = void 0;
14458
15064
  (function (KritzelMouseButton) {
14459
15065
  KritzelMouseButton[KritzelMouseButton["Left"] = 0] = "Left";
@@ -14678,198 +15284,318 @@ class KritzelBrushTool extends KritzelBaseTool {
14678
15284
  }
14679
15285
  }
14680
15286
 
14681
- class KritzelEraserTool extends KritzelBaseTool {
14682
- touchStartTimeout = null;
14683
- constructor(core) {
14684
- super(core);
14685
- }
14686
- handlePointerDown(event) {
14687
- if (event.pointerType === 'mouse') {
14688
- if (KritzelEventHelper.isLeftClick(event)) {
14689
- this._core.store.state.isErasing = true;
14690
- }
14691
- }
14692
- if (event.pointerType === 'touch') {
14693
- this.touchStartTimeout = setTimeout(() => {
14694
- if (this._core.store.state.pointers.size === 1 && !this._core.store.state.isScaling) {
14695
- this._core.store.state.isErasing = true;
14696
- }
14697
- }, 80);
14698
- }
15287
+ class KritzelSelectionGroup extends KritzelBaseObject {
15288
+ __class__ = 'KritzelSelectionGroup';
15289
+ // Store only object IDs instead of full objects
15290
+ objectIds = [];
15291
+ // Store snapshots of object state for transformations (rotation, resize)
15292
+ unchangedObjectSnapshots = new Map();
15293
+ snapshotRotation = 0;
15294
+ minX;
15295
+ maxX;
15296
+ minY;
15297
+ maxY;
15298
+ // Getter to retrieve actual objects from the store by their IDs
15299
+ get objects() {
15300
+ return this.objectIds
15301
+ .map(id => {
15302
+ const found = this._core.store.state.objects.filter(obj => obj.id === id);
15303
+ return found.length > 0 ? found[0] : null;
15304
+ })
15305
+ .filter(obj => obj !== null);
14699
15306
  }
14700
- handlePointerMove(event) {
14701
- if (event.pointerType === 'mouse') {
14702
- if (this._core.store.state.isErasing) {
14703
- const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
14704
- if (selectedObjects.length === 0)
14705
- return;
14706
- const x = this._core.store.state.pointerX;
14707
- const y = this._core.store.state.pointerY;
14708
- selectedObjects.forEach(selectedObject => {
14709
- selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
14710
- });
14711
- this._core.rerender();
14712
- }
14713
- }
14714
- if (event.pointerType === 'touch') {
14715
- if (this._core.store.state.pointers.size === 1 && this._core.store.state.isErasing) {
14716
- const shadowRoot = this._core.store.state.host?.shadowRoot;
14717
- if (!shadowRoot)
14718
- return;
14719
- const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
14720
- if (selectedObjects.length === 0)
14721
- return;
14722
- const x = this._core.store.state.pointerX;
14723
- const y = this._core.store.state.pointerY;
14724
- selectedObjects.forEach(selectedObject => {
14725
- selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
14726
- });
14727
- this._core.rerender();
14728
- }
14729
- }
15307
+ get length() {
15308
+ return this.objectIds.length;
14730
15309
  }
14731
- handlePointerUp(event) {
14732
- if (event.pointerType === 'mouse') {
14733
- if (this._core.store.state.isErasing) {
14734
- const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
14735
- objectsToRemove.forEach(object => {
14736
- object.markedForRemoval = false;
14737
- this._core.removeObject(object);
14738
- });
14739
- if (objectsToRemove.length > 0) {
14740
- this._core.rerender();
14741
- }
14742
- this._core.store.state.isErasing = false;
14743
- this._core.engine.emitObjectsChange();
14744
- }
15310
+ static create(core) {
15311
+ const object = new KritzelSelectionGroup();
15312
+ object._core = core;
15313
+ object.id = object.generateId();
15314
+ object.workspaceId = core.store.state.activeWorkspace.id;
15315
+ object.scale = core.store.state.scale;
15316
+ object.zIndex = 99999;
15317
+ return object;
15318
+ }
15319
+ addOrRemove(object) {
15320
+ const index = this.objectIds.findIndex(id => id === object.id);
15321
+ if (index === -1) {
15322
+ this.objectIds.push(object.id);
14745
15323
  }
14746
- if (event.pointerType === 'touch') {
14747
- clearTimeout(this.touchStartTimeout);
14748
- if (this._core.store.state.isErasing) {
14749
- const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
14750
- objectsToRemove.forEach(object => {
14751
- object.markedForRemoval = false;
14752
- this._core.removeObject(object);
14753
- });
14754
- if (objectsToRemove.length > 0) {
14755
- this._core.rerender();
14756
- }
14757
- this._core.store.state.isErasing = false;
14758
- this._core.engine.emitObjectsChange();
14759
- }
15324
+ else {
15325
+ this.objectIds.splice(index, 1);
14760
15326
  }
15327
+ this.captureUnchangedSnapshots();
15328
+ this.refreshObjectDimensions();
14761
15329
  }
14762
- }
14763
-
14764
- /**
14765
- * Browser Image Compression
14766
- * v2.0.2
14767
- * by Donald <donaldcwl@gmail.com>
14768
- * https://github.com/Donaldcwl/browser-image-compression
14769
- */
14770
-
14771
- function _mergeNamespaces$1(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:true,get:function(){return t[r]}});}}));})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=true;break;case 19789:f=false;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s;}return t(new Blob)})),i.readAsArrayBuffer(e);}));var e={},t={get exports(){return e},set exports(t){e=t;}};!function(e){var r,i,UZIP={};t.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t);}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else {var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else {if(8!=l)throw "unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A;}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=false);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l};}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0);}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++]);}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return -1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521;}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255;},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[t])},writeUint:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255,e[t+2]=r>>16&255,e[t+3]=r>>24&255;},readASCII:function(e,t,r){for(var i="",o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII:function(e,t,r){for(var i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},pad:function(e){return e.length<2?"0"+e:e},readUTF8:function(e,t,r){for(var i,o="",a=0;a<r;a++)o+="%"+UZIP.bin.pad(e[t+a].toString(16));try{i=decodeURIComponent(o);}catch(i){return UZIP.bin.readASCII(e,t,r)}return i},writeUTF8:function(e,t,r){for(var i=r.length,o=0,a=0;a<i;a++){var s=r.charCodeAt(a);if(0==(4294967168&s))e[t+o]=s,o++;else if(0==(4294965248&s))e[t+o]=192|s>>6,e[t+o+1]=128|s>>0&63,o+=2;else if(0==(4294901760&s))e[t+o]=224|s>>12,e[t+o+1]=128|s>>6&63,e[t+o+2]=128|s>>0&63,o+=3;else {if(0!=(4292870144&s))throw "e";e[t+o]=240|s>>18,e[t+o+1]=128|s>>12&63,e[t+o+2]=128|s>>6&63,e[t+o+3]=128|s>>0&63,o+=4;}}return o},sizeUTF8:function(e){for(var t=e.length,r=0,i=0;i<t;i++){var o=e.charCodeAt(i);if(0==(4294967168&o))r++;else if(0==(4294965248&o))r+=2;else if(0==(4294901760&o))r+=3;else {if(0!=(4292870144&o))throw "e";r+=4;}}return r}},UZIP.F={},UZIP.F.deflateRaw=function(e,t,r,i){var o=[[0,0,0,0,0],[4,4,8,4,0],[4,5,16,8,0],[4,6,16,16,0],[4,10,16,32,0],[8,16,32,32,0],[8,16,128,128,0],[8,32,128,256,0],[32,128,258,1024,1],[32,258,258,4096,1]][i],a=UZIP.F.U,s=UZIP.F._goodIndex;var f=UZIP.F._putsE,l=0,c=r<<3,u=0,h=e.length;if(0==i){for(;l<h;){f(t,c,l+(_=Math.min(65535,h-l))==h?1:0),c=UZIP.F._copyExact(e,l,_,t,c+8),l+=_;}return c>>>3}var d=a.lits,A=a.strt,g=a.prev,p=0,m=0,w=0,v=0,b=0,y=0;for(h>2&&(A[y=UZIP.F._hash(e,0)]=0),l=0;l<h;l++){if(b=y,l+1<h-2){y=UZIP.F._hash(e,l+1);var E=l+1&32767;g[E]=A[y],A[y]=E;}if(u<=l){(p>14e3||m>26697)&&h-l>100&&(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(l==h-1||u==h?1:0,d,p,v,e,w,l-w,t,c),p=m=v=0,w=l);var F=0;l<h-2&&(F=UZIP.F._bestMatch(e,l,g,b,Math.min(o[2],h-l),o[3]));var _=F>>>16,B=65535&F;if(0!=F){B=65535&F;var U=s(_=F>>>16,a.of0);a.lhst[257+U]++;var C=s(B,a.df0);a.dhst[C]++,v+=a.exb[U]+a.dxb[C],d[p]=_<<23|l-u,d[p+1]=B<<16|U<<8|C,p+=2,u=l+_;}else a.lhst[e[l]]++;m++;}}for(w==l&&0!=e.length||(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(1,d,p,v,e,w,l-w,t,c),p=0,m=0,p=m=v=0,w=l);0!=(7&c);)c++;return c>>>3},UZIP.F._bestMatch=function(e,t,r,i,o,a){var s=32767&t,f=r[s],l=s-f+32768&32767;if(f==s||i!=UZIP.F._hash(e,t-l))return 0;for(var c=0,u=0,h=Math.min(32767,t);l<=h&&0!=--a&&f!=s;){if(0==c||e[t+c]==e[t+c-l]){var d=UZIP.F._howLong(e,t,l);if(d>c){if(u=l,(c=d)>=o)break;l+2<d&&(d=l+2);for(var A=0,g=0;g<d-2;g++){var p=t-l+g+32768&32767,m=p-r[p]+32768&32767;m>A&&(A=m,f=p);}}}l+=(s=f)-(f=r[s])+32768&32767;}return c<<16|u},UZIP.F._howLong=function(e,t,r){if(e[t]!=e[t-r]||e[t+1]!=e[t+1-r]||e[t+2]!=e[t+2-r])return 0;var i=t,o=Math.min(e.length,t+258);for(t+=3;t<o&&e[t]==e[t-r];)t++;return t-i},UZIP.F._hash=function(e,t){return (e[t]<<8|e[t+1])+(e[t+2]<<4)&65535},UZIP.saved=0,UZIP.F._writeBlock=function(e,t,r,i,o,a,s,f,l){var c,u,h,d,A,g,p,m,w,v=UZIP.F.U,b=UZIP.F._putsF,y=UZIP.F._putsE;v.lhst[256]++,u=(c=UZIP.F.getTrees())[0],h=c[1],d=c[2],A=c[3],g=c[4],p=c[5],m=c[6],w=c[7];var E=32+(0==(l+3&7)?0:8-(l+3&7))+(s<<3),F=i+UZIP.F.contSize(v.fltree,v.lhst)+UZIP.F.contSize(v.fdtree,v.dhst),_=i+UZIP.F.contSize(v.ltree,v.lhst)+UZIP.F.contSize(v.dtree,v.dhst);_+=14+3*p+UZIP.F.contSize(v.itree,v.ihst)+(2*v.ihst[16]+3*v.ihst[17]+7*v.ihst[18]);for(var B=0;B<286;B++)v.lhst[B]=0;for(B=0;B<30;B++)v.dhst[B]=0;for(B=0;B<19;B++)v.ihst[B]=0;var U=E<F&&E<_?0:F<_?1:2;if(b(f,l,e),b(f,l+1,U),l+=3,0==U){for(;0!=(7&l);)l++;l=UZIP.F._copyExact(o,a,s,f,l);}else {var C,I;if(1==U&&(C=v.fltree,I=v.fdtree),2==U){UZIP.F.makeCodes(v.ltree,u),UZIP.F.revCodes(v.ltree,u),UZIP.F.makeCodes(v.dtree,h),UZIP.F.revCodes(v.dtree,h),UZIP.F.makeCodes(v.itree,d),UZIP.F.revCodes(v.itree,d),C=v.ltree,I=v.dtree,y(f,l,A-257),y(f,l+=5,g-1),y(f,l+=5,p-4),l+=4;for(var Q=0;Q<p;Q++)y(f,l+3*Q,v.itree[1+(v.ordr[Q]<<1)]);l+=3*p,l=UZIP.F._codeTiny(m,v.itree,f,l),l=UZIP.F._codeTiny(w,v.itree,f,l);}for(var M=a,x=0;x<r;x+=2){for(var S=t[x],R=S>>>23,T=M+(8388607&S);M<T;)l=UZIP.F._writeLit(o[M++],C,f,l);if(0!=R){var O=t[x+1],P=O>>16,H=O>>8&255,L=255&O;y(f,l=UZIP.F._writeLit(257+H,C,f,l),R-v.of0[H]),l+=v.exb[H],b(f,l=UZIP.F._writeLit(L,I,f,l),P-v.df0[L]),l+=v.dxb[L],M+=R;}}l=UZIP.F._writeLit(256,C,f,l);}return l},UZIP.F._copyExact=function(e,t,r,i,o){var a=o>>>3;return i[a]=r,i[a+1]=r>>>8,i[a+2]=255-i[a],i[a+3]=255-i[a+1],a+=4,i.set(new Uint8Array(e.buffer,t,r),a),o+(r+4<<3)},UZIP.F.getTrees=function(){for(var e=UZIP.F.U,t=UZIP.F._hufTree(e.lhst,e.ltree,15),r=UZIP.F._hufTree(e.dhst,e.dtree,15),i=[],o=UZIP.F._lenCodes(e.ltree,i),a=[],s=UZIP.F._lenCodes(e.dtree,a),f=0;f<i.length;f+=2)e.ihst[i[f]]++;for(f=0;f<a.length;f+=2)e.ihst[a[f]]++;for(var l=UZIP.F._hufTree(e.ihst,e.itree,7),c=19;c>4&&0==e.itree[1+(e.ordr[c-1]<<1)];)c--;return [t,r,l,o,s,c,i,a]},UZIP.F.getSecond=function(e){for(var t=[],r=0;r<e.length;r+=2)t.push(e[r+1]);return t},UZIP.F.nonZero=function(e){for(var t="",r=0;r<e.length;r+=2)0!=e[r+1]&&(t+=(r>>1)+",");return t},UZIP.F.contSize=function(e,t){for(var r=0,i=0;i<t.length;i++)r+=t[i]*e[1+(i<<1)];return r},UZIP.F._codeTiny=function(e,t,r,i){for(var o=0;o<e.length;o+=2){var a=e[o],s=e[o+1];i=UZIP.F._writeLit(a,t,r,i);var f=16==a?2:17==a?3:7;a>15&&(UZIP.F._putsE(r,i,s,f),i+=f);}return i},UZIP.F._lenCodes=function(e,t){for(var r=e.length;2!=r&&0==e[r-1];)r-=2;for(var i=0;i<r;i+=2){var o=e[i+1],a=i+3<r?e[i+3]:-1,s=i+5<r?e[i+5]:-1,f=0==i?-1:e[i-1];if(0==o&&a==o&&s==o){for(var l=i+5;l+2<r&&e[l+2]==o;)l+=2;(c=Math.min(l+1-i>>>1,138))<11?t.push(17,c-3):t.push(18,c-11),i+=2*c-2;}else if(o==f&&a==o&&s==o){for(l=i+5;l+2<r&&e[l+2]==o;)l+=2;var c=Math.min(l+1-i>>>1,6);t.push(16,c-3),i+=2*c-2;}else t.push(o,0);}return r>>>1},UZIP.F._hufTree=function(e,t,r){var i=[],o=e.length,a=t.length,s=0;for(s=0;s<a;s+=2)t[s]=0,t[s+1]=0;for(s=0;s<o;s++)0!=e[s]&&i.push({lit:s,f:e[s]});var f=i.length,l=i.slice(0);if(0==f)return 0;if(1==f){var c=i[0].lit;l=0==c?1:0;return t[1+(c<<1)]=1,t[1+(l<<1)]=1,1}i.sort((function(e,t){return e.f-t.f}));var u=i[0],h=i[1],d=0,A=1,g=2;for(i[0]={lit:-1,f:u.f+h.f,l:u,r:h,d:0};A!=f-1;)u=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],h=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],i[A++]={lit:-1,f:u.f+h.f,l:u,r:h};var p=UZIP.F.setDepth(i[A-1],0);for(p>r&&(UZIP.F.restrictDepth(l,r,p),p=r),s=0;s<f;s++)t[1+(l[s].lit<<1)]=l[s].d;return p},UZIP.F.setDepth=function(e,t){return -1!=e.lit?(e.d=t,t):Math.max(UZIP.F.setDepth(e.l,t+1),UZIP.F.setDepth(e.r,t+1))},UZIP.F.restrictDepth=function(e,t,r){var i=0,o=1<<r-t,a=0;for(e.sort((function(e,t){return t.d==e.d?e.f-t.f:t.d-e.d})),i=0;i<e.length&&e[i].d>t;i++){var s=e[i].d;e[i].d=t,a+=o-(1<<r-s);}for(a>>>=r-t;a>0;){(s=e[i].d)<t?(e[i].d++,a-=1<<t-s-1):i++;}for(;i>=0;i--)e[i].d==t&&a<0&&(e[i].d--,a++);0!=a&&console.log("debt left");},UZIP.F._goodIndex=function(e,t){var r=0;return t[16|r]<=e&&(r|=16),t[8|r]<=e&&(r|=8),t[4|r]<=e&&(r|=4),t[2|r]<=e&&(r|=2),t[1|r]<=e&&(r|=1),r},UZIP.F._writeLit=function(e,t,r,i){return UZIP.F._putsF(r,i,t[e<<1]),i+t[1+(e<<1)]},UZIP.F.inflate=function(e,t){var r=Uint8Array;if(3==e[0]&&0==e[1])return t||new r(0);var i=UZIP.F,o=i._bitsF,a=i._bitsE,s=i._decodeTiny,f=i.makeCodes,l=i.codes2map,c=i._get17,u=i.U,h=null==t;h&&(t=new r(e.length>>>2<<3));for(var d,A,g=0,p=0,m=0,w=0,v=0,b=0,y=0,E=0,F=0;0==g;)if(g=o(e,F,1),p=o(e,F+1,2),F+=3,0!=p){if(h&&(t=UZIP.F._check(t,E+(1<<17))),1==p&&(d=u.flmap,A=u.fdmap,b=511,y=31),2==p){m=a(e,F,5)+257,w=a(e,F+5,5)+1,v=a(e,F+10,4)+4,F+=14;for(var _=0;_<38;_+=2)u.itree[_]=0,u.itree[_+1]=0;var B=1;for(_=0;_<v;_++){var U=a(e,F+3*_,3);u.itree[1+(u.ordr[_]<<1)]=U,U>B&&(B=U);}F+=3*v,f(u.itree,B),l(u.itree,B,u.imap),d=u.lmap,A=u.dmap,F=s(u.imap,(1<<B)-1,m+w,e,F,u.ttree);var C=i._copyOut(u.ttree,0,m,u.ltree);b=(1<<C)-1;var I=i._copyOut(u.ttree,m,w,u.dtree);y=(1<<I)-1,f(u.ltree,C),l(u.ltree,C,d),f(u.dtree,I),l(u.dtree,I,A);}for(;;){var Q=d[c(e,F)&b];F+=15&Q;var M=Q>>>4;if(M>>>8==0)t[E++]=M;else {if(256==M)break;var x=E+M-254;if(M>264){var S=u.ldef[M-257];x=E+(S>>>3)+a(e,F,7&S),F+=7&S;}var R=A[c(e,F)&y];F+=15&R;var T=R>>>4,O=u.ddef[T],P=(O>>>4)+o(e,F,15&O);for(F+=15&O,h&&(t=UZIP.F._check(t,E+(1<<17)));E<x;)t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P];E=x;}}}else {0!=(7&F)&&(F+=8-(7&F));var H=4+(F>>>3),L=e[H-4]|e[H-3]<<8;h&&(t=UZIP.F._check(t,E+L)),t.set(new r(e.buffer,e.byteOffset+H,L),E),F=H+L<<3,E+=L;}return t.length==E?t:t.slice(0,E)},UZIP.F._check=function(e,t){var r=e.length;if(t<=r)return e;var i=new Uint8Array(Math.max(r<<1,t));return i.set(e,0),i},UZIP.F._decodeTiny=function(e,t,r,i,o,a){for(var s=UZIP.F._bitsE,f=UZIP.F._get17,l=0;l<r;){var c=e[f(i,o)&t];o+=15&c;var u=c>>>4;if(u<=15)a[l]=u,l++;else {var h=0,d=0;16==u?(d=3+s(i,o,2),o+=2,h=a[l-1]):17==u?(d=3+s(i,o,3),o+=3):18==u&&(d=11+s(i,o,7),o+=7);for(var A=l+d;l<A;)a[l]=h,l++;}}return o},UZIP.F._copyOut=function(e,t,r,i){for(var o=0,a=0,s=i.length>>>1;a<r;){var f=e[a+t];i[a<<1]=0,i[1+(a<<1)]=f,f>o&&(o=f),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},UZIP.F.makeCodes=function(e,t){for(var r,i,o,a,s=UZIP.F.U,f=e.length,l=s.bl_count,c=0;c<=t;c++)l[c]=0;for(c=1;c<f;c+=2)l[e[c]]++;var u=s.next_code;for(r=0,l[0]=0,i=1;i<=t;i++)r=r+l[i-1]<<1,u[i]=r;for(o=0;o<f;o+=2)0!=(a=e[o+1])&&(e[o]=u[a],u[a]++);},UZIP.F.codes2map=function(e,t,r){for(var i=e.length,o=UZIP.F.U.rev15,a=0;a<i;a+=2)if(0!=e[a+1])for(var s=a>>1,f=e[a+1],l=s<<4|f,c=t-f,u=e[a]<<c,h=u+(1<<c);u!=h;){r[o[u]>>>15-t]=l,u++;}},UZIP.F.revCodes=function(e,t){for(var r=UZIP.F.U.rev15,i=15-t,o=0;o<e.length;o+=2){var a=e[o]<<t-e[o+1];e[o]=r[a]>>>i;}},UZIP.F._putsE=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},UZIP.F._putsF=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},UZIP.F._bitsE=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},UZIP.F._bitsF=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},UZIP.F._get17=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},UZIP.F._get25=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},UZIP.F.U=(r=Uint16Array,i=Uint32Array,{next_code:new r(16),bl_count:new r(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new r(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new i(32),flmap:new r(512),fltree:[],fdmap:new r(32),fdtree:[],lmap:new r(32768),ltree:[],ttree:[],dmap:new r(32768),dtree:[],imap:new r(512),itree:[],rev15:new r(32768),lhst:new i(286),dhst:new i(30),ihst:new i(19),lits:new i(15e3),strt:new r(65536),prev:new r(32768)}),function(){for(var e=UZIP.F.U,t=0;t<32768;t++){var r=t;r=(4278255360&(r=(4042322160&(r=(3435973836&(r=(2863311530&r)>>>1|(1431655765&r)<<1))>>>2|(858993459&r)<<2))>>>4|(252645135&r)<<4))>>>8|(16711935&r)<<8,e.rev15[t]=(r>>>16|r<<16)>>>17;}function pushV(e,t,r){for(;0!=t--;)e.push(0,r);}for(t=0;t<32;t++)e.ldef[t]=e.of0[t]<<3|e.exb[t],e.ddef[t]=e.df0[t]<<4|e.dxb[t];pushV(e.fltree,144,8),pushV(e.fltree,112,9),pushV(e.fltree,24,7),pushV(e.fltree,8,8),UZIP.F.makeCodes(e.fltree,9),UZIP.F.codes2map(e.fltree,9,e.flmap),UZIP.F.revCodes(e.fltree,9),pushV(e.fdtree,32,5),UZIP.F.makeCodes(e.fdtree,5),UZIP.F.codes2map(e.fdtree,5,e.fdmap),UZIP.F.revCodes(e.fdtree,5),pushV(e.itree,19,0),pushV(e.ltree,286,0),pushV(e.dtree,30,0),pushV(e.ttree,320,0);}();}();var UZIP=_mergeNamespaces$1({__proto__:null,default:e},[e]);const UPNG=function(){var e={nextZero(e,t){for(;0!=e[t];)t++;return t},readUshort:(e,t)=>e[t]<<8|e[t+1],writeUshort(e,t,r){e[t]=r>>8&255,e[t+1]=255&r;},readUint:(e,t)=>16777216*e[t]+(e[t+1]<<16|e[t+2]<<8|e[t+3]),writeUint(e,t,r){e[t]=r>>24&255,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r;},readASCII(e,t,r){let i="";for(let o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII(e,t,r){for(let i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},readBytes(e,t,r){const i=[];for(let o=0;o<r;o++)i.push(e[t+o]);return i},pad:e=>e.length<2?`0${e}`:e,readUTF8(t,r,i){let o,a="";for(let o=0;o<i;o++)a+=`%${e.pad(t[r+o].toString(16))}`;try{o=decodeURIComponent(a);}catch(o){return e.readASCII(t,r,i)}return o}};function decodeImage(t,r,i,o){const a=r*i,s=_getBPP(o),f=Math.ceil(r*s/8),l=new Uint8Array(4*a),c=new Uint32Array(l.buffer),{ctype:u}=o,{depth:h}=o,d=e.readUshort;if(6==u){const e=a<<2;if(8==h)for(var A=0;A<e;A+=4)l[A]=t[A],l[A+1]=t[A+1],l[A+2]=t[A+2],l[A+3]=t[A+3];if(16==h)for(A=0;A<e;A++)l[A]=t[A<<1];}else if(2==u){const e=o.tabs.tRNS;if(null==e){if(8==h)for(A=0;A<a;A++){var g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g];}if(16==h)for(A=0;A<a;A++){g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g];}}else {var p=e[0];const r=e[1],i=e[2];if(8==h)for(A=0;A<a;A++){var m=A<<2;g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g],t[g]==p&&t[g+1]==r&&t[g+2]==i&&(l[m+3]=0);}if(16==h)for(A=0;A<a;A++){m=A<<2,g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g],d(t,g)==p&&d(t,g+2)==r&&d(t,g+4)==i&&(l[m+3]=0);}}}else if(3==u){const e=o.tabs.PLTE,s=o.tabs.tRNS,c=s?s.length:0;if(1==h)for(var w=0;w<i;w++){var v=w*f,b=w*r;for(A=0;A<r;A++){m=b+A<<2;var y=3*(E=t[v+(A>>3)]>>7-((7&A)<<0)&1);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}if(2==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>2)]>>6-((3&A)<<1)&3);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(4==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>1)]>>4-((1&A)<<2)&15);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(8==h)for(A=0;A<a;A++){var E;m=A<<2,y=3*(E=t[A]);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}else if(4==u){if(8==h)for(A=0;A<a;A++){m=A<<2;var F=t[_=A<<1];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+1];}if(16==h)for(A=0;A<a;A++){var _;m=A<<2,F=t[_=A<<2];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+2];}}else if(0==u)for(p=o.tabs.tRNS?o.tabs.tRNS:-1,w=0;w<i;w++){const e=w*f,i=w*r;if(1==h)for(var B=0;B<r;B++){var U=(F=255*(t[e+(B>>>3)]>>>7-(7&B)&1))==255*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(2==h)for(B=0;B<r;B++){U=(F=85*(t[e+(B>>>2)]>>>6-((3&B)<<1)&3))==85*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(4==h)for(B=0;B<r;B++){U=(F=17*(t[e+(B>>>1)]>>>4-((1&B)<<2)&15))==17*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(8==h)for(B=0;B<r;B++){U=(F=t[e+B])==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(16==h)for(B=0;B<r;B++){F=t[e+(B<<1)],U=d(t,e+(B<<1))==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}}return l}function _decompress(e,r,i,o){const a=_getBPP(e),s=Math.ceil(i*a/8),f=new Uint8Array((s+1+e.interlace)*o);return r=e.tabs.CgBI?t(r,f):_inflate(r,f),0==e.interlace?r=_filterZero(r,e,0,i,o):1==e.interlace&&(r=function _readInterlace(e,t){const r=t.width,i=t.height,o=_getBPP(t),a=o>>3,s=Math.ceil(r*o/8),f=new Uint8Array(i*s);let l=0;const c=[0,0,4,0,2,0,1],u=[0,4,0,2,0,1,0],h=[8,8,8,4,4,2,2],d=[8,8,4,4,2,2,1];let A=0;for(;A<7;){const p=h[A],m=d[A];let w=0,v=0,b=c[A];for(;b<i;)b+=p,v++;let y=u[A];for(;y<r;)y+=m,w++;const E=Math.ceil(w*o/8);_filterZero(e,t,l,w,v);let F=0,_=c[A];for(;_<i;){let t=u[A],i=l+F*E<<3;for(;t<r;){var g;if(1==o)g=(g=e[i>>3])>>7-(7&i)&1,f[_*s+(t>>3)]|=g<<7-((7&t)<<0);if(2==o)g=(g=e[i>>3])>>6-(7&i)&3,f[_*s+(t>>2)]|=g<<6-((3&t)<<1);if(4==o)g=(g=e[i>>3])>>4-(7&i)&15,f[_*s+(t>>1)]|=g<<4-((1&t)<<2);if(o>=8){const r=_*s+t*a;for(let t=0;t<a;t++)f[r+t]=e[(i>>3)+t];}i+=o,t+=m;}F++,_+=p;}w*v!=0&&(l+=v*(1+E)),A+=1;}return f}(r,e)),r}function _inflate(e,r){return t(new Uint8Array(e.buffer,2,e.length-6),r)}var t=function(){const e={H:{}};return e.H.N=function(t,r){const i=Uint8Array;let o,a,s=0,f=0,l=0,c=0,u=0,h=0,d=0,A=0,g=0;if(3==t[0]&&0==t[1])return r||new i(0);const p=e.H,m=p.b,w=p.e,v=p.R,b=p.n,y=p.A,E=p.Z,F=p.m,_=null==r;for(_&&(r=new i(t.length>>>2<<5));0==s;)if(s=m(t,g,1),f=m(t,g+1,2),g+=3,0!=f){if(_&&(r=e.H.W(r,A+(1<<17))),1==f&&(o=F.J,a=F.h,h=511,d=31),2==f){l=w(t,g,5)+257,c=w(t,g+5,5)+1,u=w(t,g+10,4)+4,g+=14;let e=1;for(var B=0;B<38;B+=2)F.Q[B]=0,F.Q[B+1]=0;for(B=0;B<u;B++){const r=w(t,g+3*B,3);F.Q[1+(F.X[B]<<1)]=r,r>e&&(e=r);}g+=3*u,b(F.Q,e),y(F.Q,e,F.u),o=F.w,a=F.d,g=v(F.u,(1<<e)-1,l+c,t,g,F.v);const r=p.V(F.v,0,l,F.C);h=(1<<r)-1;const i=p.V(F.v,l,c,F.D);d=(1<<i)-1,b(F.C,r),y(F.C,r,o),b(F.D,i),y(F.D,i,a);}for(;;){const e=o[E(t,g)&h];g+=15&e;const i=e>>>4;if(i>>>8==0)r[A++]=i;else {if(256==i)break;{let e=A+i-254;if(i>264){const r=F.q[i-257];e=A+(r>>>3)+w(t,g,7&r),g+=7&r;}const o=a[E(t,g)&d];g+=15&o;const s=o>>>4,f=F.c[s],l=(f>>>4)+m(t,g,15&f);for(g+=15&f;A<e;)r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l];A=e;}}}}else {0!=(7&g)&&(g+=8-(7&g));const o=4+(g>>>3),a=t[o-4]|t[o-3]<<8;_&&(r=e.H.W(r,A+a)),r.set(new i(t.buffer,t.byteOffset+o,a),A),g=o+a<<3,A+=a;}return r.length==A?r:r.slice(0,A)},e.H.W=function(e,t){const r=e.length;if(t<=r)return e;const i=new Uint8Array(r<<1);return i.set(e,0),i},e.H.R=function(t,r,i,o,a,s){const f=e.H.e,l=e.H.Z;let c=0;for(;c<i;){const e=t[l(o,a)&r];a+=15&e;const i=e>>>4;if(i<=15)s[c]=i,c++;else {let e=0,t=0;16==i?(t=3+f(o,a,2),a+=2,e=s[c-1]):17==i?(t=3+f(o,a,3),a+=3):18==i&&(t=11+f(o,a,7),a+=7);const r=c+t;for(;c<r;)s[c]=e,c++;}}return a},e.H.V=function(e,t,r,i){let o=0,a=0;const s=i.length>>>1;for(;a<r;){const r=e[a+t];i[a<<1]=0,i[1+(a<<1)]=r,r>o&&(o=r),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},e.H.n=function(t,r){const i=e.H.m,o=t.length;let a,s,f;let l;const c=i.j;for(var u=0;u<=r;u++)c[u]=0;for(u=1;u<o;u+=2)c[t[u]]++;const h=i.K;for(a=0,c[0]=0,s=1;s<=r;s++)a=a+c[s-1]<<1,h[s]=a;for(f=0;f<o;f+=2)l=t[f+1],0!=l&&(t[f]=h[l],h[l]++);},e.H.A=function(t,r,i){const o=t.length,a=e.H.m.r;for(let e=0;e<o;e+=2)if(0!=t[e+1]){const o=e>>1,s=t[e+1],f=o<<4|s,l=r-s;let c=t[e]<<l;const u=c+(1<<l);for(;c!=u;){i[a[c]>>>15-r]=f,c++;}}},e.H.l=function(t,r){const i=e.H.m.r,o=15-r;for(let e=0;e<t.length;e+=2){const a=t[e]<<r-t[e+1];t[e]=i[a]>>>o;}},e.H.M=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},e.H.I=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},e.H.e=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},e.H.b=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},e.H.Z=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},e.H.i=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},e.H.m=function(){const e=Uint16Array,t=Uint32Array;return {K:new e(16),j:new e(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new e(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new t(32),J:new e(512),_:[],h:new e(32),$:[],w:new e(32768),C:[],v:[],d:new e(32768),D:[],u:new e(512),Q:[],r:new e(32768),s:new t(286),Y:new t(30),a:new t(19),t:new t(15e3),k:new e(65536),g:new e(32768)}}(),function(){const t=e.H.m;for(var r=0;r<32768;r++){let e=r;e=(2863311530&e)>>>1|(1431655765&e)<<1,e=(3435973836&e)>>>2|(858993459&e)<<2,e=(4042322160&e)>>>4|(252645135&e)<<4,e=(4278255360&e)>>>8|(16711935&e)<<8,t.r[r]=(e>>>16|e<<16)>>>17;}function n(e,t,r){for(;0!=t--;)e.push(0,r);}for(r=0;r<32;r++)t.q[r]=t.S[r]<<3|t.T[r],t.c[r]=t.p[r]<<4|t.z[r];n(t._,144,8),n(t._,112,9),n(t._,24,7),n(t._,8,8),e.H.n(t._,9),e.H.A(t._,9,t.J),e.H.l(t._,9),n(t.$,32,5),e.H.n(t.$,5),e.H.A(t.$,5,t.h),e.H.l(t.$,5),n(t.Q,19,0),n(t.C,286,0),n(t.D,30,0),n(t.v,320,0);}(),e.H.N}();function _getBPP(e){return [1,null,3,1,2,null,4][e.ctype]*e.depth}function _filterZero(e,t,r,i,o){let a=_getBPP(t);const s=Math.ceil(i*a/8);let f,l;a=Math.ceil(a/8);let c=e[r],u=0;if(c>1&&(e[r]=[0,0,1][c-2]),3==c)for(u=a;u<s;u++)e[u+1]=e[u+1]+(e[u+1-a]>>>1)&255;for(let t=0;t<o;t++)if(f=r+t*s,l=f+t+1,c=e[l-1],u=0,0==c)for(;u<s;u++)e[f+u]=e[l+u];else if(1==c){for(;u<a;u++)e[f+u]=e[l+u];for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-a];}else if(2==c)for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-s];else if(3==c){for(;u<a;u++)e[f+u]=e[l+u]+(e[f+u-s]>>>1);for(;u<s;u++)e[f+u]=e[l+u]+(e[f+u-s]+e[f+u-a]>>>1);}else {for(;u<a;u++)e[f+u]=e[l+u]+_paeth(0,e[f+u-s],0);for(;u<s;u++)e[f+u]=e[l+u]+_paeth(e[f+u-a],e[f+u-s],e[f+u-a-s]);}return e}function _paeth(e,t,r){const i=e+t-r,o=i-e,a=i-t,s=i-r;return o*o<=a*a&&o*o<=s*s?e:a*a<=s*s?t:r}function _IHDR(t,r,i){i.width=e.readUint(t,r),r+=4,i.height=e.readUint(t,r),r+=4,i.depth=t[r],r++,i.ctype=t[r],r++,i.compress=t[r],r++,i.filter=t[r],r++,i.interlace=t[r],r++;}function _copyTile(e,t,r,i,o,a,s,f,l){const c=Math.min(t,o),u=Math.min(r,a);let h=0,d=0;for(let r=0;r<u;r++)for(let a=0;a<c;a++)if(s>=0&&f>=0?(h=r*t+a<<2,d=(f+r)*o+s+a<<2):(h=(-f+r)*t-s+a<<2,d=r*o+a<<2),0==l)i[d]=e[h],i[d+1]=e[h+1],i[d+2]=e[h+2],i[d+3]=e[h+3];else if(1==l){var A=e[h+3]*(1/255),g=e[h]*A,p=e[h+1]*A,m=e[h+2]*A,w=i[d+3]*(1/255),v=i[d]*w,b=i[d+1]*w,y=i[d+2]*w;const t=1-A,r=A+w*t,o=0==r?0:1/r;i[d+3]=255*r,i[d+0]=(g+v*t)*o,i[d+1]=(p+b*t)*o,i[d+2]=(m+y*t)*o;}else if(2==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];A==w&&g==v&&p==b&&m==y?(i[d]=0,i[d+1]=0,i[d+2]=0,i[d+3]=0):(i[d]=g,i[d+1]=p,i[d+2]=m,i[d+3]=A);}else if(3==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];if(A==w&&g==v&&p==b&&m==y)continue;if(A<220&&w>20)return false}return true}return {decode:function decode(r){const i=new Uint8Array(r);let o=8;const a=e,s=a.readUshort,f=a.readUint,l={tabs:{},frames:[]},c=new Uint8Array(i.length);let u,h=0,d=0;const A=[137,80,78,71,13,10,26,10];for(var g=0;g<8;g++)if(i[g]!=A[g])throw "The input is not a PNG file!";for(;o<i.length;){const e=a.readUint(i,o);o+=4;const r=a.readASCII(i,o,4);if(o+=4,"IHDR"==r)_IHDR(i,o,l);else if("iCCP"==r){for(var p=o;0!=i[p];)p++;a.readASCII(i,o,p-o);const s=i.slice(p+2,o+e);let f=null;try{f=_inflate(s);}catch(e){f=t(s);}l.tabs[r]=f;}else if("CgBI"==r)l.tabs[r]=i.slice(o,o+4);else if("IDAT"==r){for(g=0;g<e;g++)c[h+g]=i[o+g];h+=e;}else if("acTL"==r)l.tabs[r]={num_frames:f(i,o),num_plays:f(i,o+4)},u=new Uint8Array(i.length);else if("fcTL"==r){if(0!=d)(E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height),d=0;const e={x:f(i,o+12),y:f(i,o+16),width:f(i,o+4),height:f(i,o+8)};let t=s(i,o+22);t=s(i,o+20)/(0==t?100:t);const r={rect:e,delay:Math.round(1e3*t),dispose:i[o+24],blend:i[o+25]};l.frames.push(r);}else if("fdAT"==r){for(g=0;g<e-4;g++)u[d+g]=i[o+g+4];d+=e-4;}else if("pHYs"==r)l.tabs[r]=[a.readUint(i,o),a.readUint(i,o+4),i[o+8]];else if("cHRM"==r){l.tabs[r]=[];for(g=0;g<8;g++)l.tabs[r].push(a.readUint(i,o+4*g));}else if("tEXt"==r||"zTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});var m=a.nextZero(i,o),w=a.readASCII(i,o,m-o),v=o+e-m-1;if("tEXt"==r)y=a.readASCII(i,m+1,v);else {var b=_inflate(i.slice(m+2,m+2+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("iTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});m=0,p=o;m=a.nextZero(i,p);w=a.readASCII(i,p,m-p);const t=i[p=m+1];var y;p+=2,m=a.nextZero(i,p),a.readASCII(i,p,m-p),p=m+1,m=a.nextZero(i,p),a.readUTF8(i,p,m-p);v=e-((p=m+1)-o);if(0==t)y=a.readUTF8(i,p,v);else {b=_inflate(i.slice(p,p+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("PLTE"==r)l.tabs[r]=a.readBytes(i,o,e);else if("hIST"==r){const e=l.tabs.PLTE.length/3;l.tabs[r]=[];for(g=0;g<e;g++)l.tabs[r].push(s(i,o+2*g));}else if("tRNS"==r)3==l.ctype?l.tabs[r]=a.readBytes(i,o,e):0==l.ctype?l.tabs[r]=s(i,o):2==l.ctype&&(l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]);else if("gAMA"==r)l.tabs[r]=a.readUint(i,o)/1e5;else if("sRGB"==r)l.tabs[r]=i[o];else if("bKGD"==r)0==l.ctype||4==l.ctype?l.tabs[r]=[s(i,o)]:2==l.ctype||6==l.ctype?l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]:3==l.ctype&&(l.tabs[r]=i[o]);else if("IEND"==r)break;o+=e,a.readUint(i,o),o+=4;}var E;return 0!=d&&((E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height)),l.data=_decompress(l,c,l.width,l.height),delete l.compress,delete l.interlace,delete l.filter,l},toRGBA8:function toRGBA8(e){const t=e.width,r=e.height;if(null==e.tabs.acTL)return [decodeImage(e.data,t,r,e).buffer];const i=[];null==e.frames[0].data&&(e.frames[0].data=e.data);const o=t*r*4,a=new Uint8Array(o),s=new Uint8Array(o),f=new Uint8Array(o);for(let c=0;c<e.frames.length;c++){const u=e.frames[c],h=u.rect.x,d=u.rect.y,A=u.rect.width,g=u.rect.height,p=decodeImage(u.data,A,g,e);if(0!=c)for(var l=0;l<o;l++)f[l]=a[l];if(0==u.blend?_copyTile(p,A,g,a,t,r,h,d,0):1==u.blend&&_copyTile(p,A,g,a,t,r,h,d,1),i.push(a.buffer.slice(0)),0==u.dispose);else if(1==u.dispose)_copyTile(s,A,g,a,t,r,h,d,0);else if(2==u.dispose)for(l=0;l<o;l++)a[l]=f[l];}return i},_paeth:_paeth,_copyTile:_copyTile,_bin:e}}();!function(){const{_copyTile:e}=UPNG,{_bin:t}=UPNG,r=UPNG._paeth;var i={table:function(){const e=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let e=0;e<8;e++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update(e,t,r,o){for(let a=0;a<o;a++)e=i.table[255&(e^t[r+a])]^e>>>8;return e},crc:(e,t,r)=>4294967295^i.update(4294967295,e,t,r)};function addErr(e,t,r,i){t[r]+=e[0]*i>>4,t[r+1]+=e[1]*i>>4,t[r+2]+=e[2]*i>>4,t[r+3]+=e[3]*i>>4;}function N(e){return Math.max(0,Math.min(255,e))}function D(e,t){const r=e[0]-t[0],i=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return r*r+i*i+o*o+a*a}function dither(e,t,r,i,o,a,s){null==s&&(s=1);const f=i.length,l=[];for(var c=0;c<f;c++){const e=i[c];l.push([e>>>0&255,e>>>8&255,e>>>16&255,e>>>24&255]);}for(c=0;c<f;c++){let e=4294967295;for(var u=0,h=0;h<f;h++){var d=D(l[c],l[h]);h!=c&&d<e&&(e=d,u=h);}}const A=new Uint32Array(o.buffer),g=new Int16Array(t*r*4),p=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];for(c=0;c<p.length;c++)p[c]=255*((p[c]+.5)/16-.5);for(let o=0;o<r;o++)for(let w=0;w<t;w++){var m;c=4*(o*t+w);if(2!=s)m=[N(e[c]+g[c]),N(e[c+1]+g[c+1]),N(e[c+2]+g[c+2]),N(e[c+3]+g[c+3])];else {d=p[4*(3&o)+(3&w)];m=[N(e[c]+d),N(e[c+1]+d),N(e[c+2]+d),N(e[c+3]+d)];}u=0;let v=16777215;for(h=0;h<f;h++){const e=D(m,l[h]);e<v&&(v=e,u=h);}const b=l[u],y=[m[0]-b[0],m[1]-b[1],m[2]-b[2],m[3]-b[3]];1==s&&(w!=t-1&&addErr(y,g,c+4,7),o!=r-1&&(0!=w&&addErr(y,g,c+4*t-4,3),addErr(y,g,c+4*t,5),w!=t-1&&addErr(y,g,c+4*t+4,1))),a[c>>2]=u,A[c>>2]=i[u];}}function _main(e,r,o,a,s){null==s&&(s={});const{crc:f}=i,l=t.writeUint,c=t.writeUshort,u=t.writeASCII;let h=8;const d=e.frames.length>1;let A,g=false,p=33+(d?20:0);if(null!=s.sRGB&&(p+=13),null!=s.pHYs&&(p+=21),null!=s.iCCP&&(A=pako.deflate(s.iCCP),p+=21+A.length+4),3==e.ctype){for(var m=e.plte.length,w=0;w<m;w++)e.plte[w]>>>24!=255&&(g=true);p+=8+3*m+4+(g?8+1*m+4:0);}for(var v=0;v<e.frames.length;v++){d&&(p+=38),p+=(F=e.frames[v]).cimg.length+12,0!=v&&(p+=4);}p+=12;const b=new Uint8Array(p),y=[137,80,78,71,13,10,26,10];for(w=0;w<8;w++)b[w]=y[w];if(l(b,h,13),h+=4,u(b,h,"IHDR"),h+=4,l(b,h,r),h+=4,l(b,h,o),h+=4,b[h]=e.depth,h++,b[h]=e.ctype,h++,b[h]=0,h++,b[h]=0,h++,b[h]=0,h++,l(b,h,f(b,h-17,17)),h+=4,null!=s.sRGB&&(l(b,h,1),h+=4,u(b,h,"sRGB"),h+=4,b[h]=s.sRGB,h++,l(b,h,f(b,h-5,5)),h+=4),null!=s.iCCP){const e=13+A.length;l(b,h,e),h+=4,u(b,h,"iCCP"),h+=4,u(b,h,"ICC profile"),h+=11,h+=2,b.set(A,h),h+=A.length,l(b,h,f(b,h-(e+4),e+4)),h+=4;}if(null!=s.pHYs&&(l(b,h,9),h+=4,u(b,h,"pHYs"),h+=4,l(b,h,s.pHYs[0]),h+=4,l(b,h,s.pHYs[1]),h+=4,b[h]=s.pHYs[2],h++,l(b,h,f(b,h-13,13)),h+=4),d&&(l(b,h,8),h+=4,u(b,h,"acTL"),h+=4,l(b,h,e.frames.length),h+=4,l(b,h,null!=s.loop?s.loop:0),h+=4,l(b,h,f(b,h-12,12)),h+=4),3==e.ctype){l(b,h,3*(m=e.plte.length)),h+=4,u(b,h,"PLTE"),h+=4;for(w=0;w<m;w++){const t=3*w,r=e.plte[w],i=255&r,o=r>>>8&255,a=r>>>16&255;b[h+t+0]=i,b[h+t+1]=o,b[h+t+2]=a;}if(h+=3*m,l(b,h,f(b,h-3*m-4,3*m+4)),h+=4,g){l(b,h,m),h+=4,u(b,h,"tRNS"),h+=4;for(w=0;w<m;w++)b[h+w]=e.plte[w]>>>24&255;h+=m,l(b,h,f(b,h-m-4,m+4)),h+=4;}}let E=0;for(v=0;v<e.frames.length;v++){var F=e.frames[v];d&&(l(b,h,26),h+=4,u(b,h,"fcTL"),h+=4,l(b,h,E++),h+=4,l(b,h,F.rect.width),h+=4,l(b,h,F.rect.height),h+=4,l(b,h,F.rect.x),h+=4,l(b,h,F.rect.y),h+=4,c(b,h,a[v]),h+=2,c(b,h,1e3),h+=2,b[h]=F.dispose,h++,b[h]=F.blend,h++,l(b,h,f(b,h-30,30)),h+=4);const t=F.cimg;l(b,h,(m=t.length)+(0==v?0:4)),h+=4;const r=h;u(b,h,0==v?"IDAT":"fdAT"),h+=4,0!=v&&(l(b,h,E++),h+=4),b.set(t,h),h+=m,l(b,h,f(b,r,h-r)),h+=4;}return l(b,h,0),h+=4,u(b,h,"IEND"),h+=4,l(b,h,f(b,h-4,4)),h+=4,b.buffer}function compressPNG(e,t,r){for(let i=0;i<e.frames.length;i++){const o=e.frames[i];const a=o.rect.height,s=new Uint8Array(a*o.bpl+a);o.cimg=_filterZero(o.img,a,o.bpp,o.bpl,s,t,r);}}function compress(t,r,i,o,a){const s=a[0],f=a[1],l=a[2],c=a[3],u=a[4],h=a[5];let d=6,A=8,g=255;for(var p=0;p<t.length;p++){const e=new Uint8Array(t[p]);for(var m=e.length,w=0;w<m;w+=4)g&=e[w+3];}const v=255!=g,b=function framize(t,r,i,o,a,s){const f=[];for(var l=0;l<t.length;l++){const h=new Uint8Array(t[l]),A=new Uint32Array(h.buffer);var c;let g=0,p=0,m=r,w=i,v=o?1:0;if(0!=l){const b=s||o||1==l||0!=f[l-2].dispose?1:2;let y=0,E=1e9;for(let e=0;e<b;e++){var u=new Uint8Array(t[l-1-e]);const o=new Uint32Array(t[l-1-e]);let s=r,f=i,c=-1,h=-1;for(let e=0;e<i;e++)for(let t=0;t<r;t++){A[d=e*r+t]!=o[d]&&(t<s&&(s=t),t>c&&(c=t),e<f&&(f=e),e>h&&(h=e));} -1==c&&(s=f=c=h=0),a&&(1==(1&s)&&s--,1==(1&f)&&f--);const v=(c-s+1)*(h-f+1);v<E&&(E=v,y=e,g=s,p=f,m=c-s+1,w=h-f+1);}u=new Uint8Array(t[l-1-y]);1==y&&(f[l-1].dispose=2),c=new Uint8Array(m*w*4),e(u,r,i,c,m,w,-g,-p,0),v=e(h,r,i,c,m,w,-g,-p,3)?1:0,1==v?_prepareDiff(h,r,i,c,{x:g,y:p,width:m,height:w}):e(h,r,i,c,m,w,-g,-p,0);}else c=h.slice(0);f.push({rect:{x:g,y:p,width:m,height:w},img:c,blend:v,dispose:0});}if(o)for(l=0;l<f.length;l++){if(1==(A=f[l]).blend)continue;const e=A.rect,o=f[l-1].rect,s=Math.min(e.x,o.x),c=Math.min(e.y,o.y),u={x:s,y:c,width:Math.max(e.x+e.width,o.x+o.width)-s,height:Math.max(e.y+e.height,o.y+o.height)-c};f[l-1].dispose=1,l-1!=0&&_updateFrame(t,r,i,f,l-1,u,a),_updateFrame(t,r,i,f,l,u,a);}let h=0;if(1!=t.length)for(var d=0;d<f.length;d++){var A;h+=(A=f[d]).rect.width*A.rect.height;}return f}(t,r,i,s,f,l),y={},E=[],F=[];if(0!=o){const e=[];for(w=0;w<b.length;w++)e.push(b[w].img.buffer);const t=function concatRGBA(e){let t=0;for(var r=0;r<e.length;r++)t+=e[r].byteLength;const i=new Uint8Array(t);let o=0;for(r=0;r<e.length;r++){const t=new Uint8Array(e[r]),a=t.length;for(let e=0;e<a;e+=4){let r=t[e],a=t[e+1],s=t[e+2];const f=t[e+3];0==f&&(r=a=s=0),i[o+e]=r,i[o+e+1]=a,i[o+e+2]=s,i[o+e+3]=f;}o+=a;}return i.buffer}(e),r=quantize(t,o);for(w=0;w<r.plte.length;w++)E.push(r.plte[w].est.rgba);let i=0;for(w=0;w<b.length;w++){const e=(B=b[w]).img.length;var _=new Uint8Array(r.inds.buffer,i>>2,e>>2);F.push(_);const t=new Uint8Array(r.abuf,i,e);h&&dither(B.img,B.rect.width,B.rect.height,E,t,_),B.img.set(t),i+=e;}}else for(p=0;p<b.length;p++){var B=b[p];const e=new Uint32Array(B.img.buffer);var U=B.rect.width;m=e.length,_=new Uint8Array(m);F.push(_);for(w=0;w<m;w++){const t=e[w];if(0!=w&&t==e[w-1])_[w]=_[w-1];else if(w>U&&t==e[w-U])_[w]=_[w-U];else {let e=y[t];if(null==e&&(y[t]=e=E.length,E.push(t),E.length>=300))break;_[w]=e;}}}const C=E.length;C<=256&&0==u&&(A=C<=2?1:C<=4?2:C<=16?4:8,A=Math.max(A,c));for(p=0;p<b.length;p++){(B=b[p]).rect.x;U=B.rect.width;const e=B.rect.height;let t=B.img;let r=4*U,i=4;if(C<=256&&0==u){r=Math.ceil(A*U/8);var I=new Uint8Array(r*e);const o=F[p];for(let t=0;t<e;t++){w=t*r;const e=t*U;if(8==A)for(var Q=0;Q<U;Q++)I[w+Q]=o[e+Q];else if(4==A)for(Q=0;Q<U;Q++)I[w+(Q>>1)]|=o[e+Q]<<4-4*(1&Q);else if(2==A)for(Q=0;Q<U;Q++)I[w+(Q>>2)]|=o[e+Q]<<6-2*(3&Q);else if(1==A)for(Q=0;Q<U;Q++)I[w+(Q>>3)]|=o[e+Q]<<7-1*(7&Q);}t=I,d=3,i=1;}else if(0==v&&1==b.length){I=new Uint8Array(U*e*3);const o=U*e;for(w=0;w<o;w++){const e=3*w,r=4*w;I[e]=t[r],I[e+1]=t[r+1],I[e+2]=t[r+2];}t=I,d=2,i=3,r=3*U;}B.img=t,B.bpl=r,B.bpp=i;}return {ctype:d,depth:A,plte:E,frames:b}}function _updateFrame(t,r,i,o,a,s,f){const l=Uint8Array,c=Uint32Array,u=new l(t[a-1]),h=new c(t[a-1]),d=a+1<t.length?new l(t[a+1]):null,A=new l(t[a]),g=new c(A.buffer);let p=r,m=i,w=-1,v=-1;for(let e=0;e<s.height;e++)for(let t=0;t<s.width;t++){const i=s.x+t,f=s.y+e,l=f*r+i,c=g[l];0==c||0==o[a-1].dispose&&h[l]==c&&(null==d||0!=d[4*l+3])||(i<p&&(p=i),i>w&&(w=i),f<m&&(m=f),f>v&&(v=f));} -1==w&&(p=m=w=v=0),f&&(1==(1&p)&&p--,1==(1&m)&&m--),s={x:p,y:m,width:w-p+1,height:v-m+1};const b=o[a];b.rect=s,b.blend=1,b.img=new Uint8Array(s.width*s.height*4),0==o[a-1].dispose?(e(u,r,i,b.img,s.width,s.height,-s.x,-s.y,0),_prepareDiff(A,r,i,b.img,s)):e(A,r,i,b.img,s.width,s.height,-s.x,-s.y,0);}function _prepareDiff(t,r,i,o,a){e(t,r,i,o,a.width,a.height,-a.x,-a.y,2);}function _filterZero(e,t,r,i,o,a,s){const f=[];let l,c=[0,1,2,3,4];-1!=a?c=[a]:(t*i>5e5||1==r)&&(c=[0]),s&&(l={level:0});const u=UZIP;for(var h=0;h<c.length;h++){for(let a=0;a<t;a++)_filterLine(o,e,a,i,r,c[h]);f.push(u.deflate(o,l));}let d,A=1e9;for(h=0;h<f.length;h++)f[h].length<A&&(d=h,A=f[h].length);return f[d]}function _filterLine(e,t,i,o,a,s){const f=i*o;let l=f+i;if(e[l]=s,l++,0==s)if(o<500)for(var c=0;c<o;c++)e[l+c]=t[f+c];else e.set(new Uint8Array(t.buffer,f,o),l);else if(1==s){for(c=0;c<a;c++)e[l+c]=t[f+c];for(c=a;c<o;c++)e[l+c]=t[f+c]-t[f+c-a]+256&255;}else if(0==i){for(c=0;c<a;c++)e[l+c]=t[f+c];if(2==s)for(c=a;c<o;c++)e[l+c]=t[f+c];if(3==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-(t[f+c-a]>>1)+256&255;if(4==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-r(t[f+c-a],0,0)+256&255;}else {if(2==s)for(c=0;c<o;c++)e[l+c]=t[f+c]+256-t[f+c-o]&255;if(3==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-(t[f+c-o]>>1)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-(t[f+c-o]+t[f+c-a]>>1)&255;}if(4==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-r(0,t[f+c-o],0)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-r(t[f+c-a],t[f+c-o],t[f+c-a-o])&255;}}}function quantize(e,t){const r=new Uint8Array(e),i=r.slice(0),o=new Uint32Array(i.buffer),a=getKDtree(i,t),s=a[0],f=a[1],l=r.length,c=new Uint8Array(l>>2);let u;if(r.length<2e7)for(var h=0;h<l;h+=4){u=getNearest(s,d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255)),c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}else for(h=0;h<l;h+=4){var d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255);for(u=s;u.left;)u=planeDst(u.est,d,A,g,p)<=0?u.left:u.right;c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}return {abuf:i.buffer,inds:c,plte:f}}function getKDtree(e,t,r){null==r&&(r=1e-4);const i=new Uint32Array(e.buffer),o={i0:0,i1:e.length,bst:null,est:null,tdst:0,left:null,right:null};o.bst=stats(e,o.i0,o.i1),o.est=estats(o.bst);const a=[o];for(;a.length<t;){let t=0,o=0;for(var s=0;s<a.length;s++)a[s].est.L>t&&(t=a[s].est.L,o=s);if(t<r)break;const f=a[o],l=splitPixels(e,i,f.i0,f.i1,f.est.e,f.est.eMq255);if(f.i0>=l||f.i1<=l){f.est.L=0;continue}const c={i0:f.i0,i1:l,bst:null,est:null,tdst:0,left:null,right:null};c.bst=stats(e,c.i0,c.i1),c.est=estats(c.bst);const u={i0:l,i1:f.i1,bst:null,est:null,tdst:0,left:null,right:null};u.bst={R:[],m:[],N:f.bst.N-c.bst.N};for(s=0;s<16;s++)u.bst.R[s]=f.bst.R[s]-c.bst.R[s];for(s=0;s<4;s++)u.bst.m[s]=f.bst.m[s]-c.bst.m[s];u.est=estats(u.bst),f.left=c,f.right=u,a[o]=c,a.push(u);}a.sort(((e,t)=>t.bst.N-e.bst.N));for(s=0;s<a.length;s++)a[s].ind=s;return [o,a]}function getNearest(e,t,r,i,o){if(null==e.left)return e.tdst=function dist(e,t,r,i,o){const a=t-e[0],s=r-e[1],f=i-e[2],l=o-e[3];return a*a+s*s+f*f+l*l}(e.est.q,t,r,i,o),e;const a=planeDst(e.est,t,r,i,o);let s=e.left,f=e.right;a>0&&(s=e.right,f=e.left);const l=getNearest(s,t,r,i,o);if(l.tdst<=a*a)return l;const c=getNearest(f,t,r,i,o);return c.tdst<l.tdst?c:l}function planeDst(e,t,r,i,o){const{e:a}=e;return a[0]*t+a[1]*r+a[2]*i+a[3]*o-e.eMq}function splitPixels(e,t,r,i,o,a){for(i-=4;r<i;){for(;vecDot(e,r,o)<=a;)r+=4;for(;vecDot(e,i,o)>a;)i-=4;if(r>=i)break;const s=t[r>>2];t[r>>2]=t[i>>2],t[i>>2]=s,r+=4,i-=4;}for(;vecDot(e,r,o)>a;)r-=4;return r+4}function vecDot(e,t,r){return e[t]*r[0]+e[t+1]*r[1]+e[t+2]*r[2]+e[t+3]*r[3]}function stats(e,t,r){const i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o=[0,0,0,0],a=r-t>>2;for(let a=t;a<r;a+=4){const t=e[a]*(1/255),r=e[a+1]*(1/255),s=e[a+2]*(1/255),f=e[a+3]*(1/255);o[0]+=t,o[1]+=r,o[2]+=s,o[3]+=f,i[0]+=t*t,i[1]+=t*r,i[2]+=t*s,i[3]+=t*f,i[5]+=r*r,i[6]+=r*s,i[7]+=r*f,i[10]+=s*s,i[11]+=s*f,i[15]+=f*f;}return i[4]=i[1],i[8]=i[2],i[9]=i[6],i[12]=i[3],i[13]=i[7],i[14]=i[11],{R:i,m:o,N:a}}function estats(e){const{R:t}=e,{m:r}=e,{N:i}=e,a=r[0],s=r[1],f=r[2],l=r[3],c=0==i?0:1/i,u=[t[0]-a*a*c,t[1]-a*s*c,t[2]-a*f*c,t[3]-a*l*c,t[4]-s*a*c,t[5]-s*s*c,t[6]-s*f*c,t[7]-s*l*c,t[8]-f*a*c,t[9]-f*s*c,t[10]-f*f*c,t[11]-f*l*c,t[12]-l*a*c,t[13]-l*s*c,t[14]-l*f*c,t[15]-l*l*c],h=u,d=o;let A=[Math.random(),Math.random(),Math.random(),Math.random()],g=0,p=0;if(0!=i)for(let e=0;e<16&&(A=d.multVec(h,A),p=Math.sqrt(d.dot(A,A)),A=d.sml(1/p,A),!(0!=e&&Math.abs(p-g)<1e-9));e++)g=p;const m=[a*c,s*c,f*c,l*c];return {Cov:u,q:m,e:A,L:g,eMq255:d.dot(d.sml(255,m),A),eMq:d.dot(A,m),rgba:(Math.round(255*m[3])<<24|Math.round(255*m[2])<<16|Math.round(255*m[1])<<8|Math.round(255*m[0])<<0)>>>0}}var o={multVec:(e,t)=>[e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7]*t[3],e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11]*t[3],e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]*t[3]],dot:(e,t)=>e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],sml:(e,t)=>[e*t[0],e*t[1],e*t[2],e*t[3]]};UPNG.encode=function encode(e,t,r,i,o,a,s){null==i&&(i=0),null==s&&(s=false);const f=compress(e,t,r,i,[false,false,false,0,s,false]);return compressPNG(f,-1),_main(f,t,r,o,a)},UPNG.encodeLL=function encodeLL(e,t,r,i,o,a,s,f){const l={ctype:0+(1==i?0:2)+(0==o?0:4),depth:a,frames:[]},c=(i+o)*a,u=c*t;for(let i=0;i<e.length;i++)l.frames.push({rect:{x:0,y:0,width:t,height:r},img:new Uint8Array(e[i]),blend:0,dispose:1,bpp:Math.ceil(c/8),bpl:Math.ceil(u/8)});return compressPNG(l,0,true),_main(l,t,r,s,f)},UPNG.encode.compress=compress,UPNG.encode.dither=dither,UPNG.quantize=quantize,UPNG.quantize.getKDtree=getKDtree,UPNG.quantize.getNearest=getNearest;}();const r={toArrayBuffer(e,t){const i=e.width,o=e.height,a=i<<2,s=e.getContext("2d").getImageData(0,0,i,o),f=new Uint32Array(s.data.buffer),l=(32*i+31)/32<<2,c=l*o,u=122+c,h=new ArrayBuffer(u),d=new DataView(h),A=1<<20;let g,p,m,w,v=A,b=0,y=0,E=0;function set16(e){d.setUint16(y,e,true),y+=2;}function set32(e){d.setUint32(y,e,true),y+=4;}function seek(e){y+=e;}set16(19778),set32(u),seek(4),set32(122),set32(108),set32(i),set32(-o>>>0),set16(1),set16(32),set32(3),set32(c),set32(2835),set32(2835),seek(8),set32(16711680),set32(65280),set32(255),set32(4278190080),set32(1466527264),function convert(){for(;b<o&&v>0;){for(w=122+b*l,g=0;g<a;)v--,p=f[E++],m=p>>>24,d.setUint32(w+g,p<<8|m),g+=4;b++;}E<f.length?(v=A,setTimeout(convert,r._dly)):t(h);}();},toBlob(e,t){this.toArrayBuffer(e,(e=>{t(new Blob([e],{type:"image/bmp"}));}));},_dly:9};var i={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},o={[i.CHROME]:16384,[i.FIREFOX]:11180,[i.DESKTOP_SAFARI]:16384,[i.IE]:8192,[i.IOS]:4096,[i.ETC]:8192};const a="undefined"!=typeof window,s="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,f=a&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),CustomFile=(a||s)&&(f&&f.getOriginalSymbol(window,"File")||"undefined"!=typeof File&&File),CustomFileReader=(a||s)&&(f&&f.getOriginalSymbol(window,"FileReader")||"undefined"!=typeof FileReader&&FileReader);function getFilefromDataUrl(e,t,r=Date.now()){return new Promise((i=>{const o=e.split(","),a=o[0].match(/:(.*?);/)[1],s=globalThis.atob(o[1]);let f=s.length;const l=new Uint8Array(f);for(;f--;)l[f]=s.charCodeAt(f);const c=new Blob([l],{type:a});c.name=t,c.lastModified=r,i(c);}))}function getDataUrlFromFile(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=()=>t(i.result),i.onerror=e=>r(e),i.readAsDataURL(e);}))}function loadImage(e){return new Promise(((t,r)=>{const i=new Image;i.onload=()=>t(i),i.onerror=e=>r(e),i.src=e;}))}function getBrowserName(){if(void 0!==getBrowserName.cachedResult)return getBrowserName.cachedResult;let e=i.ETC;const{userAgent:t}=navigator;return /Chrom(e|ium)/i.test(t)?e=i.CHROME:/iP(ad|od|hone)/i.test(t)&&/WebKit/i.test(t)?e=i.IOS:/Safari/i.test(t)?e=i.DESKTOP_SAFARI:/Firefox/i.test(t)?e=i.FIREFOX:(/MSIE/i.test(t)||true==!!document.documentMode)&&(e=i.IE),getBrowserName.cachedResult=e,getBrowserName.cachedResult}function approximateBelowMaximumCanvasSizeOfBrowser(e,t){const r=getBrowserName(),i=o[r];let a=e,s=t,f=a*s;const l=a>s?s/a:a/s;for(;f>i*i;){const e=(i+a)/2,t=(i+s)/2;e<t?(s=t,a=t*l):(s=e*l,a=e),f=a*s;}return {width:a,height:s}}function getNewCanvasAndCtx(e,t){let r,i;try{if(r=new OffscreenCanvas(e,t),i=r.getContext("2d"),null===i)throw new Error("getContext of OffscreenCanvas returns null")}catch(e){r=document.createElement("canvas"),i=r.getContext("2d");}return r.width=e,r.height=t,[r,i]}function drawImageInCanvas(e,t){const{width:r,height:i}=approximateBelowMaximumCanvasSizeOfBrowser(e.width,e.height),[o,a]=getNewCanvasAndCtx(r,i);return t&&/jpe?g/.test(t)&&(a.fillStyle="white",a.fillRect(0,0,o.width,o.height)),a.drawImage(e,0,0,o.width,o.height),o}function isIOS(){return void 0!==isIOS.cachedResult||(isIOS.cachedResult=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"undefined"!=typeof document&&"ontouchend"in document),isIOS.cachedResult}function drawFileInCanvas(e,t={}){return new Promise((function(r,o){let a,s;var $Try_2_Post=function(){try{return s=drawImageInCanvas(a,t.fileType||e.type),r([a,s])}catch(e){return o(e)}},$Try_2_Catch=function(t){try{var $Try_3_Catch=function(e){try{throw e}catch(e){return o(e)}};try{let t;return getDataUrlFromFile(e).then((function(e){try{return t=e,loadImage(t).then((function(e){try{return a=e,function(){try{return $Try_2_Post()}catch(e){return o(e)}}()}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){$Try_3_Catch(e);}}catch(e){return o(e)}};try{if(isIOS()||[i.DESKTOP_SAFARI,i.MOBILE_SAFARI].includes(getBrowserName()))throw new Error("Skip createImageBitmap on IOS and Safari");return createImageBitmap(e).then((function(e){try{return a=e,$Try_2_Post()}catch(e){return $Try_2_Catch()}}),$Try_2_Catch)}catch(e){$Try_2_Catch();}}))}function canvasToFile(e,t,i,o,a=1){return new Promise((function(s,f){let l;if("image/png"===t){let c,u,h;return c=e.getContext("2d"),({data:u}=c.getImageData(0,0,e.width,e.height)),h=UPNG.encode([u.buffer],e.width,e.height,4096*a),l=new Blob([h],{type:t}),l.name=i,l.lastModified=o,$If_4.call(this)}{if("image/bmp"===t)return new Promise((t=>r.toBlob(e,t))).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_5.call(this)}catch(e){return f(e)}}.bind(this),f);{if("function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas)return e.convertToBlob({type:t,quality:a}).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f);{let d;return d=e.toDataURL(t,a),getFilefromDataUrl(d,i,o).then(function(e){try{return l=e,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f)}function $If_6(){return $If_5.call(this)}}function $If_5(){return $If_4.call(this)}}function $If_4(){return s(l)}}))}function cleanupCanvasMemory(e){e.width=0,e.height=0;}function isAutoOrientationInBrowser(){return new Promise((function(e,t){let i,o,a,s;return void 0!==isAutoOrientationInBrowser.cachedResult?e(isAutoOrientationInBrowser.cachedResult):(getFilefromDataUrl("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(r){try{return i=r,drawFileInCanvas(i).then((function(r){try{return o=r[1],canvasToFile(o,i.type,i.name,i.lastModified).then((function(r){try{return a=r,cleanupCanvasMemory(o),drawFileInCanvas(a).then((function(r){try{return s=r[0],isAutoOrientationInBrowser.cachedResult=1===s.width&&2===s.height,e(isAutoOrientationInBrowser.cachedResult)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t))}))}function getExifOrientation(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=e=>{const r=new DataView(e.target.result);if(65496!=r.getUint16(0,false))return t(-2);const i=r.byteLength;let o=2;for(;o<i;){if(r.getUint16(o+2,false)<=8)return t(-1);const e=r.getUint16(o,false);if(o+=2,65505==e){if(1165519206!=r.getUint32(o+=2,false))return t(-1);const e=18761==r.getUint16(o+=6,false);o+=r.getUint32(o+4,e);const i=r.getUint16(o,e);o+=2;for(let a=0;a<i;a++)if(274==r.getUint16(o+12*a,e))return t(r.getUint16(o+12*a+8,e))}else {if(65280!=(65280&e))break;o+=r.getUint16(o,false);}}return t(-1)},i.onerror=e=>r(e),i.readAsArrayBuffer(e);}))}function handleMaxWidthOrHeight(e,t){const{width:r}=e,{height:i}=e,{maxWidthOrHeight:o}=t;let a,s=e;return isFinite(o)&&(r>o||i>o)&&([s,a]=getNewCanvasAndCtx(r,i),r>i?(s.width=o,s.height=i/r*o):(s.width=r/i*o,s.height=o),a.drawImage(e,0,0,s.width,s.height),cleanupCanvasMemory(e)),s}function followExifOrientation(e,t){const{width:r}=e,{height:i}=e,[o,a]=getNewCanvasAndCtx(r,i);switch(t>4&&t<9?(o.width=i,o.height=r):(o.width=r,o.height=i),t){case 2:a.transform(-1,0,0,1,r,0);break;case 3:a.transform(-1,0,0,-1,r,i);break;case 4:a.transform(1,0,0,-1,0,i);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,i,0);break;case 7:a.transform(0,-1,-1,0,i,r);break;case 8:a.transform(0,-1,1,0,0,r);}return a.drawImage(e,0,0,r,i),cleanupCanvasMemory(e),o}function compress(e,t,r=0){return new Promise((function(i,o){let a,s,f,l,c,u,h,d,A,g,p,m,w,v,b,y,E,F,_,B;function incProgress(e=5){if(t.signal&&t.signal.aborted)throw t.signal.reason;a+=e,t.onProgress(Math.min(a,100));}function setProgress(e){if(t.signal&&t.signal.aborted)throw t.signal.reason;a=Math.min(Math.max(e,a),100),t.onProgress(a);}return a=r,s=t.maxIteration||10,f=1024*t.maxSizeMB*1024,incProgress(),drawFileInCanvas(e,t).then(function(r){try{return [,l]=r,incProgress(),c=handleMaxWidthOrHeight(l,t),incProgress(),new Promise((function(r,i){var o;if(!(o=t.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return i(e)}}.bind(this),i);function $If_2(){return r(o)}return $If_2.call(this)})).then(function(r){try{return u=r,incProgress(),isAutoOrientationInBrowser().then(function(r){try{return h=r?c:followExifOrientation(c,u),incProgress(),d=t.initialQuality||1,A=t.fileType||e.type,canvasToFile(h,A,e.name,e.lastModified,d).then(function(r){try{{if(g=r,incProgress(),p=g.size>f,m=g.size>e.size,!p&&!m)return setProgress(100),i(g);var a;function $Loop_3(){if(s--&&(b>f||b>w)){let t,r;return t=B?.95*_.width:_.width,r=B?.95*_.height:_.height,[E,F]=getNewCanvasAndCtx(t,r),F.drawImage(_,0,0,t,r),d*="image/png"===A?.85:.95,canvasToFile(E,A,e.name,e.lastModified,d).then((function(e){try{return y=e,cleanupCanvasMemory(_),_=E,b=y.size,setProgress(Math.min(99,Math.floor((v-b)/(v-f)*100))),$Loop_3}catch(e){return o(e)}}),o)}return [1]}return w=e.size,v=g.size,b=v,_=h,B=!t.alwaysKeepResolution&&p,(a=function(e){for(;e;){if(e.then)return void e.then(a,o);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3;}else e=e.call(this);}catch(e){return o(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(_),cleanupCanvasMemory(E),cleanupCanvasMemory(c),cleanupCanvasMemory(h),cleanupCanvasMemory(l),setProgress(100),i(y)}}}catch(u){return o(u)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}))}const l="\nlet scriptImported = false\nself.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n})\n";let c;function compressOnWebWorker(e,t){return new Promise(((r,i)=>{c||(c=function createWorkerScriptURL(e){const t=[];return t.push(e),URL.createObjectURL(new Blob(t))}(l));const o=new Worker(c);o.addEventListener("message",(function handler(e){if(t.signal&&t.signal.aborted)o.terminate();else if(void 0===e.data.progress){if(e.data.error)return i(new Error(e.data.error)),void o.terminate();r(e.data.file),o.terminate();}else t.onProgress(e.data.progress);})),o.addEventListener("error",i),t.signal&&t.signal.addEventListener("abort",(()=>{i(t.signal.reason),o.terminate();})),o.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}});}))}function imageCompression(e,t){return new Promise((function(r,i){let o,a,s,f,l,c;if(o={...t},s=0,({onProgress:f}=o),o.maxSizeMB=o.maxSizeMB||Number.POSITIVE_INFINITY,l="boolean"!=typeof o.useWebWorker||o.useWebWorker,delete o.useWebWorker,o.onProgress=e=>{s=e,"function"==typeof f&&f(s);},!(e instanceof Blob||e instanceof CustomFile))return i(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return i(new Error("The file given is not an image"));if(c="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!l||"function"!=typeof Worker||c)return compress(e,o).then(function(e){try{return a=e,$If_4.call(this)}catch(e){return i(e)}}.bind(this),i);var u=function(){try{return $If_4.call(this)}catch(e){return i(e)}}.bind(this),$Try_1_Catch=function(t){try{return compress(e,o).then((function(e){try{return a=e,u()}catch(e){return i(e)}}),i)}catch(e){return i(e)}};try{return o.libURL=o.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",compressOnWebWorker(e,o).then((function(e){try{return a=e,u()}catch(e){return $Try_1_Catch()}}),$Try_1_Catch)}catch(e){$Try_1_Catch();}function $If_4(){try{a.name=e.name,a.lastModified=e.lastModified;}catch(e){}try{o.preserveExif&&"image/jpeg"===e.type&&(!o.fileType||o.fileType&&o.fileType===e.type)&&(a=copyExifWithoutOrientation(e,a));}catch(e){}return r(a)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupCanvasMemory=cleanupCanvasMemory,imageCompression.isAutoOrientationInBrowser=isAutoOrientationInBrowser,imageCompression.approximateBelowMaximumCanvasSizeOfBrowser=approximateBelowMaximumCanvasSizeOfBrowser,imageCompression.copyExifWithoutOrientation=copyExifWithoutOrientation,imageCompression.getBrowserName=getBrowserName,imageCompression.version="2.0.2";
14772
-
14773
- class KritzelImageTool extends KritzelBaseTool {
14774
- fileInput = null;
14775
- maxCompressionSize = 300;
14776
- constructor(core) {
14777
- super(core);
14778
- this.setupFileInput();
15330
+ deselectAllChildren() {
15331
+ this.objects.forEach(obj => (obj.isSelected = false));
14779
15332
  }
14780
- onActivate() {
14781
- this.openFilePicker();
15333
+ updateWorkspaceId(workspaceId) {
15334
+ this.workspaceId = workspaceId;
15335
+ this.objects.forEach(obj => (obj.workspaceId = workspaceId));
14782
15336
  }
14783
- openFilePicker() {
14784
- if (this._core.store.isDisabled) {
14785
- return;
14786
- }
14787
- this.fileInput.click();
15337
+ updateZIndices(startZIndex) {
15338
+ this.objects.forEach((obj, i) => {
15339
+ obj.zIndex = startZIndex + i;
15340
+ });
14788
15341
  }
14789
- setupFileInput() {
14790
- this.fileInput = document.createElement('input');
14791
- this.fileInput.type = 'file';
14792
- this.fileInput.accept = 'image/*';
14793
- this.fileInput.style.display = 'none';
14794
- this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
14795
- this.fileInput.addEventListener('cancel', this.handleCancel.bind(this));
14796
- document.body.appendChild(this.fileInput);
15342
+ updatePosition(x, y) {
15343
+ this.objects.forEach(obj => {
15344
+ const deltaX = obj.translateX - this.translateX;
15345
+ const deltaY = obj.translateY - this.translateY;
15346
+ obj.updatePosition(x + deltaX, y + deltaY);
15347
+ });
15348
+ // Update snapshots
15349
+ this.unchangedObjectSnapshots.forEach(snapshot => {
15350
+ const deltaX = snapshot.translateX - this.translateX;
15351
+ const deltaY = snapshot.translateY - this.translateY;
15352
+ snapshot.translateX = x + deltaX;
15353
+ snapshot.translateY = x + deltaY;
15354
+ });
15355
+ this.translateX = x;
15356
+ this.translateY = y;
15357
+ this._core.store.state.objects.update(this);
14797
15358
  }
14798
- handleFileSelect(event) {
14799
- const input = event.target;
14800
- if (input.files && input.files[0]) {
14801
- const file = input.files[0];
14802
- imageCompression(file, {
14803
- maxWidthOrHeight: this.maxCompressionSize,
14804
- })
14805
- .then(compressedFile => {
14806
- this.readFile(compressedFile);
14807
- })
14808
- .catch(error => {
14809
- console.error('Error during image compression or processing:', error);
14810
- this.handleCancel();
15359
+ /**
15360
+ * Capture snapshots of current object states for undo/redo operations
15361
+ */
15362
+ captureUnchangedSnapshots() {
15363
+ this.unchangedObjectSnapshots.clear();
15364
+ this.snapshotRotation = this.rotation;
15365
+ this.objects.forEach(obj => {
15366
+ this.unchangedObjectSnapshots.set(obj.id, {
15367
+ id: obj.id,
15368
+ translateX: obj.translateX,
15369
+ translateY: obj.translateY,
15370
+ rotation: obj.rotation,
15371
+ width: obj.width,
15372
+ height: obj.height,
15373
+ totalWidth: obj.totalWidth,
15374
+ totalHeight: obj.totalHeight,
15375
+ scale: obj.scale,
14811
15376
  });
15377
+ });
15378
+ }
15379
+ serialize() {
15380
+ const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, ...remainingProps } = this;
15381
+ const clonedProps = structuredClone(remainingProps);
15382
+ if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
15383
+ clonedProps.element = element.cloneNode(true);
14812
15384
  }
14813
- else {
14814
- console.info('File selection cancelled by user.');
14815
- this.handleCancel();
14816
- }
14817
- if (input) {
14818
- input.value = '';
15385
+ // Convert Map to plain object for serialization
15386
+ clonedProps.unchangedObjectSnapshots = Object.fromEntries(this.unchangedObjectSnapshots);
15387
+ return clonedProps;
15388
+ }
15389
+ deserialize(object) {
15390
+ // First, deserialize all base properties using parent's deserialize
15391
+ super.deserialize(object);
15392
+ // Restore the Map from serialized object
15393
+ if (object.unchangedObjectSnapshots) {
15394
+ this.unchangedObjectSnapshots = new Map(Object.entries(object.unchangedObjectSnapshots));
14819
15395
  }
15396
+ return this;
14820
15397
  }
14821
- readFile(file) {
14822
- const reader = new FileReader();
14823
- reader.onload = e => {
14824
- const img = new Image();
14825
- img.src = e.target?.result;
14826
- img.onload = () => this.createKritzelImage(img);
14827
- };
14828
- reader.readAsDataURL(file);
15398
+ update() {
15399
+ // Only update the selection group itself
15400
+ // Child objects are already updated during move/resize/rotate operations
15401
+ // Updating them again here would create redundant y.js updates
15402
+ this._core.store.state.objects.update(this);
14829
15403
  }
14830
- createKritzelImage(img) {
14831
- const image = KritzelImage.create(this._core);
14832
- const { scaledWidth, scaledHeight } = image.calculateScaledDimensions(img);
14833
- image.src = img.src;
14834
- image.width = scaledWidth;
14835
- image.height = scaledHeight;
14836
- image.zIndex = this._core.store.currentZIndex;
14837
- image.centerInViewport();
14838
- this.addImageToStore(image);
14839
- return image;
15404
+ move(startX, startY, endX, endY) {
15405
+ const deltaX = (startX - endX) / this._core.store.state.scale;
15406
+ const deltaY = (startY - endY) / this._core.store.state.scale;
15407
+ this.translateX += deltaX;
15408
+ this.translateY += deltaY;
15409
+ this._core.store.state.objects.transaction(() => {
15410
+ this._core.store.state.objects.update(this);
15411
+ this.objects.forEach(obj => {
15412
+ obj.move(startX, startY, endX, endY);
15413
+ // Update any lines that are anchored to this object
15414
+ this._core.anchorManager.updateAnchorsForObject(obj.id);
15415
+ });
15416
+ });
15417
+ // Update snapshots
15418
+ this.unchangedObjectSnapshots.forEach(snapshot => {
15419
+ snapshot.translateX += deltaX;
15420
+ snapshot.translateY += deltaY;
15421
+ });
14840
15422
  }
14841
- addImageToStore(image) {
14842
- this._core.addObject(image);
14843
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
14844
- this._core.selectObjects([image]);
14845
- this._core.engine.emitObjectsChange();
15423
+ resize(x, y, width, height) {
15424
+ const widthScaleFactor = width / this.width;
15425
+ const heightScaleFactor = height / this.height;
15426
+ // Calculate old center
15427
+ const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
15428
+ const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
15429
+ // Calculate new center
15430
+ const newTotalWidth = width + this.padding * 2;
15431
+ const newTotalHeight = height + this.padding * 2;
15432
+ const newCenterX = x + newTotalWidth / 2 / this.scale;
15433
+ const newCenterY = y + newTotalHeight / 2 / this.scale;
15434
+ const rotation = this.rotation;
15435
+ const cos = Math.cos(-rotation);
15436
+ const sin = Math.sin(-rotation);
15437
+ const cosR = Math.cos(rotation);
15438
+ const sinR = Math.sin(rotation);
15439
+ this._core.store.state.objects.transaction(() => {
15440
+ this.objects.forEach(child => {
15441
+ // Calculate child center
15442
+ const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
15443
+ const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
15444
+ // Vector from old group center to child center
15445
+ const dx = childCenterX - oldCenterX;
15446
+ const dy = childCenterY - oldCenterY;
15447
+ // Rotate to local space (align with group axes)
15448
+ const localX = dx * cos - dy * sin;
15449
+ const localY = dx * sin + dy * cos;
15450
+ // Scale in local space
15451
+ const scaledLocalX = localX * widthScaleFactor;
15452
+ const scaledLocalY = localY * heightScaleFactor;
15453
+ // Rotate back to world space
15454
+ const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
15455
+ const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
15456
+ // New child center
15457
+ const newChildCenterX = newCenterX + rotatedX;
15458
+ const newChildCenterY = newCenterY + rotatedY;
15459
+ // New child top-left
15460
+ // Calculate relative rotation
15461
+ const relativeRotation = child.rotation - rotation;
15462
+ const cosRel = Math.cos(relativeRotation);
15463
+ const sinRel = Math.sin(relativeRotation);
15464
+ // Project the group's scale factors onto the child's local axes
15465
+ // We use absolute values because scaling is magnitude-based
15466
+ // If the child is aligned (0 deg), cos=1, sin=0 -> scales match
15467
+ // If the child is 90 deg, cos=0, sin=1 -> scales swap
15468
+ const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
15469
+ const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
15470
+ const updatedWidth = child.width * newChildWidthScale;
15471
+ const updatedHeight = child.height * newChildHeightScale;
15472
+ const updatedTotalWidth = updatedWidth + child.padding * 2;
15473
+ const updatedTotalHeight = updatedHeight + child.padding * 2;
15474
+ const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
15475
+ const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
15476
+ child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
15477
+ // Update any lines that are anchored to this child object
15478
+ this._core.anchorManager.updateAnchorsForObject(child.id);
15479
+ });
15480
+ // Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
15481
+ this.refreshObjectDimensions();
15482
+ this.captureUnchangedSnapshots();
15483
+ this._core.store.state.objects.update(this);
15484
+ });
14846
15485
  }
14847
- handleCancel() {
14848
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
15486
+ rotate(value) {
15487
+ this.rotation = value;
15488
+ const centerX = this.translateX + this.totalWidth / 2 / this.scale;
15489
+ const centerY = this.translateY + this.totalHeight / 2 / this.scale;
15490
+ const angle = value - this.snapshotRotation;
15491
+ const cos = Math.cos(angle);
15492
+ const sin = Math.sin(angle);
15493
+ this._core.store.state.objects.transaction(() => {
15494
+ // Update the SelectionGroup itself to propagate rotation to other tabs
15495
+ this._core.store.state.objects.update(this);
15496
+ this.objects.forEach(child => {
15497
+ const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
15498
+ if (!unchangedSnapshot)
15499
+ return;
15500
+ const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
15501
+ const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
15502
+ const rotatedX = cos * offsetX - sin * offsetY;
15503
+ const rotatedY = sin * offsetX + cos * offsetY;
15504
+ child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
15505
+ child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
15506
+ child.rotate(this.objects.length === 1 ? value : unchangedSnapshot.rotation + angle);
15507
+ });
15508
+ });
15509
+ }
15510
+ copy() {
15511
+ const selectionGroup = KritzelSelectionGroup.create(this._core);
15512
+ this.objects
15513
+ .sort((a, b) => a.zIndex - b.zIndex)
15514
+ .forEach(obj => {
15515
+ const copiedObject = obj.copy();
15516
+ selectionGroup.addOrRemove(copiedObject);
15517
+ });
15518
+ selectionGroup.captureUnchangedSnapshots();
15519
+ if (this.objects.length === 1) {
15520
+ selectionGroup.rotation = this.objects[0].rotation;
15521
+ }
15522
+ return selectionGroup;
15523
+ }
15524
+ refreshObjectDimensions() {
15525
+ if (this.objects.length === 1) {
15526
+ const obj = this.objects[0];
15527
+ this.minX = obj.boundingBox.x / this.scale;
15528
+ this.maxX = obj.boundingBox.x / this.scale + obj.boundingBox.width;
15529
+ this.minY = obj.boundingBox.y / this.scale;
15530
+ this.maxY = obj.boundingBox.y / this.scale + obj.boundingBox.height;
15531
+ this.translateX = (this.minX - this.padding) * this.scale;
15532
+ this.translateY = (this.minY - this.padding) * this.scale;
15533
+ this.width = (this.maxX - this.minX - this.padding) * this.scale;
15534
+ this.height = (this.maxY - this.minY - this.padding) * this.scale;
15535
+ }
15536
+ else {
15537
+ const rotation = this.rotation;
15538
+ const cos = Math.cos(-rotation);
15539
+ const sin = Math.sin(-rotation);
15540
+ let minX = Infinity;
15541
+ let maxX = -Infinity;
15542
+ let minY = Infinity;
15543
+ let maxY = -Infinity;
15544
+ this.objects.forEach(obj => {
15545
+ const polygon = obj.rotatedPolygon;
15546
+ const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
15547
+ corners.forEach(corner => {
15548
+ // Rotate corner into local space (aligned with group rotation)
15549
+ const rx = corner.x * cos - corner.y * sin;
15550
+ const ry = corner.x * sin + corner.y * cos;
15551
+ if (rx < minX)
15552
+ minX = rx;
15553
+ if (rx > maxX)
15554
+ maxX = rx;
15555
+ if (ry < minY)
15556
+ minY = ry;
15557
+ if (ry > maxY)
15558
+ maxY = ry;
15559
+ });
15560
+ });
15561
+ // Dimensions in world units (unrotated)
15562
+ const worldWidth = maxX - minX;
15563
+ const worldHeight = maxY - minY;
15564
+ this.width = (worldWidth - this.padding) * this.scale;
15565
+ this.height = (worldHeight - this.padding) * this.scale;
15566
+ // Center of the box in rotated space
15567
+ const cRx = (minX + maxX) / 2;
15568
+ const cRy = (minY + maxY) / 2;
15569
+ // Rotate center back to world space
15570
+ const cosR = Math.cos(rotation);
15571
+ const sinR = Math.sin(rotation);
15572
+ const cx = cRx * cosR - cRy * sinR;
15573
+ const cy = cRx * sinR + cRy * cosR;
15574
+ this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
15575
+ this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
15576
+ }
15577
+ this._core.store.state.objects.update(this);
15578
+ }
15579
+ getOffsetXToCenterFromSnapshot(snapshot) {
15580
+ const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
15581
+ const groupCenterX = this.translateX + this.totalWidth / this.scale / 2;
15582
+ return objCenterX - groupCenterX;
15583
+ }
15584
+ getOffsetYToCenterFromSnapshot(snapshot) {
15585
+ const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
15586
+ const groupCenterY = this.translateY + this.totalHeight / this.scale / 2;
15587
+ return objCenterY - groupCenterY;
14849
15588
  }
14850
15589
  }
14851
15590
 
14852
- class KritzelTextTool extends KritzelBaseTool {
14853
- fontFamily = 'Arial';
14854
- fontSize = 16;
14855
- fontColor = '#000000';
14856
- palette = [
14857
- '#000000',
14858
- '#FFFFFF',
14859
- '#FF0000',
14860
- '#00FF00',
14861
- '#0000FF',
14862
- '#FFFF00',
14863
- '#FF00FF',
14864
- '#00FFFF',
14865
- '#808080',
14866
- '#C0C0C0',
14867
- '#800000',
14868
- '#008000',
14869
- '#000080',
14870
- '#808000',
14871
- '#800080',
14872
- ];
15591
+ class KritzelLineTool extends KritzelBaseTool {
15592
+ color = '#000000';
15593
+ size = 4;
15594
+ palettes = ['#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF', '#808080', '#C0C0C0', '#800000', '#008000', '#000080', '#808000', '#800080'];
15595
+ /** Arrow head configuration for lines created with this tool */
15596
+ arrows;
15597
+ _startX = 0;
15598
+ _startY = 0;
14873
15599
  constructor(core) {
14874
15600
  super(core);
14875
15601
  }
@@ -14878,867 +15604,1040 @@ class KritzelTextTool extends KritzelBaseTool {
14878
15604
  event.preventDefault();
14879
15605
  }
14880
15606
  if (event.pointerType === 'mouse') {
14881
- const path = event.composedPath().slice(1);
14882
- const objectElement = path.find(element => element.classList && element.classList.contains('object'));
14883
- const object = this._core.findObjectById(objectElement?.id);
14884
- const activeText = this._core.store.activeText;
14885
- if (activeText === null && object instanceof KritzelText) {
14886
- object.edit(event);
14887
- return;
14888
- }
14889
- if (activeText !== null && object instanceof KritzelText) {
14890
- activeText.save();
14891
- object.edit(event);
14892
- return;
15607
+ if (KritzelEventHelper.isLeftClick(event)) {
15608
+ this._core.store.state.isDrawing = true;
15609
+ const x = event.clientX - this._core.store.offsetX;
15610
+ const y = event.clientY - this._core.store.offsetY;
15611
+ this._startX = x;
15612
+ this._startY = y;
15613
+ const line = KritzelLine.create(this._core, {
15614
+ startX: x,
15615
+ startY: y,
15616
+ endX: x,
15617
+ endY: y,
15618
+ translateX: -this._core.store.state.translateX,
15619
+ translateY: -this._core.store.state.translateY,
15620
+ scale: this._core.store.state.scale,
15621
+ stroke: this.color,
15622
+ strokeWidth: this.size,
15623
+ arrows: this.arrows,
15624
+ });
15625
+ line.isCompleted = false;
15626
+ this._core.store.state.objects.insert(line);
14893
15627
  }
14894
- if (activeText !== null && object instanceof KritzelText === false) {
14895
- this._core.resetActiveText();
14896
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
14897
- return;
15628
+ }
15629
+ if (event.pointerType === 'touch') {
15630
+ const activePointers = Array.from(this._core.store.state.pointers.values());
15631
+ if (activePointers.length === 1) {
15632
+ const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
15633
+ const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
15634
+ this._core.store.state.isDrawing = true;
15635
+ this._startX = x;
15636
+ this._startY = y;
15637
+ const line = KritzelLine.create(this._core, {
15638
+ startX: x,
15639
+ startY: y,
15640
+ endX: x,
15641
+ endY: y,
15642
+ translateX: -this._core.store.state.translateX,
15643
+ translateY: -this._core.store.state.translateY,
15644
+ scale: this._core.store.state.scale,
15645
+ stroke: this.color,
15646
+ strokeWidth: this.size,
15647
+ arrows: this.arrows,
15648
+ });
15649
+ line.isCompleted = false;
15650
+ this._core.store.state.objects.insert(line);
14898
15651
  }
14899
- if (KritzelEventHelper.isLeftClick(event) === false) {
14900
- return;
15652
+ }
15653
+ }
15654
+ handlePointerMove(event) {
15655
+ if (event.cancelable) {
15656
+ event.preventDefault();
15657
+ }
15658
+ if (event.pointerType === 'mouse') {
15659
+ if (this._core.store.state.isDrawing) {
15660
+ const currentLine = this._core.store.currentLine;
15661
+ if (currentLine) {
15662
+ const x = event.clientX - this._core.store.offsetX;
15663
+ const y = event.clientY - this._core.store.offsetY;
15664
+ const updatedLine = KritzelLine.create(this._core, {
15665
+ startX: this._startX,
15666
+ startY: this._startY,
15667
+ endX: x,
15668
+ endY: y,
15669
+ translateX: -this._core.store.state.translateX,
15670
+ translateY: -this._core.store.state.translateY,
15671
+ scale: this._core.store.state.scale,
15672
+ stroke: this.color,
15673
+ strokeWidth: this.size,
15674
+ arrows: this.arrows,
15675
+ });
15676
+ updatedLine.id = currentLine.id;
15677
+ updatedLine.workspaceId = currentLine.workspaceId;
15678
+ updatedLine.isCompleted = false;
15679
+ this._core.store.state.objects.update(updatedLine);
15680
+ }
14901
15681
  }
14902
- const clientX = event.clientX - this._core.store.offsetX;
14903
- const clientY = event.clientY - this._core.store.offsetY;
14904
- const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
14905
- text.fontColor = this.fontColor;
14906
- text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
14907
- text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
14908
- text.zIndex = this._core.store.currentZIndex;
14909
- this._core.store.state.objects.insert(text);
14910
- this._core.rerender();
14911
- text.edit(event);
14912
15682
  }
14913
15683
  if (event.pointerType === 'touch') {
14914
15684
  const activePointers = Array.from(this._core.store.state.pointers.values());
14915
- const path = event.composedPath().slice(1);
14916
- const objectElement = path.find(element => element.classList && element.classList.contains('object'));
14917
- const object = this._core.findObjectById(objectElement?.id);
14918
- const activeText = this._core.store.activeText;
14919
- if (activeText === null && object instanceof KritzelText) {
14920
- object.edit(event);
14921
- return;
14922
- }
14923
- if (activeText !== null && object instanceof KritzelText) {
14924
- activeText.save();
14925
- object.edit(event);
14926
- return;
14927
- }
14928
- if (activeText !== null && object instanceof KritzelText === false) {
14929
- this._core.resetActiveText();
14930
- this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
14931
- return;
14932
- }
14933
- if (activePointers.length > 1) {
14934
- return;
15685
+ if (activePointers.length === 1) {
15686
+ const currentLine = this._core.store.currentLine;
15687
+ if (currentLine) {
15688
+ const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
15689
+ const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
15690
+ const updatedLine = KritzelLine.create(this._core, {
15691
+ startX: this._startX,
15692
+ startY: this._startY,
15693
+ endX: x,
15694
+ endY: y,
15695
+ translateX: -this._core.store.state.translateX,
15696
+ translateY: -this._core.store.state.translateY,
15697
+ scale: this._core.store.state.scale,
15698
+ stroke: this.color,
15699
+ strokeWidth: this.size,
15700
+ arrows: this.arrows,
15701
+ });
15702
+ updatedLine.id = currentLine.id;
15703
+ updatedLine.workspaceId = currentLine.workspaceId;
15704
+ updatedLine.isCompleted = false;
15705
+ this._core.store.state.objects.update(updatedLine);
15706
+ }
14935
15707
  }
14936
- const clientX = Math.round(activePointers[0].clientX - this._core.store.offsetX);
14937
- const clientY = Math.round(activePointers[0].clientY - this._core.store.offsetY);
14938
- const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
14939
- text.fontColor = this.fontColor;
14940
- text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
14941
- text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
14942
- text.zIndex = this._core.store.currentZIndex;
14943
- this._core.store.state.objects.insert(text);
14944
- this._core.rerender();
14945
- text.edit(event);
14946
15708
  }
14947
15709
  }
14948
15710
  handlePointerUp(event) {
14949
15711
  if (event.cancelable) {
14950
15712
  event.preventDefault();
14951
15713
  }
14952
- this._core.store.activeText?.edit(event);
15714
+ if (event.pointerType === 'mouse') {
15715
+ if (this._core.store.state.isDrawing) {
15716
+ this._core.store.state.isDrawing = false;
15717
+ const currentLine = this._core.store.currentLine;
15718
+ if (currentLine) {
15719
+ currentLine.isCompleted = true;
15720
+ currentLine.zIndex = this._core.store.currentZIndex;
15721
+ this._core.store.state.objects.update(currentLine);
15722
+ this._core.engine.emitObjectsChange();
15723
+ // Switch to selection tool and select the drawn line
15724
+ this.selectLineAndSwitchTool(currentLine);
15725
+ }
15726
+ }
15727
+ }
15728
+ if (event.pointerType === 'touch') {
15729
+ if (this._core.store.state.isDrawing) {
15730
+ this._core.store.state.isDrawing = false;
15731
+ const currentLine = this._core.store.currentLine;
15732
+ if (currentLine) {
15733
+ currentLine.isCompleted = true;
15734
+ currentLine.zIndex = this._core.store.currentZIndex;
15735
+ this._core.store.state.objects.update(currentLine);
15736
+ this._core.engine.emitObjectsChange();
15737
+ // Switch to selection tool and select the drawn line
15738
+ this.selectLineAndSwitchTool(currentLine);
15739
+ }
15740
+ }
15741
+ }
14953
15742
  }
14954
- }
14955
-
14956
- class KritzelBaseHandler {
14957
- _core;
14958
- constructor(core) {
14959
- this._core = core;
15743
+ selectLineAndSwitchTool(line) {
15744
+ // Create a selection group with the drawn line
15745
+ const selectionGroup = KritzelSelectionGroup.create(this._core);
15746
+ selectionGroup.addOrRemove(line);
15747
+ selectionGroup.isSelected = true;
15748
+ selectionGroup.rotation = line.rotation;
15749
+ // Add the selection group
15750
+ this._core.addSelectionGroup(selectionGroup);
15751
+ // Switch to selection tool
15752
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
15753
+ // Rerender
15754
+ this._core.rerender();
14960
15755
  }
14961
15756
  }
14962
15757
 
14963
- class KritzelMoveHandler extends KritzelBaseHandler {
14964
- dragStartX;
14965
- dragStartY;
14966
- startX;
14967
- startY;
14968
- endX;
14969
- endY;
14970
- hasMoved = false;
14971
- trackedPointerId = null;
15758
+ class KritzelEraserTool extends KritzelBaseTool {
15759
+ touchStartTimeout = null;
14972
15760
  constructor(core) {
14973
15761
  super(core);
14974
15762
  }
14975
- reset() {
14976
- this.dragStartX = 0;
14977
- this.dragStartY = 0;
14978
- this.startX = 0;
14979
- this.startY = 0;
14980
- this.endX = 0;
14981
- this.endY = 0;
14982
- this.hasMoved = false;
14983
- this.trackedPointerId = null;
14984
- }
14985
- cancelPendingDrag() {
14986
- this._core.store.state.isDragging = false;
14987
- this.reset();
14988
- }
14989
15763
  handlePointerDown(event) {
14990
15764
  if (event.pointerType === 'mouse') {
14991
15765
  if (KritzelEventHelper.isLeftClick(event)) {
14992
- const selectionGroup = this._core.store.selectionGroup;
14993
- if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected) {
14994
- const clientX = event.clientX - this._core.store.offsetX;
14995
- const clientY = event.clientY - this._core.store.offsetY;
14996
- this._core.store.state.isDragging = true;
14997
- this.dragStartX = clientX;
14998
- this.dragStartY = clientY;
14999
- this.startX = this.dragStartX;
15000
- this.startY = this.dragStartY;
15001
- this.trackedPointerId = event.pointerId;
15002
- }
15003
- else {
15004
- this.trackedPointerId = null;
15005
- }
15006
- }
15007
- else {
15008
- this.trackedPointerId = null;
15766
+ this._core.store.state.isErasing = true;
15009
15767
  }
15010
15768
  }
15011
15769
  if (event.pointerType === 'touch') {
15012
- const activePointers = Array.from(this._core.store.state.pointers.values());
15013
- if (this._core.store.state.pointers.size === 1) {
15014
- if (this._core.store.state.isScaling) {
15015
- this.trackedPointerId = null;
15016
- return;
15017
- }
15018
- const selectionGroup = this._core.store.selectionGroup;
15019
- if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected) {
15020
- const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
15021
- const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
15022
- this.dragStartX = x;
15023
- this.dragStartY = y;
15024
- this.startX = x;
15025
- this.startY = y;
15026
- this.trackedPointerId = event.pointerId;
15027
- }
15028
- else {
15029
- this.trackedPointerId = null;
15770
+ this.touchStartTimeout = setTimeout(() => {
15771
+ if (this._core.store.state.pointers.size === 1 && !this._core.store.state.isScaling) {
15772
+ this._core.store.state.isErasing = true;
15030
15773
  }
15031
- }
15774
+ }, 80);
15032
15775
  }
15033
15776
  }
15034
15777
  handlePointerMove(event) {
15035
- if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
15036
- return;
15037
- }
15038
15778
  if (event.pointerType === 'mouse') {
15039
- const selectionGroup = this._core.store.selectionGroup;
15040
- if (this._core.store.state.isDragging && selectionGroup) {
15041
- const clientX = event.clientX - this._core.store.offsetX;
15042
- const clientY = event.clientY - this._core.store.offsetY;
15043
- this.endX = clientX;
15044
- this.endY = clientY;
15045
- const moveDeltaX = Math.abs(clientX - this.startX);
15046
- const moveDeltaY = Math.abs(clientY - this.startY);
15047
- const moveThreshold = 5;
15048
- if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
15049
- selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
15050
- this.dragStartX = clientX;
15051
- this.dragStartY = clientY;
15052
- this.hasMoved = true;
15053
- }
15779
+ if (this._core.store.state.isErasing) {
15780
+ const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
15781
+ if (selectedObjects.length === 0)
15782
+ return;
15783
+ const x = this._core.store.state.pointerX;
15784
+ const y = this._core.store.state.pointerY;
15785
+ selectedObjects.forEach(selectedObject => {
15786
+ selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
15787
+ });
15788
+ this._core.rerender();
15054
15789
  }
15055
15790
  }
15056
15791
  if (event.pointerType === 'touch') {
15057
- const activePointers = Array.from(this._core.store.state.pointers.values());
15058
- const selectionGroup = this._core.store.selectionGroup;
15059
- if (this._core.store.state.pointers.size === 1 &&
15060
- selectionGroup &&
15061
- !this._core.store.state.isResizeHandleSelected &&
15062
- !this._core.store.state.isRotationHandleSelected &&
15063
- !this._core.store.state.isScaling) {
15064
- const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
15065
- const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
15066
- this._core.store.state.isDragging = true;
15067
- this.endX = x;
15068
- this.endY = y;
15069
- const moveDeltaX = Math.abs(x - this.startX);
15070
- const moveDeltaY = Math.abs(y - this.startY);
15071
- const moveThreshold = 5;
15072
- if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
15073
- clearTimeout(this._core.store.state.longTouchTimeout);
15074
- selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
15075
- this.dragStartX = x;
15076
- this.dragStartY = y;
15077
- this.hasMoved = true;
15078
- }
15792
+ if (this._core.store.state.pointers.size === 1 && this._core.store.state.isErasing) {
15793
+ const shadowRoot = this._core.store.state.host?.shadowRoot;
15794
+ if (!shadowRoot)
15795
+ return;
15796
+ const selectedObjects = this._core.getObjectsFromPointerEvent(event, '.object');
15797
+ if (selectedObjects.length === 0)
15798
+ return;
15799
+ const x = this._core.store.state.pointerX;
15800
+ const y = this._core.store.state.pointerY;
15801
+ selectedObjects.forEach(selectedObject => {
15802
+ selectedObject.markedForRemoval = selectedObject.hitTest(x, y);
15803
+ });
15804
+ this._core.rerender();
15079
15805
  }
15080
15806
  }
15081
15807
  }
15082
15808
  handlePointerUp(event) {
15083
- if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
15084
- return;
15085
- }
15086
15809
  if (event.pointerType === 'mouse') {
15087
- if (this._core.store.state.isDragging) {
15088
- this._core.store.state.isDragging = false;
15089
- if (this.hasMoved) {
15090
- this._core.store.selectionGroup.update();
15091
- this._core.engine.emitObjectsChange();
15092
- this._core.store.state.hasObjectsChanged = true;
15810
+ if (this._core.store.state.isErasing) {
15811
+ const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
15812
+ objectsToRemove.forEach(object => {
15813
+ object.markedForRemoval = false;
15814
+ this._core.removeObject(object);
15815
+ });
15816
+ if (objectsToRemove.length > 0) {
15817
+ this._core.rerender();
15093
15818
  }
15819
+ this._core.store.state.isErasing = false;
15820
+ this._core.engine.emitObjectsChange();
15094
15821
  }
15095
15822
  }
15096
15823
  if (event.pointerType === 'touch') {
15097
- if (this._core.store.state.isDragging) {
15098
- this._core.store.state.isDragging = false;
15099
- if (this.hasMoved) {
15100
- this._core.store.selectionGroup.update();
15101
- this._core.engine.emitObjectsChange();
15102
- this._core.store.state.hasObjectsChanged = true;
15824
+ clearTimeout(this.touchStartTimeout);
15825
+ if (this._core.store.state.isErasing) {
15826
+ const objectsToRemove = this._core.store.allObjects.filter(object => object.markedForRemoval);
15827
+ objectsToRemove.forEach(object => {
15828
+ object.markedForRemoval = false;
15829
+ this._core.removeObject(object);
15830
+ });
15831
+ if (objectsToRemove.length > 0) {
15832
+ this._core.rerender();
15103
15833
  }
15834
+ this._core.store.state.isErasing = false;
15835
+ this._core.engine.emitObjectsChange();
15104
15836
  }
15105
15837
  }
15106
- this.reset();
15107
15838
  }
15108
15839
  }
15109
15840
 
15110
- var KritzelHandleType;
15111
- (function (KritzelHandleType) {
15112
- KritzelHandleType["TopLeft"] = "top-left";
15113
- KritzelHandleType["TopRight"] = "top-right";
15114
- KritzelHandleType["BottomLeft"] = "bottom-left";
15115
- KritzelHandleType["BottomRight"] = "bottom-right";
15116
- })(KritzelHandleType || (KritzelHandleType = {}));
15841
+ /**
15842
+ * Browser Image Compression
15843
+ * v2.0.2
15844
+ * by Donald <donaldcwl@gmail.com>
15845
+ * https://github.com/Donaldcwl/browser-image-compression
15846
+ */
15117
15847
 
15118
- class KritzelResizeHandler extends KritzelBaseHandler {
15119
- initialMouseX = 0;
15120
- initialMouseY = 0;
15121
- initialSize = { x: 0, y: 0, width: 0, height: 0 };
15122
- newSize = { x: 0, y: 0, width: 0, height: 0 };
15123
- hasResized = false;
15848
+ function _mergeNamespaces$1(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(r){if("default"!==r&&!(r in e)){var i=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,i.get?i:{enumerable:true,get:function(){return t[r]}});}}));})),Object.freeze(e)}function copyExifWithoutOrientation(e,t){return new Promise((function(r,i){let o;return getApp1Segment(e).then((function(e){try{return o=e,r(new Blob([t.slice(0,2),o,t.slice(2)],{type:"image/jpeg"}))}catch(e){return i(e)}}),i)}))}const getApp1Segment=e=>new Promise(((t,r)=>{const i=new FileReader;i.addEventListener("load",(({target:{result:e}})=>{const i=new DataView(e);let o=0;if(65496!==i.getUint16(o))return r("not a valid JPEG");for(o+=2;;){const a=i.getUint16(o);if(65498===a)break;const s=i.getUint16(o+2);if(65505===a&&1165519206===i.getUint32(o+4)){const a=o+10;let f;switch(i.getUint16(a)){case 18761:f=true;break;case 19789:f=false;break;default:return r("TIFF header contains invalid endian")}if(42!==i.getUint16(a+2,f))return r("TIFF header contains invalid version");const l=i.getUint32(a+4,f),c=a+l+2+12*i.getUint16(a+l,f);for(let e=a+l+2;e<c;e+=12){if(274==i.getUint16(e,f)){if(3!==i.getUint16(e+2,f))return r("Orientation data type is invalid");if(1!==i.getUint32(e+4,f))return r("Orientation data count is invalid");i.setUint16(e+8,1,f);break}}return t(e.slice(o,o+2+s))}o+=2+s;}return t(new Blob)})),i.readAsArrayBuffer(e);}));var e={},t={get exports(){return e},set exports(t){e=t;}};!function(e){var r,i,UZIP={};t.exports=UZIP,UZIP.parse=function(e,t){for(var r=UZIP.bin.readUshort,i=UZIP.bin.readUint,o=0,a={},s=new Uint8Array(e),f=s.length-4;101010256!=i(s,f);)f--;o=f;o+=4;var l=r(s,o+=4);r(s,o+=2);var c=i(s,o+=2),u=i(s,o+=4);o+=4,o=u;for(var h=0;h<l;h++){i(s,o),o+=4,o+=4,o+=4,i(s,o+=4);c=i(s,o+=4);var d=i(s,o+=4),A=r(s,o+=4),g=r(s,o+2),p=r(s,o+4);o+=6;var m=i(s,o+=8);o+=4,o+=A+g+p,UZIP._readLocal(s,m,a,c,d,t);}return a},UZIP._readLocal=function(e,t,r,i,o,a){var s=UZIP.bin.readUshort,f=UZIP.bin.readUint;f(e,t),s(e,t+=4),s(e,t+=2);var l=s(e,t+=2);f(e,t+=2),f(e,t+=4),t+=4;var c=s(e,t+=8),u=s(e,t+=2);t+=2;var h=UZIP.bin.readUTF8(e,t,c);if(t+=c,t+=u,a)r[h]={size:o,csize:i};else {var d=new Uint8Array(e.buffer,t);if(0==l)r[h]=new Uint8Array(d.buffer.slice(t,t+i));else {if(8!=l)throw "unknown compression method: "+l;var A=new Uint8Array(o);UZIP.inflateRaw(d,A),r[h]=A;}}},UZIP.inflateRaw=function(e,t){return UZIP.F.inflate(e,t)},UZIP.inflate=function(e,t){return UZIP.inflateRaw(new Uint8Array(e.buffer,e.byteOffset+2,e.length-6),t)},UZIP.deflate=function(e,t){null==t&&(t={level:6});var r=0,i=new Uint8Array(50+Math.floor(1.1*e.length));i[r]=120,i[r+1]=156,r+=2,r=UZIP.F.deflateRaw(e,i,r,t.level);var o=UZIP.adler(e,0,e.length);return i[r+0]=o>>>24&255,i[r+1]=o>>>16&255,i[r+2]=o>>>8&255,i[r+3]=o>>>0&255,new Uint8Array(i.buffer,0,r+4)},UZIP.deflateRaw=function(e,t){null==t&&(t={level:6});var r=new Uint8Array(50+Math.floor(1.1*e.length)),i=UZIP.F.deflateRaw(e,r,i,t.level);return new Uint8Array(r.buffer,0,i)},UZIP.encode=function(e,t){null==t&&(t=false);var r=0,i=UZIP.bin.writeUint,o=UZIP.bin.writeUshort,a={};for(var s in e){var f=!UZIP._noNeed(s)&&!t,l=e[s],c=UZIP.crc.crc(l,0,l.length);a[s]={cpr:f,usize:l.length,crc:c,file:f?UZIP.deflateRaw(l):l};}for(var s in a)r+=a[s].file.length+30+46+2*UZIP.bin.sizeUTF8(s);r+=22;var u=new Uint8Array(r),h=0,d=[];for(var s in a){var A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,0);}var g=0,p=h;for(var s in a){A=a[s];d.push(h),h=UZIP._writeHeader(u,h,s,A,1,d[g++]);}var m=h-p;return i(u,h,101010256),h+=4,o(u,h+=4,g),o(u,h+=2,g),i(u,h+=2,m),i(u,h+=4,p),h+=4,h+=2,u.buffer},UZIP._noNeed=function(e){var t=e.split(".").pop().toLowerCase();return -1!="png,jpg,jpeg,zip".indexOf(t)},UZIP._writeHeader=function(e,t,r,i,o,a){var s=UZIP.bin.writeUint,f=UZIP.bin.writeUshort,l=i.file;return s(e,t,0==o?67324752:33639248),t+=4,1==o&&(t+=2),f(e,t,20),f(e,t+=2,0),f(e,t+=2,i.cpr?8:0),s(e,t+=2,0),s(e,t+=4,i.crc),s(e,t+=4,l.length),s(e,t+=4,i.usize),f(e,t+=4,UZIP.bin.sizeUTF8(r)),f(e,t+=2,0),t+=2,1==o&&(t+=2,t+=2,s(e,t+=6,a),t+=4),t+=UZIP.bin.writeUTF8(e,t,r),0==o&&(e.set(l,t),t+=l.length),t},UZIP.crc={table:function(){for(var e=new Uint32Array(256),t=0;t<256;t++){for(var r=t,i=0;i<8;i++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update:function(e,t,r,i){for(var o=0;o<i;o++)e=UZIP.crc.table[255&(e^t[r+o])]^e>>>8;return e},crc:function(e,t,r){return 4294967295^UZIP.crc.update(4294967295,e,t,r)}},UZIP.adler=function(e,t,r){for(var i=1,o=0,a=t,s=t+r;a<s;){for(var f=Math.min(a+5552,s);a<f;)o+=i+=e[a++];i%=65521,o%=65521;}return o<<16|i},UZIP.bin={readUshort:function(e,t){return e[t]|e[t+1]<<8},writeUshort:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255;},readUint:function(e,t){return 16777216*e[t+3]+(e[t+2]<<16|e[t+1]<<8|e[t])},writeUint:function(e,t,r){e[t]=255&r,e[t+1]=r>>8&255,e[t+2]=r>>16&255,e[t+3]=r>>24&255;},readASCII:function(e,t,r){for(var i="",o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII:function(e,t,r){for(var i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},pad:function(e){return e.length<2?"0"+e:e},readUTF8:function(e,t,r){for(var i,o="",a=0;a<r;a++)o+="%"+UZIP.bin.pad(e[t+a].toString(16));try{i=decodeURIComponent(o);}catch(i){return UZIP.bin.readASCII(e,t,r)}return i},writeUTF8:function(e,t,r){for(var i=r.length,o=0,a=0;a<i;a++){var s=r.charCodeAt(a);if(0==(4294967168&s))e[t+o]=s,o++;else if(0==(4294965248&s))e[t+o]=192|s>>6,e[t+o+1]=128|s>>0&63,o+=2;else if(0==(4294901760&s))e[t+o]=224|s>>12,e[t+o+1]=128|s>>6&63,e[t+o+2]=128|s>>0&63,o+=3;else {if(0!=(4292870144&s))throw "e";e[t+o]=240|s>>18,e[t+o+1]=128|s>>12&63,e[t+o+2]=128|s>>6&63,e[t+o+3]=128|s>>0&63,o+=4;}}return o},sizeUTF8:function(e){for(var t=e.length,r=0,i=0;i<t;i++){var o=e.charCodeAt(i);if(0==(4294967168&o))r++;else if(0==(4294965248&o))r+=2;else if(0==(4294901760&o))r+=3;else {if(0!=(4292870144&o))throw "e";r+=4;}}return r}},UZIP.F={},UZIP.F.deflateRaw=function(e,t,r,i){var o=[[0,0,0,0,0],[4,4,8,4,0],[4,5,16,8,0],[4,6,16,16,0],[4,10,16,32,0],[8,16,32,32,0],[8,16,128,128,0],[8,32,128,256,0],[32,128,258,1024,1],[32,258,258,4096,1]][i],a=UZIP.F.U,s=UZIP.F._goodIndex;var f=UZIP.F._putsE,l=0,c=r<<3,u=0,h=e.length;if(0==i){for(;l<h;){f(t,c,l+(_=Math.min(65535,h-l))==h?1:0),c=UZIP.F._copyExact(e,l,_,t,c+8),l+=_;}return c>>>3}var d=a.lits,A=a.strt,g=a.prev,p=0,m=0,w=0,v=0,b=0,y=0;for(h>2&&(A[y=UZIP.F._hash(e,0)]=0),l=0;l<h;l++){if(b=y,l+1<h-2){y=UZIP.F._hash(e,l+1);var E=l+1&32767;g[E]=A[y],A[y]=E;}if(u<=l){(p>14e3||m>26697)&&h-l>100&&(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(l==h-1||u==h?1:0,d,p,v,e,w,l-w,t,c),p=m=v=0,w=l);var F=0;l<h-2&&(F=UZIP.F._bestMatch(e,l,g,b,Math.min(o[2],h-l),o[3]));var _=F>>>16,B=65535&F;if(0!=F){B=65535&F;var U=s(_=F>>>16,a.of0);a.lhst[257+U]++;var C=s(B,a.df0);a.dhst[C]++,v+=a.exb[U]+a.dxb[C],d[p]=_<<23|l-u,d[p+1]=B<<16|U<<8|C,p+=2,u=l+_;}else a.lhst[e[l]]++;m++;}}for(w==l&&0!=e.length||(u<l&&(d[p]=l-u,p+=2,u=l),c=UZIP.F._writeBlock(1,d,p,v,e,w,l-w,t,c),p=0,m=0,p=m=v=0,w=l);0!=(7&c);)c++;return c>>>3},UZIP.F._bestMatch=function(e,t,r,i,o,a){var s=32767&t,f=r[s],l=s-f+32768&32767;if(f==s||i!=UZIP.F._hash(e,t-l))return 0;for(var c=0,u=0,h=Math.min(32767,t);l<=h&&0!=--a&&f!=s;){if(0==c||e[t+c]==e[t+c-l]){var d=UZIP.F._howLong(e,t,l);if(d>c){if(u=l,(c=d)>=o)break;l+2<d&&(d=l+2);for(var A=0,g=0;g<d-2;g++){var p=t-l+g+32768&32767,m=p-r[p]+32768&32767;m>A&&(A=m,f=p);}}}l+=(s=f)-(f=r[s])+32768&32767;}return c<<16|u},UZIP.F._howLong=function(e,t,r){if(e[t]!=e[t-r]||e[t+1]!=e[t+1-r]||e[t+2]!=e[t+2-r])return 0;var i=t,o=Math.min(e.length,t+258);for(t+=3;t<o&&e[t]==e[t-r];)t++;return t-i},UZIP.F._hash=function(e,t){return (e[t]<<8|e[t+1])+(e[t+2]<<4)&65535},UZIP.saved=0,UZIP.F._writeBlock=function(e,t,r,i,o,a,s,f,l){var c,u,h,d,A,g,p,m,w,v=UZIP.F.U,b=UZIP.F._putsF,y=UZIP.F._putsE;v.lhst[256]++,u=(c=UZIP.F.getTrees())[0],h=c[1],d=c[2],A=c[3],g=c[4],p=c[5],m=c[6],w=c[7];var E=32+(0==(l+3&7)?0:8-(l+3&7))+(s<<3),F=i+UZIP.F.contSize(v.fltree,v.lhst)+UZIP.F.contSize(v.fdtree,v.dhst),_=i+UZIP.F.contSize(v.ltree,v.lhst)+UZIP.F.contSize(v.dtree,v.dhst);_+=14+3*p+UZIP.F.contSize(v.itree,v.ihst)+(2*v.ihst[16]+3*v.ihst[17]+7*v.ihst[18]);for(var B=0;B<286;B++)v.lhst[B]=0;for(B=0;B<30;B++)v.dhst[B]=0;for(B=0;B<19;B++)v.ihst[B]=0;var U=E<F&&E<_?0:F<_?1:2;if(b(f,l,e),b(f,l+1,U),l+=3,0==U){for(;0!=(7&l);)l++;l=UZIP.F._copyExact(o,a,s,f,l);}else {var C,I;if(1==U&&(C=v.fltree,I=v.fdtree),2==U){UZIP.F.makeCodes(v.ltree,u),UZIP.F.revCodes(v.ltree,u),UZIP.F.makeCodes(v.dtree,h),UZIP.F.revCodes(v.dtree,h),UZIP.F.makeCodes(v.itree,d),UZIP.F.revCodes(v.itree,d),C=v.ltree,I=v.dtree,y(f,l,A-257),y(f,l+=5,g-1),y(f,l+=5,p-4),l+=4;for(var Q=0;Q<p;Q++)y(f,l+3*Q,v.itree[1+(v.ordr[Q]<<1)]);l+=3*p,l=UZIP.F._codeTiny(m,v.itree,f,l),l=UZIP.F._codeTiny(w,v.itree,f,l);}for(var M=a,x=0;x<r;x+=2){for(var S=t[x],R=S>>>23,T=M+(8388607&S);M<T;)l=UZIP.F._writeLit(o[M++],C,f,l);if(0!=R){var O=t[x+1],P=O>>16,H=O>>8&255,L=255&O;y(f,l=UZIP.F._writeLit(257+H,C,f,l),R-v.of0[H]),l+=v.exb[H],b(f,l=UZIP.F._writeLit(L,I,f,l),P-v.df0[L]),l+=v.dxb[L],M+=R;}}l=UZIP.F._writeLit(256,C,f,l);}return l},UZIP.F._copyExact=function(e,t,r,i,o){var a=o>>>3;return i[a]=r,i[a+1]=r>>>8,i[a+2]=255-i[a],i[a+3]=255-i[a+1],a+=4,i.set(new Uint8Array(e.buffer,t,r),a),o+(r+4<<3)},UZIP.F.getTrees=function(){for(var e=UZIP.F.U,t=UZIP.F._hufTree(e.lhst,e.ltree,15),r=UZIP.F._hufTree(e.dhst,e.dtree,15),i=[],o=UZIP.F._lenCodes(e.ltree,i),a=[],s=UZIP.F._lenCodes(e.dtree,a),f=0;f<i.length;f+=2)e.ihst[i[f]]++;for(f=0;f<a.length;f+=2)e.ihst[a[f]]++;for(var l=UZIP.F._hufTree(e.ihst,e.itree,7),c=19;c>4&&0==e.itree[1+(e.ordr[c-1]<<1)];)c--;return [t,r,l,o,s,c,i,a]},UZIP.F.getSecond=function(e){for(var t=[],r=0;r<e.length;r+=2)t.push(e[r+1]);return t},UZIP.F.nonZero=function(e){for(var t="",r=0;r<e.length;r+=2)0!=e[r+1]&&(t+=(r>>1)+",");return t},UZIP.F.contSize=function(e,t){for(var r=0,i=0;i<t.length;i++)r+=t[i]*e[1+(i<<1)];return r},UZIP.F._codeTiny=function(e,t,r,i){for(var o=0;o<e.length;o+=2){var a=e[o],s=e[o+1];i=UZIP.F._writeLit(a,t,r,i);var f=16==a?2:17==a?3:7;a>15&&(UZIP.F._putsE(r,i,s,f),i+=f);}return i},UZIP.F._lenCodes=function(e,t){for(var r=e.length;2!=r&&0==e[r-1];)r-=2;for(var i=0;i<r;i+=2){var o=e[i+1],a=i+3<r?e[i+3]:-1,s=i+5<r?e[i+5]:-1,f=0==i?-1:e[i-1];if(0==o&&a==o&&s==o){for(var l=i+5;l+2<r&&e[l+2]==o;)l+=2;(c=Math.min(l+1-i>>>1,138))<11?t.push(17,c-3):t.push(18,c-11),i+=2*c-2;}else if(o==f&&a==o&&s==o){for(l=i+5;l+2<r&&e[l+2]==o;)l+=2;var c=Math.min(l+1-i>>>1,6);t.push(16,c-3),i+=2*c-2;}else t.push(o,0);}return r>>>1},UZIP.F._hufTree=function(e,t,r){var i=[],o=e.length,a=t.length,s=0;for(s=0;s<a;s+=2)t[s]=0,t[s+1]=0;for(s=0;s<o;s++)0!=e[s]&&i.push({lit:s,f:e[s]});var f=i.length,l=i.slice(0);if(0==f)return 0;if(1==f){var c=i[0].lit;l=0==c?1:0;return t[1+(c<<1)]=1,t[1+(l<<1)]=1,1}i.sort((function(e,t){return e.f-t.f}));var u=i[0],h=i[1],d=0,A=1,g=2;for(i[0]={lit:-1,f:u.f+h.f,l:u,r:h,d:0};A!=f-1;)u=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],h=d!=A&&(g==f||i[d].f<i[g].f)?i[d++]:i[g++],i[A++]={lit:-1,f:u.f+h.f,l:u,r:h};var p=UZIP.F.setDepth(i[A-1],0);for(p>r&&(UZIP.F.restrictDepth(l,r,p),p=r),s=0;s<f;s++)t[1+(l[s].lit<<1)]=l[s].d;return p},UZIP.F.setDepth=function(e,t){return -1!=e.lit?(e.d=t,t):Math.max(UZIP.F.setDepth(e.l,t+1),UZIP.F.setDepth(e.r,t+1))},UZIP.F.restrictDepth=function(e,t,r){var i=0,o=1<<r-t,a=0;for(e.sort((function(e,t){return t.d==e.d?e.f-t.f:t.d-e.d})),i=0;i<e.length&&e[i].d>t;i++){var s=e[i].d;e[i].d=t,a+=o-(1<<r-s);}for(a>>>=r-t;a>0;){(s=e[i].d)<t?(e[i].d++,a-=1<<t-s-1):i++;}for(;i>=0;i--)e[i].d==t&&a<0&&(e[i].d--,a++);0!=a&&console.log("debt left");},UZIP.F._goodIndex=function(e,t){var r=0;return t[16|r]<=e&&(r|=16),t[8|r]<=e&&(r|=8),t[4|r]<=e&&(r|=4),t[2|r]<=e&&(r|=2),t[1|r]<=e&&(r|=1),r},UZIP.F._writeLit=function(e,t,r,i){return UZIP.F._putsF(r,i,t[e<<1]),i+t[1+(e<<1)]},UZIP.F.inflate=function(e,t){var r=Uint8Array;if(3==e[0]&&0==e[1])return t||new r(0);var i=UZIP.F,o=i._bitsF,a=i._bitsE,s=i._decodeTiny,f=i.makeCodes,l=i.codes2map,c=i._get17,u=i.U,h=null==t;h&&(t=new r(e.length>>>2<<3));for(var d,A,g=0,p=0,m=0,w=0,v=0,b=0,y=0,E=0,F=0;0==g;)if(g=o(e,F,1),p=o(e,F+1,2),F+=3,0!=p){if(h&&(t=UZIP.F._check(t,E+(1<<17))),1==p&&(d=u.flmap,A=u.fdmap,b=511,y=31),2==p){m=a(e,F,5)+257,w=a(e,F+5,5)+1,v=a(e,F+10,4)+4,F+=14;for(var _=0;_<38;_+=2)u.itree[_]=0,u.itree[_+1]=0;var B=1;for(_=0;_<v;_++){var U=a(e,F+3*_,3);u.itree[1+(u.ordr[_]<<1)]=U,U>B&&(B=U);}F+=3*v,f(u.itree,B),l(u.itree,B,u.imap),d=u.lmap,A=u.dmap,F=s(u.imap,(1<<B)-1,m+w,e,F,u.ttree);var C=i._copyOut(u.ttree,0,m,u.ltree);b=(1<<C)-1;var I=i._copyOut(u.ttree,m,w,u.dtree);y=(1<<I)-1,f(u.ltree,C),l(u.ltree,C,d),f(u.dtree,I),l(u.dtree,I,A);}for(;;){var Q=d[c(e,F)&b];F+=15&Q;var M=Q>>>4;if(M>>>8==0)t[E++]=M;else {if(256==M)break;var x=E+M-254;if(M>264){var S=u.ldef[M-257];x=E+(S>>>3)+a(e,F,7&S),F+=7&S;}var R=A[c(e,F)&y];F+=15&R;var T=R>>>4,O=u.ddef[T],P=(O>>>4)+o(e,F,15&O);for(F+=15&O,h&&(t=UZIP.F._check(t,E+(1<<17)));E<x;)t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P],t[E]=t[E++-P];E=x;}}}else {0!=(7&F)&&(F+=8-(7&F));var H=4+(F>>>3),L=e[H-4]|e[H-3]<<8;h&&(t=UZIP.F._check(t,E+L)),t.set(new r(e.buffer,e.byteOffset+H,L),E),F=H+L<<3,E+=L;}return t.length==E?t:t.slice(0,E)},UZIP.F._check=function(e,t){var r=e.length;if(t<=r)return e;var i=new Uint8Array(Math.max(r<<1,t));return i.set(e,0),i},UZIP.F._decodeTiny=function(e,t,r,i,o,a){for(var s=UZIP.F._bitsE,f=UZIP.F._get17,l=0;l<r;){var c=e[f(i,o)&t];o+=15&c;var u=c>>>4;if(u<=15)a[l]=u,l++;else {var h=0,d=0;16==u?(d=3+s(i,o,2),o+=2,h=a[l-1]):17==u?(d=3+s(i,o,3),o+=3):18==u&&(d=11+s(i,o,7),o+=7);for(var A=l+d;l<A;)a[l]=h,l++;}}return o},UZIP.F._copyOut=function(e,t,r,i){for(var o=0,a=0,s=i.length>>>1;a<r;){var f=e[a+t];i[a<<1]=0,i[1+(a<<1)]=f,f>o&&(o=f),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},UZIP.F.makeCodes=function(e,t){for(var r,i,o,a,s=UZIP.F.U,f=e.length,l=s.bl_count,c=0;c<=t;c++)l[c]=0;for(c=1;c<f;c+=2)l[e[c]]++;var u=s.next_code;for(r=0,l[0]=0,i=1;i<=t;i++)r=r+l[i-1]<<1,u[i]=r;for(o=0;o<f;o+=2)0!=(a=e[o+1])&&(e[o]=u[a],u[a]++);},UZIP.F.codes2map=function(e,t,r){for(var i=e.length,o=UZIP.F.U.rev15,a=0;a<i;a+=2)if(0!=e[a+1])for(var s=a>>1,f=e[a+1],l=s<<4|f,c=t-f,u=e[a]<<c,h=u+(1<<c);u!=h;){r[o[u]>>>15-t]=l,u++;}},UZIP.F.revCodes=function(e,t){for(var r=UZIP.F.U.rev15,i=15-t,o=0;o<e.length;o+=2){var a=e[o]<<t-e[o+1];e[o]=r[a]>>>i;}},UZIP.F._putsE=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},UZIP.F._putsF=function(e,t,r){r<<=7&t;var i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},UZIP.F._bitsE=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},UZIP.F._bitsF=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},UZIP.F._get17=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},UZIP.F._get25=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},UZIP.F.U=(r=Uint16Array,i=Uint32Array,{next_code:new r(16),bl_count:new r(16),ordr:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],of0:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],exb:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],ldef:new r(32),df0:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],dxb:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],ddef:new i(32),flmap:new r(512),fltree:[],fdmap:new r(32),fdtree:[],lmap:new r(32768),ltree:[],ttree:[],dmap:new r(32768),dtree:[],imap:new r(512),itree:[],rev15:new r(32768),lhst:new i(286),dhst:new i(30),ihst:new i(19),lits:new i(15e3),strt:new r(65536),prev:new r(32768)}),function(){for(var e=UZIP.F.U,t=0;t<32768;t++){var r=t;r=(4278255360&(r=(4042322160&(r=(3435973836&(r=(2863311530&r)>>>1|(1431655765&r)<<1))>>>2|(858993459&r)<<2))>>>4|(252645135&r)<<4))>>>8|(16711935&r)<<8,e.rev15[t]=(r>>>16|r<<16)>>>17;}function pushV(e,t,r){for(;0!=t--;)e.push(0,r);}for(t=0;t<32;t++)e.ldef[t]=e.of0[t]<<3|e.exb[t],e.ddef[t]=e.df0[t]<<4|e.dxb[t];pushV(e.fltree,144,8),pushV(e.fltree,112,9),pushV(e.fltree,24,7),pushV(e.fltree,8,8),UZIP.F.makeCodes(e.fltree,9),UZIP.F.codes2map(e.fltree,9,e.flmap),UZIP.F.revCodes(e.fltree,9),pushV(e.fdtree,32,5),UZIP.F.makeCodes(e.fdtree,5),UZIP.F.codes2map(e.fdtree,5,e.fdmap),UZIP.F.revCodes(e.fdtree,5),pushV(e.itree,19,0),pushV(e.ltree,286,0),pushV(e.dtree,30,0),pushV(e.ttree,320,0);}();}();var UZIP=_mergeNamespaces$1({__proto__:null,default:e},[e]);const UPNG=function(){var e={nextZero(e,t){for(;0!=e[t];)t++;return t},readUshort:(e,t)=>e[t]<<8|e[t+1],writeUshort(e,t,r){e[t]=r>>8&255,e[t+1]=255&r;},readUint:(e,t)=>16777216*e[t]+(e[t+1]<<16|e[t+2]<<8|e[t+3]),writeUint(e,t,r){e[t]=r>>24&255,e[t+1]=r>>16&255,e[t+2]=r>>8&255,e[t+3]=255&r;},readASCII(e,t,r){let i="";for(let o=0;o<r;o++)i+=String.fromCharCode(e[t+o]);return i},writeASCII(e,t,r){for(let i=0;i<r.length;i++)e[t+i]=r.charCodeAt(i);},readBytes(e,t,r){const i=[];for(let o=0;o<r;o++)i.push(e[t+o]);return i},pad:e=>e.length<2?`0${e}`:e,readUTF8(t,r,i){let o,a="";for(let o=0;o<i;o++)a+=`%${e.pad(t[r+o].toString(16))}`;try{o=decodeURIComponent(a);}catch(o){return e.readASCII(t,r,i)}return o}};function decodeImage(t,r,i,o){const a=r*i,s=_getBPP(o),f=Math.ceil(r*s/8),l=new Uint8Array(4*a),c=new Uint32Array(l.buffer),{ctype:u}=o,{depth:h}=o,d=e.readUshort;if(6==u){const e=a<<2;if(8==h)for(var A=0;A<e;A+=4)l[A]=t[A],l[A+1]=t[A+1],l[A+2]=t[A+2],l[A+3]=t[A+3];if(16==h)for(A=0;A<e;A++)l[A]=t[A<<1];}else if(2==u){const e=o.tabs.tRNS;if(null==e){if(8==h)for(A=0;A<a;A++){var g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g];}if(16==h)for(A=0;A<a;A++){g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g];}}else {var p=e[0];const r=e[1],i=e[2];if(8==h)for(A=0;A<a;A++){var m=A<<2;g=3*A;c[A]=255<<24|t[g+2]<<16|t[g+1]<<8|t[g],t[g]==p&&t[g+1]==r&&t[g+2]==i&&(l[m+3]=0);}if(16==h)for(A=0;A<a;A++){m=A<<2,g=6*A;c[A]=255<<24|t[g+4]<<16|t[g+2]<<8|t[g],d(t,g)==p&&d(t,g+2)==r&&d(t,g+4)==i&&(l[m+3]=0);}}}else if(3==u){const e=o.tabs.PLTE,s=o.tabs.tRNS,c=s?s.length:0;if(1==h)for(var w=0;w<i;w++){var v=w*f,b=w*r;for(A=0;A<r;A++){m=b+A<<2;var y=3*(E=t[v+(A>>3)]>>7-((7&A)<<0)&1);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}if(2==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>2)]>>6-((3&A)<<1)&3);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(4==h)for(w=0;w<i;w++)for(v=w*f,b=w*r,A=0;A<r;A++){m=b+A<<2,y=3*(E=t[v+(A>>1)]>>4-((1&A)<<2)&15);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}if(8==h)for(A=0;A<a;A++){var E;m=A<<2,y=3*(E=t[A]);l[m]=e[y],l[m+1]=e[y+1],l[m+2]=e[y+2],l[m+3]=E<c?s[E]:255;}}else if(4==u){if(8==h)for(A=0;A<a;A++){m=A<<2;var F=t[_=A<<1];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+1];}if(16==h)for(A=0;A<a;A++){var _;m=A<<2,F=t[_=A<<2];l[m]=F,l[m+1]=F,l[m+2]=F,l[m+3]=t[_+2];}}else if(0==u)for(p=o.tabs.tRNS?o.tabs.tRNS:-1,w=0;w<i;w++){const e=w*f,i=w*r;if(1==h)for(var B=0;B<r;B++){var U=(F=255*(t[e+(B>>>3)]>>>7-(7&B)&1))==255*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(2==h)for(B=0;B<r;B++){U=(F=85*(t[e+(B>>>2)]>>>6-((3&B)<<1)&3))==85*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(4==h)for(B=0;B<r;B++){U=(F=17*(t[e+(B>>>1)]>>>4-((1&B)<<2)&15))==17*p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(8==h)for(B=0;B<r;B++){U=(F=t[e+B])==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}else if(16==h)for(B=0;B<r;B++){F=t[e+(B<<1)],U=d(t,e+(B<<1))==p?0:255;c[i+B]=U<<24|F<<16|F<<8|F;}}return l}function _decompress(e,r,i,o){const a=_getBPP(e),s=Math.ceil(i*a/8),f=new Uint8Array((s+1+e.interlace)*o);return r=e.tabs.CgBI?t(r,f):_inflate(r,f),0==e.interlace?r=_filterZero(r,e,0,i,o):1==e.interlace&&(r=function _readInterlace(e,t){const r=t.width,i=t.height,o=_getBPP(t),a=o>>3,s=Math.ceil(r*o/8),f=new Uint8Array(i*s);let l=0;const c=[0,0,4,0,2,0,1],u=[0,4,0,2,0,1,0],h=[8,8,8,4,4,2,2],d=[8,8,4,4,2,2,1];let A=0;for(;A<7;){const p=h[A],m=d[A];let w=0,v=0,b=c[A];for(;b<i;)b+=p,v++;let y=u[A];for(;y<r;)y+=m,w++;const E=Math.ceil(w*o/8);_filterZero(e,t,l,w,v);let F=0,_=c[A];for(;_<i;){let t=u[A],i=l+F*E<<3;for(;t<r;){var g;if(1==o)g=(g=e[i>>3])>>7-(7&i)&1,f[_*s+(t>>3)]|=g<<7-((7&t)<<0);if(2==o)g=(g=e[i>>3])>>6-(7&i)&3,f[_*s+(t>>2)]|=g<<6-((3&t)<<1);if(4==o)g=(g=e[i>>3])>>4-(7&i)&15,f[_*s+(t>>1)]|=g<<4-((1&t)<<2);if(o>=8){const r=_*s+t*a;for(let t=0;t<a;t++)f[r+t]=e[(i>>3)+t];}i+=o,t+=m;}F++,_+=p;}w*v!=0&&(l+=v*(1+E)),A+=1;}return f}(r,e)),r}function _inflate(e,r){return t(new Uint8Array(e.buffer,2,e.length-6),r)}var t=function(){const e={H:{}};return e.H.N=function(t,r){const i=Uint8Array;let o,a,s=0,f=0,l=0,c=0,u=0,h=0,d=0,A=0,g=0;if(3==t[0]&&0==t[1])return r||new i(0);const p=e.H,m=p.b,w=p.e,v=p.R,b=p.n,y=p.A,E=p.Z,F=p.m,_=null==r;for(_&&(r=new i(t.length>>>2<<5));0==s;)if(s=m(t,g,1),f=m(t,g+1,2),g+=3,0!=f){if(_&&(r=e.H.W(r,A+(1<<17))),1==f&&(o=F.J,a=F.h,h=511,d=31),2==f){l=w(t,g,5)+257,c=w(t,g+5,5)+1,u=w(t,g+10,4)+4,g+=14;let e=1;for(var B=0;B<38;B+=2)F.Q[B]=0,F.Q[B+1]=0;for(B=0;B<u;B++){const r=w(t,g+3*B,3);F.Q[1+(F.X[B]<<1)]=r,r>e&&(e=r);}g+=3*u,b(F.Q,e),y(F.Q,e,F.u),o=F.w,a=F.d,g=v(F.u,(1<<e)-1,l+c,t,g,F.v);const r=p.V(F.v,0,l,F.C);h=(1<<r)-1;const i=p.V(F.v,l,c,F.D);d=(1<<i)-1,b(F.C,r),y(F.C,r,o),b(F.D,i),y(F.D,i,a);}for(;;){const e=o[E(t,g)&h];g+=15&e;const i=e>>>4;if(i>>>8==0)r[A++]=i;else {if(256==i)break;{let e=A+i-254;if(i>264){const r=F.q[i-257];e=A+(r>>>3)+w(t,g,7&r),g+=7&r;}const o=a[E(t,g)&d];g+=15&o;const s=o>>>4,f=F.c[s],l=(f>>>4)+m(t,g,15&f);for(g+=15&f;A<e;)r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l],r[A]=r[A++-l];A=e;}}}}else {0!=(7&g)&&(g+=8-(7&g));const o=4+(g>>>3),a=t[o-4]|t[o-3]<<8;_&&(r=e.H.W(r,A+a)),r.set(new i(t.buffer,t.byteOffset+o,a),A),g=o+a<<3,A+=a;}return r.length==A?r:r.slice(0,A)},e.H.W=function(e,t){const r=e.length;if(t<=r)return e;const i=new Uint8Array(r<<1);return i.set(e,0),i},e.H.R=function(t,r,i,o,a,s){const f=e.H.e,l=e.H.Z;let c=0;for(;c<i;){const e=t[l(o,a)&r];a+=15&e;const i=e>>>4;if(i<=15)s[c]=i,c++;else {let e=0,t=0;16==i?(t=3+f(o,a,2),a+=2,e=s[c-1]):17==i?(t=3+f(o,a,3),a+=3):18==i&&(t=11+f(o,a,7),a+=7);const r=c+t;for(;c<r;)s[c]=e,c++;}}return a},e.H.V=function(e,t,r,i){let o=0,a=0;const s=i.length>>>1;for(;a<r;){const r=e[a+t];i[a<<1]=0,i[1+(a<<1)]=r,r>o&&(o=r),a++;}for(;a<s;)i[a<<1]=0,i[1+(a<<1)]=0,a++;return o},e.H.n=function(t,r){const i=e.H.m,o=t.length;let a,s,f;let l;const c=i.j;for(var u=0;u<=r;u++)c[u]=0;for(u=1;u<o;u+=2)c[t[u]]++;const h=i.K;for(a=0,c[0]=0,s=1;s<=r;s++)a=a+c[s-1]<<1,h[s]=a;for(f=0;f<o;f+=2)l=t[f+1],0!=l&&(t[f]=h[l],h[l]++);},e.H.A=function(t,r,i){const o=t.length,a=e.H.m.r;for(let e=0;e<o;e+=2)if(0!=t[e+1]){const o=e>>1,s=t[e+1],f=o<<4|s,l=r-s;let c=t[e]<<l;const u=c+(1<<l);for(;c!=u;){i[a[c]>>>15-r]=f,c++;}}},e.H.l=function(t,r){const i=e.H.m.r,o=15-r;for(let e=0;e<t.length;e+=2){const a=t[e]<<r-t[e+1];t[e]=i[a]>>>o;}},e.H.M=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8;},e.H.I=function(e,t,r){r<<=7&t;const i=t>>>3;e[i]|=r,e[i+1]|=r>>>8,e[i+2]|=r>>>16;},e.H.e=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8)>>>(7&t)&(1<<r)-1},e.H.b=function(e,t,r){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)&(1<<r)-1},e.H.Z=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16)>>>(7&t)},e.H.i=function(e,t){return (e[t>>>3]|e[1+(t>>>3)]<<8|e[2+(t>>>3)]<<16|e[3+(t>>>3)]<<24)>>>(7&t)},e.H.m=function(){const e=Uint16Array,t=Uint32Array;return {K:new e(16),j:new e(16),X:[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],S:[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,999,999,999],T:[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0],q:new e(32),p:[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,65535,65535],z:[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0],c:new t(32),J:new e(512),_:[],h:new e(32),$:[],w:new e(32768),C:[],v:[],d:new e(32768),D:[],u:new e(512),Q:[],r:new e(32768),s:new t(286),Y:new t(30),a:new t(19),t:new t(15e3),k:new e(65536),g:new e(32768)}}(),function(){const t=e.H.m;for(var r=0;r<32768;r++){let e=r;e=(2863311530&e)>>>1|(1431655765&e)<<1,e=(3435973836&e)>>>2|(858993459&e)<<2,e=(4042322160&e)>>>4|(252645135&e)<<4,e=(4278255360&e)>>>8|(16711935&e)<<8,t.r[r]=(e>>>16|e<<16)>>>17;}function n(e,t,r){for(;0!=t--;)e.push(0,r);}for(r=0;r<32;r++)t.q[r]=t.S[r]<<3|t.T[r],t.c[r]=t.p[r]<<4|t.z[r];n(t._,144,8),n(t._,112,9),n(t._,24,7),n(t._,8,8),e.H.n(t._,9),e.H.A(t._,9,t.J),e.H.l(t._,9),n(t.$,32,5),e.H.n(t.$,5),e.H.A(t.$,5,t.h),e.H.l(t.$,5),n(t.Q,19,0),n(t.C,286,0),n(t.D,30,0),n(t.v,320,0);}(),e.H.N}();function _getBPP(e){return [1,null,3,1,2,null,4][e.ctype]*e.depth}function _filterZero(e,t,r,i,o){let a=_getBPP(t);const s=Math.ceil(i*a/8);let f,l;a=Math.ceil(a/8);let c=e[r],u=0;if(c>1&&(e[r]=[0,0,1][c-2]),3==c)for(u=a;u<s;u++)e[u+1]=e[u+1]+(e[u+1-a]>>>1)&255;for(let t=0;t<o;t++)if(f=r+t*s,l=f+t+1,c=e[l-1],u=0,0==c)for(;u<s;u++)e[f+u]=e[l+u];else if(1==c){for(;u<a;u++)e[f+u]=e[l+u];for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-a];}else if(2==c)for(;u<s;u++)e[f+u]=e[l+u]+e[f+u-s];else if(3==c){for(;u<a;u++)e[f+u]=e[l+u]+(e[f+u-s]>>>1);for(;u<s;u++)e[f+u]=e[l+u]+(e[f+u-s]+e[f+u-a]>>>1);}else {for(;u<a;u++)e[f+u]=e[l+u]+_paeth(0,e[f+u-s],0);for(;u<s;u++)e[f+u]=e[l+u]+_paeth(e[f+u-a],e[f+u-s],e[f+u-a-s]);}return e}function _paeth(e,t,r){const i=e+t-r,o=i-e,a=i-t,s=i-r;return o*o<=a*a&&o*o<=s*s?e:a*a<=s*s?t:r}function _IHDR(t,r,i){i.width=e.readUint(t,r),r+=4,i.height=e.readUint(t,r),r+=4,i.depth=t[r],r++,i.ctype=t[r],r++,i.compress=t[r],r++,i.filter=t[r],r++,i.interlace=t[r],r++;}function _copyTile(e,t,r,i,o,a,s,f,l){const c=Math.min(t,o),u=Math.min(r,a);let h=0,d=0;for(let r=0;r<u;r++)for(let a=0;a<c;a++)if(s>=0&&f>=0?(h=r*t+a<<2,d=(f+r)*o+s+a<<2):(h=(-f+r)*t-s+a<<2,d=r*o+a<<2),0==l)i[d]=e[h],i[d+1]=e[h+1],i[d+2]=e[h+2],i[d+3]=e[h+3];else if(1==l){var A=e[h+3]*(1/255),g=e[h]*A,p=e[h+1]*A,m=e[h+2]*A,w=i[d+3]*(1/255),v=i[d]*w,b=i[d+1]*w,y=i[d+2]*w;const t=1-A,r=A+w*t,o=0==r?0:1/r;i[d+3]=255*r,i[d+0]=(g+v*t)*o,i[d+1]=(p+b*t)*o,i[d+2]=(m+y*t)*o;}else if(2==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];A==w&&g==v&&p==b&&m==y?(i[d]=0,i[d+1]=0,i[d+2]=0,i[d+3]=0):(i[d]=g,i[d+1]=p,i[d+2]=m,i[d+3]=A);}else if(3==l){A=e[h+3],g=e[h],p=e[h+1],m=e[h+2],w=i[d+3],v=i[d],b=i[d+1],y=i[d+2];if(A==w&&g==v&&p==b&&m==y)continue;if(A<220&&w>20)return false}return true}return {decode:function decode(r){const i=new Uint8Array(r);let o=8;const a=e,s=a.readUshort,f=a.readUint,l={tabs:{},frames:[]},c=new Uint8Array(i.length);let u,h=0,d=0;const A=[137,80,78,71,13,10,26,10];for(var g=0;g<8;g++)if(i[g]!=A[g])throw "The input is not a PNG file!";for(;o<i.length;){const e=a.readUint(i,o);o+=4;const r=a.readASCII(i,o,4);if(o+=4,"IHDR"==r)_IHDR(i,o,l);else if("iCCP"==r){for(var p=o;0!=i[p];)p++;a.readASCII(i,o,p-o);const s=i.slice(p+2,o+e);let f=null;try{f=_inflate(s);}catch(e){f=t(s);}l.tabs[r]=f;}else if("CgBI"==r)l.tabs[r]=i.slice(o,o+4);else if("IDAT"==r){for(g=0;g<e;g++)c[h+g]=i[o+g];h+=e;}else if("acTL"==r)l.tabs[r]={num_frames:f(i,o),num_plays:f(i,o+4)},u=new Uint8Array(i.length);else if("fcTL"==r){if(0!=d)(E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height),d=0;const e={x:f(i,o+12),y:f(i,o+16),width:f(i,o+4),height:f(i,o+8)};let t=s(i,o+22);t=s(i,o+20)/(0==t?100:t);const r={rect:e,delay:Math.round(1e3*t),dispose:i[o+24],blend:i[o+25]};l.frames.push(r);}else if("fdAT"==r){for(g=0;g<e-4;g++)u[d+g]=i[o+g+4];d+=e-4;}else if("pHYs"==r)l.tabs[r]=[a.readUint(i,o),a.readUint(i,o+4),i[o+8]];else if("cHRM"==r){l.tabs[r]=[];for(g=0;g<8;g++)l.tabs[r].push(a.readUint(i,o+4*g));}else if("tEXt"==r||"zTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});var m=a.nextZero(i,o),w=a.readASCII(i,o,m-o),v=o+e-m-1;if("tEXt"==r)y=a.readASCII(i,m+1,v);else {var b=_inflate(i.slice(m+2,m+2+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("iTXt"==r){null==l.tabs[r]&&(l.tabs[r]={});m=0,p=o;m=a.nextZero(i,p);w=a.readASCII(i,p,m-p);const t=i[p=m+1];var y;p+=2,m=a.nextZero(i,p),a.readASCII(i,p,m-p),p=m+1,m=a.nextZero(i,p),a.readUTF8(i,p,m-p);v=e-((p=m+1)-o);if(0==t)y=a.readUTF8(i,p,v);else {b=_inflate(i.slice(p,p+v));y=a.readUTF8(b,0,b.length);}l.tabs[r][w]=y;}else if("PLTE"==r)l.tabs[r]=a.readBytes(i,o,e);else if("hIST"==r){const e=l.tabs.PLTE.length/3;l.tabs[r]=[];for(g=0;g<e;g++)l.tabs[r].push(s(i,o+2*g));}else if("tRNS"==r)3==l.ctype?l.tabs[r]=a.readBytes(i,o,e):0==l.ctype?l.tabs[r]=s(i,o):2==l.ctype&&(l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]);else if("gAMA"==r)l.tabs[r]=a.readUint(i,o)/1e5;else if("sRGB"==r)l.tabs[r]=i[o];else if("bKGD"==r)0==l.ctype||4==l.ctype?l.tabs[r]=[s(i,o)]:2==l.ctype||6==l.ctype?l.tabs[r]=[s(i,o),s(i,o+2),s(i,o+4)]:3==l.ctype&&(l.tabs[r]=i[o]);else if("IEND"==r)break;o+=e,a.readUint(i,o),o+=4;}var E;return 0!=d&&((E=l.frames[l.frames.length-1]).data=_decompress(l,u.slice(0,d),E.rect.width,E.rect.height)),l.data=_decompress(l,c,l.width,l.height),delete l.compress,delete l.interlace,delete l.filter,l},toRGBA8:function toRGBA8(e){const t=e.width,r=e.height;if(null==e.tabs.acTL)return [decodeImage(e.data,t,r,e).buffer];const i=[];null==e.frames[0].data&&(e.frames[0].data=e.data);const o=t*r*4,a=new Uint8Array(o),s=new Uint8Array(o),f=new Uint8Array(o);for(let c=0;c<e.frames.length;c++){const u=e.frames[c],h=u.rect.x,d=u.rect.y,A=u.rect.width,g=u.rect.height,p=decodeImage(u.data,A,g,e);if(0!=c)for(var l=0;l<o;l++)f[l]=a[l];if(0==u.blend?_copyTile(p,A,g,a,t,r,h,d,0):1==u.blend&&_copyTile(p,A,g,a,t,r,h,d,1),i.push(a.buffer.slice(0)),0==u.dispose);else if(1==u.dispose)_copyTile(s,A,g,a,t,r,h,d,0);else if(2==u.dispose)for(l=0;l<o;l++)a[l]=f[l];}return i},_paeth:_paeth,_copyTile:_copyTile,_bin:e}}();!function(){const{_copyTile:e}=UPNG,{_bin:t}=UPNG,r=UPNG._paeth;var i={table:function(){const e=new Uint32Array(256);for(let t=0;t<256;t++){let r=t;for(let e=0;e<8;e++)1&r?r=3988292384^r>>>1:r>>>=1;e[t]=r;}return e}(),update(e,t,r,o){for(let a=0;a<o;a++)e=i.table[255&(e^t[r+a])]^e>>>8;return e},crc:(e,t,r)=>4294967295^i.update(4294967295,e,t,r)};function addErr(e,t,r,i){t[r]+=e[0]*i>>4,t[r+1]+=e[1]*i>>4,t[r+2]+=e[2]*i>>4,t[r+3]+=e[3]*i>>4;}function N(e){return Math.max(0,Math.min(255,e))}function D(e,t){const r=e[0]-t[0],i=e[1]-t[1],o=e[2]-t[2],a=e[3]-t[3];return r*r+i*i+o*o+a*a}function dither(e,t,r,i,o,a,s){null==s&&(s=1);const f=i.length,l=[];for(var c=0;c<f;c++){const e=i[c];l.push([e>>>0&255,e>>>8&255,e>>>16&255,e>>>24&255]);}for(c=0;c<f;c++){let e=4294967295;for(var u=0,h=0;h<f;h++){var d=D(l[c],l[h]);h!=c&&d<e&&(e=d,u=h);}}const A=new Uint32Array(o.buffer),g=new Int16Array(t*r*4),p=[0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5];for(c=0;c<p.length;c++)p[c]=255*((p[c]+.5)/16-.5);for(let o=0;o<r;o++)for(let w=0;w<t;w++){var m;c=4*(o*t+w);if(2!=s)m=[N(e[c]+g[c]),N(e[c+1]+g[c+1]),N(e[c+2]+g[c+2]),N(e[c+3]+g[c+3])];else {d=p[4*(3&o)+(3&w)];m=[N(e[c]+d),N(e[c+1]+d),N(e[c+2]+d),N(e[c+3]+d)];}u=0;let v=16777215;for(h=0;h<f;h++){const e=D(m,l[h]);e<v&&(v=e,u=h);}const b=l[u],y=[m[0]-b[0],m[1]-b[1],m[2]-b[2],m[3]-b[3]];1==s&&(w!=t-1&&addErr(y,g,c+4,7),o!=r-1&&(0!=w&&addErr(y,g,c+4*t-4,3),addErr(y,g,c+4*t,5),w!=t-1&&addErr(y,g,c+4*t+4,1))),a[c>>2]=u,A[c>>2]=i[u];}}function _main(e,r,o,a,s){null==s&&(s={});const{crc:f}=i,l=t.writeUint,c=t.writeUshort,u=t.writeASCII;let h=8;const d=e.frames.length>1;let A,g=false,p=33+(d?20:0);if(null!=s.sRGB&&(p+=13),null!=s.pHYs&&(p+=21),null!=s.iCCP&&(A=pako.deflate(s.iCCP),p+=21+A.length+4),3==e.ctype){for(var m=e.plte.length,w=0;w<m;w++)e.plte[w]>>>24!=255&&(g=true);p+=8+3*m+4+(g?8+1*m+4:0);}for(var v=0;v<e.frames.length;v++){d&&(p+=38),p+=(F=e.frames[v]).cimg.length+12,0!=v&&(p+=4);}p+=12;const b=new Uint8Array(p),y=[137,80,78,71,13,10,26,10];for(w=0;w<8;w++)b[w]=y[w];if(l(b,h,13),h+=4,u(b,h,"IHDR"),h+=4,l(b,h,r),h+=4,l(b,h,o),h+=4,b[h]=e.depth,h++,b[h]=e.ctype,h++,b[h]=0,h++,b[h]=0,h++,b[h]=0,h++,l(b,h,f(b,h-17,17)),h+=4,null!=s.sRGB&&(l(b,h,1),h+=4,u(b,h,"sRGB"),h+=4,b[h]=s.sRGB,h++,l(b,h,f(b,h-5,5)),h+=4),null!=s.iCCP){const e=13+A.length;l(b,h,e),h+=4,u(b,h,"iCCP"),h+=4,u(b,h,"ICC profile"),h+=11,h+=2,b.set(A,h),h+=A.length,l(b,h,f(b,h-(e+4),e+4)),h+=4;}if(null!=s.pHYs&&(l(b,h,9),h+=4,u(b,h,"pHYs"),h+=4,l(b,h,s.pHYs[0]),h+=4,l(b,h,s.pHYs[1]),h+=4,b[h]=s.pHYs[2],h++,l(b,h,f(b,h-13,13)),h+=4),d&&(l(b,h,8),h+=4,u(b,h,"acTL"),h+=4,l(b,h,e.frames.length),h+=4,l(b,h,null!=s.loop?s.loop:0),h+=4,l(b,h,f(b,h-12,12)),h+=4),3==e.ctype){l(b,h,3*(m=e.plte.length)),h+=4,u(b,h,"PLTE"),h+=4;for(w=0;w<m;w++){const t=3*w,r=e.plte[w],i=255&r,o=r>>>8&255,a=r>>>16&255;b[h+t+0]=i,b[h+t+1]=o,b[h+t+2]=a;}if(h+=3*m,l(b,h,f(b,h-3*m-4,3*m+4)),h+=4,g){l(b,h,m),h+=4,u(b,h,"tRNS"),h+=4;for(w=0;w<m;w++)b[h+w]=e.plte[w]>>>24&255;h+=m,l(b,h,f(b,h-m-4,m+4)),h+=4;}}let E=0;for(v=0;v<e.frames.length;v++){var F=e.frames[v];d&&(l(b,h,26),h+=4,u(b,h,"fcTL"),h+=4,l(b,h,E++),h+=4,l(b,h,F.rect.width),h+=4,l(b,h,F.rect.height),h+=4,l(b,h,F.rect.x),h+=4,l(b,h,F.rect.y),h+=4,c(b,h,a[v]),h+=2,c(b,h,1e3),h+=2,b[h]=F.dispose,h++,b[h]=F.blend,h++,l(b,h,f(b,h-30,30)),h+=4);const t=F.cimg;l(b,h,(m=t.length)+(0==v?0:4)),h+=4;const r=h;u(b,h,0==v?"IDAT":"fdAT"),h+=4,0!=v&&(l(b,h,E++),h+=4),b.set(t,h),h+=m,l(b,h,f(b,r,h-r)),h+=4;}return l(b,h,0),h+=4,u(b,h,"IEND"),h+=4,l(b,h,f(b,h-4,4)),h+=4,b.buffer}function compressPNG(e,t,r){for(let i=0;i<e.frames.length;i++){const o=e.frames[i];const a=o.rect.height,s=new Uint8Array(a*o.bpl+a);o.cimg=_filterZero(o.img,a,o.bpp,o.bpl,s,t,r);}}function compress(t,r,i,o,a){const s=a[0],f=a[1],l=a[2],c=a[3],u=a[4],h=a[5];let d=6,A=8,g=255;for(var p=0;p<t.length;p++){const e=new Uint8Array(t[p]);for(var m=e.length,w=0;w<m;w+=4)g&=e[w+3];}const v=255!=g,b=function framize(t,r,i,o,a,s){const f=[];for(var l=0;l<t.length;l++){const h=new Uint8Array(t[l]),A=new Uint32Array(h.buffer);var c;let g=0,p=0,m=r,w=i,v=o?1:0;if(0!=l){const b=s||o||1==l||0!=f[l-2].dispose?1:2;let y=0,E=1e9;for(let e=0;e<b;e++){var u=new Uint8Array(t[l-1-e]);const o=new Uint32Array(t[l-1-e]);let s=r,f=i,c=-1,h=-1;for(let e=0;e<i;e++)for(let t=0;t<r;t++){A[d=e*r+t]!=o[d]&&(t<s&&(s=t),t>c&&(c=t),e<f&&(f=e),e>h&&(h=e));} -1==c&&(s=f=c=h=0),a&&(1==(1&s)&&s--,1==(1&f)&&f--);const v=(c-s+1)*(h-f+1);v<E&&(E=v,y=e,g=s,p=f,m=c-s+1,w=h-f+1);}u=new Uint8Array(t[l-1-y]);1==y&&(f[l-1].dispose=2),c=new Uint8Array(m*w*4),e(u,r,i,c,m,w,-g,-p,0),v=e(h,r,i,c,m,w,-g,-p,3)?1:0,1==v?_prepareDiff(h,r,i,c,{x:g,y:p,width:m,height:w}):e(h,r,i,c,m,w,-g,-p,0);}else c=h.slice(0);f.push({rect:{x:g,y:p,width:m,height:w},img:c,blend:v,dispose:0});}if(o)for(l=0;l<f.length;l++){if(1==(A=f[l]).blend)continue;const e=A.rect,o=f[l-1].rect,s=Math.min(e.x,o.x),c=Math.min(e.y,o.y),u={x:s,y:c,width:Math.max(e.x+e.width,o.x+o.width)-s,height:Math.max(e.y+e.height,o.y+o.height)-c};f[l-1].dispose=1,l-1!=0&&_updateFrame(t,r,i,f,l-1,u,a),_updateFrame(t,r,i,f,l,u,a);}let h=0;if(1!=t.length)for(var d=0;d<f.length;d++){var A;h+=(A=f[d]).rect.width*A.rect.height;}return f}(t,r,i,s,f,l),y={},E=[],F=[];if(0!=o){const e=[];for(w=0;w<b.length;w++)e.push(b[w].img.buffer);const t=function concatRGBA(e){let t=0;for(var r=0;r<e.length;r++)t+=e[r].byteLength;const i=new Uint8Array(t);let o=0;for(r=0;r<e.length;r++){const t=new Uint8Array(e[r]),a=t.length;for(let e=0;e<a;e+=4){let r=t[e],a=t[e+1],s=t[e+2];const f=t[e+3];0==f&&(r=a=s=0),i[o+e]=r,i[o+e+1]=a,i[o+e+2]=s,i[o+e+3]=f;}o+=a;}return i.buffer}(e),r=quantize(t,o);for(w=0;w<r.plte.length;w++)E.push(r.plte[w].est.rgba);let i=0;for(w=0;w<b.length;w++){const e=(B=b[w]).img.length;var _=new Uint8Array(r.inds.buffer,i>>2,e>>2);F.push(_);const t=new Uint8Array(r.abuf,i,e);h&&dither(B.img,B.rect.width,B.rect.height,E,t,_),B.img.set(t),i+=e;}}else for(p=0;p<b.length;p++){var B=b[p];const e=new Uint32Array(B.img.buffer);var U=B.rect.width;m=e.length,_=new Uint8Array(m);F.push(_);for(w=0;w<m;w++){const t=e[w];if(0!=w&&t==e[w-1])_[w]=_[w-1];else if(w>U&&t==e[w-U])_[w]=_[w-U];else {let e=y[t];if(null==e&&(y[t]=e=E.length,E.push(t),E.length>=300))break;_[w]=e;}}}const C=E.length;C<=256&&0==u&&(A=C<=2?1:C<=4?2:C<=16?4:8,A=Math.max(A,c));for(p=0;p<b.length;p++){(B=b[p]).rect.x;U=B.rect.width;const e=B.rect.height;let t=B.img;let r=4*U,i=4;if(C<=256&&0==u){r=Math.ceil(A*U/8);var I=new Uint8Array(r*e);const o=F[p];for(let t=0;t<e;t++){w=t*r;const e=t*U;if(8==A)for(var Q=0;Q<U;Q++)I[w+Q]=o[e+Q];else if(4==A)for(Q=0;Q<U;Q++)I[w+(Q>>1)]|=o[e+Q]<<4-4*(1&Q);else if(2==A)for(Q=0;Q<U;Q++)I[w+(Q>>2)]|=o[e+Q]<<6-2*(3&Q);else if(1==A)for(Q=0;Q<U;Q++)I[w+(Q>>3)]|=o[e+Q]<<7-1*(7&Q);}t=I,d=3,i=1;}else if(0==v&&1==b.length){I=new Uint8Array(U*e*3);const o=U*e;for(w=0;w<o;w++){const e=3*w,r=4*w;I[e]=t[r],I[e+1]=t[r+1],I[e+2]=t[r+2];}t=I,d=2,i=3,r=3*U;}B.img=t,B.bpl=r,B.bpp=i;}return {ctype:d,depth:A,plte:E,frames:b}}function _updateFrame(t,r,i,o,a,s,f){const l=Uint8Array,c=Uint32Array,u=new l(t[a-1]),h=new c(t[a-1]),d=a+1<t.length?new l(t[a+1]):null,A=new l(t[a]),g=new c(A.buffer);let p=r,m=i,w=-1,v=-1;for(let e=0;e<s.height;e++)for(let t=0;t<s.width;t++){const i=s.x+t,f=s.y+e,l=f*r+i,c=g[l];0==c||0==o[a-1].dispose&&h[l]==c&&(null==d||0!=d[4*l+3])||(i<p&&(p=i),i>w&&(w=i),f<m&&(m=f),f>v&&(v=f));} -1==w&&(p=m=w=v=0),f&&(1==(1&p)&&p--,1==(1&m)&&m--),s={x:p,y:m,width:w-p+1,height:v-m+1};const b=o[a];b.rect=s,b.blend=1,b.img=new Uint8Array(s.width*s.height*4),0==o[a-1].dispose?(e(u,r,i,b.img,s.width,s.height,-s.x,-s.y,0),_prepareDiff(A,r,i,b.img,s)):e(A,r,i,b.img,s.width,s.height,-s.x,-s.y,0);}function _prepareDiff(t,r,i,o,a){e(t,r,i,o,a.width,a.height,-a.x,-a.y,2);}function _filterZero(e,t,r,i,o,a,s){const f=[];let l,c=[0,1,2,3,4];-1!=a?c=[a]:(t*i>5e5||1==r)&&(c=[0]),s&&(l={level:0});const u=UZIP;for(var h=0;h<c.length;h++){for(let a=0;a<t;a++)_filterLine(o,e,a,i,r,c[h]);f.push(u.deflate(o,l));}let d,A=1e9;for(h=0;h<f.length;h++)f[h].length<A&&(d=h,A=f[h].length);return f[d]}function _filterLine(e,t,i,o,a,s){const f=i*o;let l=f+i;if(e[l]=s,l++,0==s)if(o<500)for(var c=0;c<o;c++)e[l+c]=t[f+c];else e.set(new Uint8Array(t.buffer,f,o),l);else if(1==s){for(c=0;c<a;c++)e[l+c]=t[f+c];for(c=a;c<o;c++)e[l+c]=t[f+c]-t[f+c-a]+256&255;}else if(0==i){for(c=0;c<a;c++)e[l+c]=t[f+c];if(2==s)for(c=a;c<o;c++)e[l+c]=t[f+c];if(3==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-(t[f+c-a]>>1)+256&255;if(4==s)for(c=a;c<o;c++)e[l+c]=t[f+c]-r(t[f+c-a],0,0)+256&255;}else {if(2==s)for(c=0;c<o;c++)e[l+c]=t[f+c]+256-t[f+c-o]&255;if(3==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-(t[f+c-o]>>1)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-(t[f+c-o]+t[f+c-a]>>1)&255;}if(4==s){for(c=0;c<a;c++)e[l+c]=t[f+c]+256-r(0,t[f+c-o],0)&255;for(c=a;c<o;c++)e[l+c]=t[f+c]+256-r(t[f+c-a],t[f+c-o],t[f+c-a-o])&255;}}}function quantize(e,t){const r=new Uint8Array(e),i=r.slice(0),o=new Uint32Array(i.buffer),a=getKDtree(i,t),s=a[0],f=a[1],l=r.length,c=new Uint8Array(l>>2);let u;if(r.length<2e7)for(var h=0;h<l;h+=4){u=getNearest(s,d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255)),c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}else for(h=0;h<l;h+=4){var d=r[h]*(1/255),A=r[h+1]*(1/255),g=r[h+2]*(1/255),p=r[h+3]*(1/255);for(u=s;u.left;)u=planeDst(u.est,d,A,g,p)<=0?u.left:u.right;c[h>>2]=u.ind,o[h>>2]=u.est.rgba;}return {abuf:i.buffer,inds:c,plte:f}}function getKDtree(e,t,r){null==r&&(r=1e-4);const i=new Uint32Array(e.buffer),o={i0:0,i1:e.length,bst:null,est:null,tdst:0,left:null,right:null};o.bst=stats(e,o.i0,o.i1),o.est=estats(o.bst);const a=[o];for(;a.length<t;){let t=0,o=0;for(var s=0;s<a.length;s++)a[s].est.L>t&&(t=a[s].est.L,o=s);if(t<r)break;const f=a[o],l=splitPixels(e,i,f.i0,f.i1,f.est.e,f.est.eMq255);if(f.i0>=l||f.i1<=l){f.est.L=0;continue}const c={i0:f.i0,i1:l,bst:null,est:null,tdst:0,left:null,right:null};c.bst=stats(e,c.i0,c.i1),c.est=estats(c.bst);const u={i0:l,i1:f.i1,bst:null,est:null,tdst:0,left:null,right:null};u.bst={R:[],m:[],N:f.bst.N-c.bst.N};for(s=0;s<16;s++)u.bst.R[s]=f.bst.R[s]-c.bst.R[s];for(s=0;s<4;s++)u.bst.m[s]=f.bst.m[s]-c.bst.m[s];u.est=estats(u.bst),f.left=c,f.right=u,a[o]=c,a.push(u);}a.sort(((e,t)=>t.bst.N-e.bst.N));for(s=0;s<a.length;s++)a[s].ind=s;return [o,a]}function getNearest(e,t,r,i,o){if(null==e.left)return e.tdst=function dist(e,t,r,i,o){const a=t-e[0],s=r-e[1],f=i-e[2],l=o-e[3];return a*a+s*s+f*f+l*l}(e.est.q,t,r,i,o),e;const a=planeDst(e.est,t,r,i,o);let s=e.left,f=e.right;a>0&&(s=e.right,f=e.left);const l=getNearest(s,t,r,i,o);if(l.tdst<=a*a)return l;const c=getNearest(f,t,r,i,o);return c.tdst<l.tdst?c:l}function planeDst(e,t,r,i,o){const{e:a}=e;return a[0]*t+a[1]*r+a[2]*i+a[3]*o-e.eMq}function splitPixels(e,t,r,i,o,a){for(i-=4;r<i;){for(;vecDot(e,r,o)<=a;)r+=4;for(;vecDot(e,i,o)>a;)i-=4;if(r>=i)break;const s=t[r>>2];t[r>>2]=t[i>>2],t[i>>2]=s,r+=4,i-=4;}for(;vecDot(e,r,o)>a;)r-=4;return r+4}function vecDot(e,t,r){return e[t]*r[0]+e[t+1]*r[1]+e[t+2]*r[2]+e[t+3]*r[3]}function stats(e,t,r){const i=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],o=[0,0,0,0],a=r-t>>2;for(let a=t;a<r;a+=4){const t=e[a]*(1/255),r=e[a+1]*(1/255),s=e[a+2]*(1/255),f=e[a+3]*(1/255);o[0]+=t,o[1]+=r,o[2]+=s,o[3]+=f,i[0]+=t*t,i[1]+=t*r,i[2]+=t*s,i[3]+=t*f,i[5]+=r*r,i[6]+=r*s,i[7]+=r*f,i[10]+=s*s,i[11]+=s*f,i[15]+=f*f;}return i[4]=i[1],i[8]=i[2],i[9]=i[6],i[12]=i[3],i[13]=i[7],i[14]=i[11],{R:i,m:o,N:a}}function estats(e){const{R:t}=e,{m:r}=e,{N:i}=e,a=r[0],s=r[1],f=r[2],l=r[3],c=0==i?0:1/i,u=[t[0]-a*a*c,t[1]-a*s*c,t[2]-a*f*c,t[3]-a*l*c,t[4]-s*a*c,t[5]-s*s*c,t[6]-s*f*c,t[7]-s*l*c,t[8]-f*a*c,t[9]-f*s*c,t[10]-f*f*c,t[11]-f*l*c,t[12]-l*a*c,t[13]-l*s*c,t[14]-l*f*c,t[15]-l*l*c],h=u,d=o;let A=[Math.random(),Math.random(),Math.random(),Math.random()],g=0,p=0;if(0!=i)for(let e=0;e<16&&(A=d.multVec(h,A),p=Math.sqrt(d.dot(A,A)),A=d.sml(1/p,A),!(0!=e&&Math.abs(p-g)<1e-9));e++)g=p;const m=[a*c,s*c,f*c,l*c];return {Cov:u,q:m,e:A,L:g,eMq255:d.dot(d.sml(255,m),A),eMq:d.dot(A,m),rgba:(Math.round(255*m[3])<<24|Math.round(255*m[2])<<16|Math.round(255*m[1])<<8|Math.round(255*m[0])<<0)>>>0}}var o={multVec:(e,t)=>[e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],e[4]*t[0]+e[5]*t[1]+e[6]*t[2]+e[7]*t[3],e[8]*t[0]+e[9]*t[1]+e[10]*t[2]+e[11]*t[3],e[12]*t[0]+e[13]*t[1]+e[14]*t[2]+e[15]*t[3]],dot:(e,t)=>e[0]*t[0]+e[1]*t[1]+e[2]*t[2]+e[3]*t[3],sml:(e,t)=>[e*t[0],e*t[1],e*t[2],e*t[3]]};UPNG.encode=function encode(e,t,r,i,o,a,s){null==i&&(i=0),null==s&&(s=false);const f=compress(e,t,r,i,[false,false,false,0,s,false]);return compressPNG(f,-1),_main(f,t,r,o,a)},UPNG.encodeLL=function encodeLL(e,t,r,i,o,a,s,f){const l={ctype:0+(1==i?0:2)+(0==o?0:4),depth:a,frames:[]},c=(i+o)*a,u=c*t;for(let i=0;i<e.length;i++)l.frames.push({rect:{x:0,y:0,width:t,height:r},img:new Uint8Array(e[i]),blend:0,dispose:1,bpp:Math.ceil(c/8),bpl:Math.ceil(u/8)});return compressPNG(l,0,true),_main(l,t,r,s,f)},UPNG.encode.compress=compress,UPNG.encode.dither=dither,UPNG.quantize=quantize,UPNG.quantize.getKDtree=getKDtree,UPNG.quantize.getNearest=getNearest;}();const r={toArrayBuffer(e,t){const i=e.width,o=e.height,a=i<<2,s=e.getContext("2d").getImageData(0,0,i,o),f=new Uint32Array(s.data.buffer),l=(32*i+31)/32<<2,c=l*o,u=122+c,h=new ArrayBuffer(u),d=new DataView(h),A=1<<20;let g,p,m,w,v=A,b=0,y=0,E=0;function set16(e){d.setUint16(y,e,true),y+=2;}function set32(e){d.setUint32(y,e,true),y+=4;}function seek(e){y+=e;}set16(19778),set32(u),seek(4),set32(122),set32(108),set32(i),set32(-o>>>0),set16(1),set16(32),set32(3),set32(c),set32(2835),set32(2835),seek(8),set32(16711680),set32(65280),set32(255),set32(4278190080),set32(1466527264),function convert(){for(;b<o&&v>0;){for(w=122+b*l,g=0;g<a;)v--,p=f[E++],m=p>>>24,d.setUint32(w+g,p<<8|m),g+=4;b++;}E<f.length?(v=A,setTimeout(convert,r._dly)):t(h);}();},toBlob(e,t){this.toArrayBuffer(e,(e=>{t(new Blob([e],{type:"image/bmp"}));}));},_dly:9};var i={CHROME:"CHROME",FIREFOX:"FIREFOX",DESKTOP_SAFARI:"DESKTOP_SAFARI",IE:"IE",IOS:"IOS",ETC:"ETC"},o={[i.CHROME]:16384,[i.FIREFOX]:11180,[i.DESKTOP_SAFARI]:16384,[i.IE]:8192,[i.IOS]:4096,[i.ETC]:8192};const a="undefined"!=typeof window,s="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,f=a&&window.cordova&&window.cordova.require&&window.cordova.require("cordova/modulemapper"),CustomFile=(a||s)&&(f&&f.getOriginalSymbol(window,"File")||"undefined"!=typeof File&&File),CustomFileReader=(a||s)&&(f&&f.getOriginalSymbol(window,"FileReader")||"undefined"!=typeof FileReader&&FileReader);function getFilefromDataUrl(e,t,r=Date.now()){return new Promise((i=>{const o=e.split(","),a=o[0].match(/:(.*?);/)[1],s=globalThis.atob(o[1]);let f=s.length;const l=new Uint8Array(f);for(;f--;)l[f]=s.charCodeAt(f);const c=new Blob([l],{type:a});c.name=t,c.lastModified=r,i(c);}))}function getDataUrlFromFile(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=()=>t(i.result),i.onerror=e=>r(e),i.readAsDataURL(e);}))}function loadImage(e){return new Promise(((t,r)=>{const i=new Image;i.onload=()=>t(i),i.onerror=e=>r(e),i.src=e;}))}function getBrowserName(){if(void 0!==getBrowserName.cachedResult)return getBrowserName.cachedResult;let e=i.ETC;const{userAgent:t}=navigator;return /Chrom(e|ium)/i.test(t)?e=i.CHROME:/iP(ad|od|hone)/i.test(t)&&/WebKit/i.test(t)?e=i.IOS:/Safari/i.test(t)?e=i.DESKTOP_SAFARI:/Firefox/i.test(t)?e=i.FIREFOX:(/MSIE/i.test(t)||true==!!document.documentMode)&&(e=i.IE),getBrowserName.cachedResult=e,getBrowserName.cachedResult}function approximateBelowMaximumCanvasSizeOfBrowser(e,t){const r=getBrowserName(),i=o[r];let a=e,s=t,f=a*s;const l=a>s?s/a:a/s;for(;f>i*i;){const e=(i+a)/2,t=(i+s)/2;e<t?(s=t,a=t*l):(s=e*l,a=e),f=a*s;}return {width:a,height:s}}function getNewCanvasAndCtx(e,t){let r,i;try{if(r=new OffscreenCanvas(e,t),i=r.getContext("2d"),null===i)throw new Error("getContext of OffscreenCanvas returns null")}catch(e){r=document.createElement("canvas"),i=r.getContext("2d");}return r.width=e,r.height=t,[r,i]}function drawImageInCanvas(e,t){const{width:r,height:i}=approximateBelowMaximumCanvasSizeOfBrowser(e.width,e.height),[o,a]=getNewCanvasAndCtx(r,i);return t&&/jpe?g/.test(t)&&(a.fillStyle="white",a.fillRect(0,0,o.width,o.height)),a.drawImage(e,0,0,o.width,o.height),o}function isIOS(){return void 0!==isIOS.cachedResult||(isIOS.cachedResult=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].includes(navigator.platform)||navigator.userAgent.includes("Mac")&&"undefined"!=typeof document&&"ontouchend"in document),isIOS.cachedResult}function drawFileInCanvas(e,t={}){return new Promise((function(r,o){let a,s;var $Try_2_Post=function(){try{return s=drawImageInCanvas(a,t.fileType||e.type),r([a,s])}catch(e){return o(e)}},$Try_2_Catch=function(t){try{var $Try_3_Catch=function(e){try{throw e}catch(e){return o(e)}};try{let t;return getDataUrlFromFile(e).then((function(e){try{return t=e,loadImage(t).then((function(e){try{return a=e,function(){try{return $Try_2_Post()}catch(e){return o(e)}}()}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){return $Try_3_Catch(e)}}),$Try_3_Catch)}catch(e){$Try_3_Catch(e);}}catch(e){return o(e)}};try{if(isIOS()||[i.DESKTOP_SAFARI,i.MOBILE_SAFARI].includes(getBrowserName()))throw new Error("Skip createImageBitmap on IOS and Safari");return createImageBitmap(e).then((function(e){try{return a=e,$Try_2_Post()}catch(e){return $Try_2_Catch()}}),$Try_2_Catch)}catch(e){$Try_2_Catch();}}))}function canvasToFile(e,t,i,o,a=1){return new Promise((function(s,f){let l;if("image/png"===t){let c,u,h;return c=e.getContext("2d"),({data:u}=c.getImageData(0,0,e.width,e.height)),h=UPNG.encode([u.buffer],e.width,e.height,4096*a),l=new Blob([h],{type:t}),l.name=i,l.lastModified=o,$If_4.call(this)}{if("image/bmp"===t)return new Promise((t=>r.toBlob(e,t))).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_5.call(this)}catch(e){return f(e)}}.bind(this),f);{if("function"==typeof OffscreenCanvas&&e instanceof OffscreenCanvas)return e.convertToBlob({type:t,quality:a}).then(function(e){try{return l=e,l.name=i,l.lastModified=o,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f);{let d;return d=e.toDataURL(t,a),getFilefromDataUrl(d,i,o).then(function(e){try{return l=e,$If_6.call(this)}catch(e){return f(e)}}.bind(this),f)}function $If_6(){return $If_5.call(this)}}function $If_5(){return $If_4.call(this)}}function $If_4(){return s(l)}}))}function cleanupCanvasMemory(e){e.width=0,e.height=0;}function isAutoOrientationInBrowser(){return new Promise((function(e,t){let i,o,a,s;return void 0!==isAutoOrientationInBrowser.cachedResult?e(isAutoOrientationInBrowser.cachedResult):(getFilefromDataUrl("data:image/jpeg;base64,/9j/4QAiRXhpZgAATU0AKgAAAAgAAQESAAMAAAABAAYAAAAAAAD/2wCEAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/AABEIAAEAAgMBEQACEQEDEQH/xABKAAEAAAAAAAAAAAAAAAAAAAALEAEAAAAAAAAAAAAAAAAAAAAAAQEAAAAAAAAAAAAAAAAAAAAAEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwA/8H//2Q==","test.jpg",Date.now()).then((function(r){try{return i=r,drawFileInCanvas(i).then((function(r){try{return o=r[1],canvasToFile(o,i.type,i.name,i.lastModified).then((function(r){try{return a=r,cleanupCanvasMemory(o),drawFileInCanvas(a).then((function(r){try{return s=r[0],isAutoOrientationInBrowser.cachedResult=1===s.width&&2===s.height,e(isAutoOrientationInBrowser.cachedResult)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t)}catch(e){return t(e)}}),t))}))}function getExifOrientation(e){return new Promise(((t,r)=>{const i=new CustomFileReader;i.onload=e=>{const r=new DataView(e.target.result);if(65496!=r.getUint16(0,false))return t(-2);const i=r.byteLength;let o=2;for(;o<i;){if(r.getUint16(o+2,false)<=8)return t(-1);const e=r.getUint16(o,false);if(o+=2,65505==e){if(1165519206!=r.getUint32(o+=2,false))return t(-1);const e=18761==r.getUint16(o+=6,false);o+=r.getUint32(o+4,e);const i=r.getUint16(o,e);o+=2;for(let a=0;a<i;a++)if(274==r.getUint16(o+12*a,e))return t(r.getUint16(o+12*a+8,e))}else {if(65280!=(65280&e))break;o+=r.getUint16(o,false);}}return t(-1)},i.onerror=e=>r(e),i.readAsArrayBuffer(e);}))}function handleMaxWidthOrHeight(e,t){const{width:r}=e,{height:i}=e,{maxWidthOrHeight:o}=t;let a,s=e;return isFinite(o)&&(r>o||i>o)&&([s,a]=getNewCanvasAndCtx(r,i),r>i?(s.width=o,s.height=i/r*o):(s.width=r/i*o,s.height=o),a.drawImage(e,0,0,s.width,s.height),cleanupCanvasMemory(e)),s}function followExifOrientation(e,t){const{width:r}=e,{height:i}=e,[o,a]=getNewCanvasAndCtx(r,i);switch(t>4&&t<9?(o.width=i,o.height=r):(o.width=r,o.height=i),t){case 2:a.transform(-1,0,0,1,r,0);break;case 3:a.transform(-1,0,0,-1,r,i);break;case 4:a.transform(1,0,0,-1,0,i);break;case 5:a.transform(0,1,1,0,0,0);break;case 6:a.transform(0,1,-1,0,i,0);break;case 7:a.transform(0,-1,-1,0,i,r);break;case 8:a.transform(0,-1,1,0,0,r);}return a.drawImage(e,0,0,r,i),cleanupCanvasMemory(e),o}function compress(e,t,r=0){return new Promise((function(i,o){let a,s,f,l,c,u,h,d,A,g,p,m,w,v,b,y,E,F,_,B;function incProgress(e=5){if(t.signal&&t.signal.aborted)throw t.signal.reason;a+=e,t.onProgress(Math.min(a,100));}function setProgress(e){if(t.signal&&t.signal.aborted)throw t.signal.reason;a=Math.min(Math.max(e,a),100),t.onProgress(a);}return a=r,s=t.maxIteration||10,f=1024*t.maxSizeMB*1024,incProgress(),drawFileInCanvas(e,t).then(function(r){try{return [,l]=r,incProgress(),c=handleMaxWidthOrHeight(l,t),incProgress(),new Promise((function(r,i){var o;if(!(o=t.exifOrientation))return getExifOrientation(e).then(function(e){try{return o=e,$If_2.call(this)}catch(e){return i(e)}}.bind(this),i);function $If_2(){return r(o)}return $If_2.call(this)})).then(function(r){try{return u=r,incProgress(),isAutoOrientationInBrowser().then(function(r){try{return h=r?c:followExifOrientation(c,u),incProgress(),d=t.initialQuality||1,A=t.fileType||e.type,canvasToFile(h,A,e.name,e.lastModified,d).then(function(r){try{{if(g=r,incProgress(),p=g.size>f,m=g.size>e.size,!p&&!m)return setProgress(100),i(g);var a;function $Loop_3(){if(s--&&(b>f||b>w)){let t,r;return t=B?.95*_.width:_.width,r=B?.95*_.height:_.height,[E,F]=getNewCanvasAndCtx(t,r),F.drawImage(_,0,0,t,r),d*="image/png"===A?.85:.95,canvasToFile(E,A,e.name,e.lastModified,d).then((function(e){try{return y=e,cleanupCanvasMemory(_),_=E,b=y.size,setProgress(Math.min(99,Math.floor((v-b)/(v-f)*100))),$Loop_3}catch(e){return o(e)}}),o)}return [1]}return w=e.size,v=g.size,b=v,_=h,B=!t.alwaysKeepResolution&&p,(a=function(e){for(;e;){if(e.then)return void e.then(a,o);try{if(e.pop){if(e.length)return e.pop()?$Loop_3_exit.call(this):e;e=$Loop_3;}else e=e.call(this);}catch(e){return o(e)}}}.bind(this))($Loop_3);function $Loop_3_exit(){return cleanupCanvasMemory(_),cleanupCanvasMemory(E),cleanupCanvasMemory(c),cleanupCanvasMemory(h),cleanupCanvasMemory(l),setProgress(100),i(y)}}}catch(u){return o(u)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}catch(e){return o(e)}}.bind(this),o)}))}const l="\nlet scriptImported = false\nself.addEventListener('message', async (e) => {\n const { file, id, imageCompressionLibUrl, options } = e.data\n options.onProgress = (progress) => self.postMessage({ progress, id })\n try {\n if (!scriptImported) {\n // console.log('[worker] importScripts', imageCompressionLibUrl)\n self.importScripts(imageCompressionLibUrl)\n scriptImported = true\n }\n // console.log('[worker] self', self)\n const compressedFile = await imageCompression(file, options)\n self.postMessage({ file: compressedFile, id })\n } catch (e) {\n // console.error('[worker] error', e)\n self.postMessage({ error: e.message + '\\n' + e.stack, id })\n }\n})\n";let c;function compressOnWebWorker(e,t){return new Promise(((r,i)=>{c||(c=function createWorkerScriptURL(e){const t=[];return t.push(e),URL.createObjectURL(new Blob(t))}(l));const o=new Worker(c);o.addEventListener("message",(function handler(e){if(t.signal&&t.signal.aborted)o.terminate();else if(void 0===e.data.progress){if(e.data.error)return i(new Error(e.data.error)),void o.terminate();r(e.data.file),o.terminate();}else t.onProgress(e.data.progress);})),o.addEventListener("error",i),t.signal&&t.signal.addEventListener("abort",(()=>{i(t.signal.reason),o.terminate();})),o.postMessage({file:e,imageCompressionLibUrl:t.libURL,options:{...t,onProgress:void 0,signal:void 0}});}))}function imageCompression(e,t){return new Promise((function(r,i){let o,a,s,f,l,c;if(o={...t},s=0,({onProgress:f}=o),o.maxSizeMB=o.maxSizeMB||Number.POSITIVE_INFINITY,l="boolean"!=typeof o.useWebWorker||o.useWebWorker,delete o.useWebWorker,o.onProgress=e=>{s=e,"function"==typeof f&&f(s);},!(e instanceof Blob||e instanceof CustomFile))return i(new Error("The file given is not an instance of Blob or File"));if(!/^image/.test(e.type))return i(new Error("The file given is not an image"));if(c="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope,!l||"function"!=typeof Worker||c)return compress(e,o).then(function(e){try{return a=e,$If_4.call(this)}catch(e){return i(e)}}.bind(this),i);var u=function(){try{return $If_4.call(this)}catch(e){return i(e)}}.bind(this),$Try_1_Catch=function(t){try{return compress(e,o).then((function(e){try{return a=e,u()}catch(e){return i(e)}}),i)}catch(e){return i(e)}};try{return o.libURL=o.libURL||"https://cdn.jsdelivr.net/npm/browser-image-compression@2.0.2/dist/browser-image-compression.js",compressOnWebWorker(e,o).then((function(e){try{return a=e,u()}catch(e){return $Try_1_Catch()}}),$Try_1_Catch)}catch(e){$Try_1_Catch();}function $If_4(){try{a.name=e.name,a.lastModified=e.lastModified;}catch(e){}try{o.preserveExif&&"image/jpeg"===e.type&&(!o.fileType||o.fileType&&o.fileType===e.type)&&(a=copyExifWithoutOrientation(e,a));}catch(e){}return r(a)}}))}imageCompression.getDataUrlFromFile=getDataUrlFromFile,imageCompression.getFilefromDataUrl=getFilefromDataUrl,imageCompression.loadImage=loadImage,imageCompression.drawImageInCanvas=drawImageInCanvas,imageCompression.drawFileInCanvas=drawFileInCanvas,imageCompression.canvasToFile=canvasToFile,imageCompression.getExifOrientation=getExifOrientation,imageCompression.handleMaxWidthOrHeight=handleMaxWidthOrHeight,imageCompression.followExifOrientation=followExifOrientation,imageCompression.cleanupCanvasMemory=cleanupCanvasMemory,imageCompression.isAutoOrientationInBrowser=isAutoOrientationInBrowser,imageCompression.approximateBelowMaximumCanvasSizeOfBrowser=approximateBelowMaximumCanvasSizeOfBrowser,imageCompression.copyExifWithoutOrientation=copyExifWithoutOrientation,imageCompression.getBrowserName=getBrowserName,imageCompression.version="2.0.2";
15849
+
15850
+ class KritzelImageTool extends KritzelBaseTool {
15851
+ fileInput = null;
15852
+ maxCompressionSize = 300;
15124
15853
  constructor(core) {
15125
15854
  super(core);
15855
+ this.setupFileInput();
15126
15856
  }
15127
- reset() {
15128
- this.initialMouseX = 0;
15129
- this.initialMouseY = 0;
15130
- this.initialSize = { x: 0, y: 0, width: 0, height: 0 };
15131
- this.newSize = { x: 0, y: 0, width: 0, height: 0 };
15132
- this.hasResized = false;
15857
+ onActivate() {
15858
+ this.openFilePicker();
15133
15859
  }
15134
- handlePointerDown(event) {
15135
- if (event.pointerType === 'mouse') {
15136
- if (KritzelEventHelper.isLeftClick(event)) {
15137
- const selectionGroup = this._core.store.selectionGroup;
15138
- if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
15139
- const clientX = event.clientX - this._core.store.offsetX;
15140
- const clientY = event.clientY - this._core.store.offsetY;
15141
- this._core.store.state.isResizing = true;
15142
- this.initialMouseX = clientX;
15143
- this.initialMouseY = clientY;
15144
- this.initialSize.width = selectionGroup.width;
15145
- this.initialSize.height = selectionGroup.height;
15146
- this.initialSize.x = selectionGroup.translateX;
15147
- this.initialSize.y = selectionGroup.translateY;
15148
- }
15149
- }
15150
- }
15151
- if (event.pointerType === 'touch') {
15152
- const activePointers = Array.from(this._core.store.state.pointers.values());
15153
- const firstTouch = activePointers[0];
15154
- if (!firstTouch) {
15155
- return;
15156
- }
15157
- if (activePointers.length === 1) {
15158
- const selectionGroup = this._core.store.selectionGroup;
15159
- if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
15160
- const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
15161
- const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
15162
- this._core.store.state.isResizing = true;
15163
- this.initialMouseX = clientX;
15164
- this.initialMouseY = clientY;
15165
- this.initialSize.width = selectionGroup.width;
15166
- this.initialSize.height = selectionGroup.height;
15167
- this.initialSize.x = selectionGroup.translateX;
15168
- this.initialSize.y = selectionGroup.translateY;
15169
- clearTimeout(this._core.store.state.longTouchTimeout);
15170
- }
15171
- }
15860
+ openFilePicker() {
15861
+ if (this._core.store.isDisabled) {
15862
+ return;
15172
15863
  }
15864
+ this.fileInput.click();
15173
15865
  }
15174
- handlePointerMove(event) {
15175
- if (event.pointerType === 'mouse') {
15176
- const selectionGroup = this._core.store.selectionGroup;
15177
- if (this._core.store.state.isResizing && selectionGroup) {
15178
- const clientX = event.clientX - this._core.store.offsetX;
15179
- const clientY = event.clientY - this._core.store.offsetY;
15180
- const dx = clientX - this.initialMouseX;
15181
- const dy = clientY - this.initialMouseY;
15182
- const resizeDeltaX = Math.abs(dx);
15183
- const resizeDeltaY = Math.abs(dy);
15184
- const resizeThreshold = 5;
15185
- if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
15186
- this.hasResized = true;
15187
- }
15188
- if (!this.hasResized) {
15189
- return;
15190
- }
15191
- const rotation = selectionGroup.rotation;
15192
- const sin = Math.sin(rotation);
15193
- const cos = Math.cos(rotation);
15194
- const activeScale = selectionGroup.scale || this._core.store.state.scale;
15195
- // Calculate delta in local unrotated space
15196
- // We rotate the screen delta by -rotation to align with the object's axes
15197
- const localDx = dx * cos + dy * sin;
15198
- const localDy = -dx * sin + dy * cos;
15199
- // Calculate the center of the selection group before resize
15200
- const initialCenterX = this.initialSize.x + this.initialSize.width / activeScale / 2;
15201
- const initialCenterY = this.initialSize.y + this.initialSize.height / activeScale / 2;
15202
- // The center moves by half of the screen delta (scaled)
15203
- // This is true regardless of rotation because the resize happens symmetrically around the center
15204
- // relative to the fixed point logic
15205
- const newCenterX = initialCenterX + dx / activeScale / 2;
15206
- const newCenterY = initialCenterY + dy / activeScale / 2;
15207
- switch (this._core.store.state.resizeHandleType) {
15208
- case KritzelHandleType.TopLeft:
15209
- this.newSize.width = this.initialSize.width - localDx;
15210
- this.newSize.height = this.initialSize.height - localDy;
15211
- break;
15212
- case KritzelHandleType.TopRight:
15213
- this.newSize.width = this.initialSize.width + localDx;
15214
- this.newSize.height = this.initialSize.height - localDy;
15215
- break;
15216
- case KritzelHandleType.BottomLeft:
15217
- this.newSize.width = this.initialSize.width - localDx;
15218
- this.newSize.height = this.initialSize.height + localDy;
15219
- break;
15220
- case KritzelHandleType.BottomRight:
15221
- this.newSize.width = this.initialSize.width + localDx;
15222
- this.newSize.height = this.initialSize.height + localDy;
15223
- break;
15224
- }
15225
- this.newSize.x = newCenterX - this.newSize.width / activeScale / 2;
15226
- this.newSize.y = newCenterY - this.newSize.height / activeScale / 2;
15227
- selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
15228
- }
15229
- }
15230
- if (event.pointerType === 'touch') {
15231
- const activePointers = Array.from(this._core.store.state.pointers.values());
15232
- const oneFingerTouch = activePointers[0];
15233
- if (!oneFingerTouch) {
15234
- return;
15235
- }
15236
- const selectionGroup = this._core.store.selectionGroup;
15237
- if (this._core.store.state.isResizing && selectionGroup) {
15238
- const clientX = Math.round(oneFingerTouch.clientX - this._core.store.offsetX);
15239
- const clientY = Math.round(oneFingerTouch.clientY - this._core.store.offsetY);
15240
- const dx = clientX - this.initialMouseX;
15241
- const dy = clientY - this.initialMouseY;
15242
- const resizeDeltaX = Math.abs(dx);
15243
- const resizeDeltaY = Math.abs(dy);
15244
- const resizeThreshold = 5;
15245
- if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
15246
- clearTimeout(this._core.store.state.longTouchTimeout);
15247
- this.hasResized = true;
15248
- }
15249
- if (!this.hasResized) {
15250
- return;
15251
- }
15252
- const rotation = selectionGroup.rotation;
15253
- const sin = Math.sin(rotation);
15254
- const cos = Math.cos(rotation);
15255
- const activeScale = selectionGroup.scale || this._core.store.state.scale;
15256
- const localDx = dx * cos + dy * sin;
15257
- const localDy = -dx * sin + dy * cos;
15258
- const initialCenterX = this.initialSize.x + this.initialSize.width / activeScale / 2;
15259
- const initialCenterY = this.initialSize.y + this.initialSize.height / activeScale / 2;
15260
- const newCenterX = initialCenterX + dx / activeScale / 2;
15261
- const newCenterY = initialCenterY + dy / activeScale / 2;
15262
- switch (this._core.store.state.resizeHandleType) {
15263
- case KritzelHandleType.TopLeft:
15264
- this.newSize.width = this.initialSize.width - localDx;
15265
- this.newSize.height = this.initialSize.height - localDy;
15266
- break;
15267
- case KritzelHandleType.TopRight:
15268
- this.newSize.width = this.initialSize.width + localDx;
15269
- this.newSize.height = this.initialSize.height - localDy;
15270
- break;
15271
- case KritzelHandleType.BottomLeft:
15272
- this.newSize.width = this.initialSize.width - localDx;
15273
- this.newSize.height = this.initialSize.height + localDy;
15274
- break;
15275
- case KritzelHandleType.BottomRight:
15276
- this.newSize.width = this.initialSize.width + localDx;
15277
- this.newSize.height = this.initialSize.height + localDy;
15278
- break;
15279
- }
15280
- this.newSize.x = newCenterX - this.newSize.width / activeScale / 2;
15281
- this.newSize.y = newCenterY - this.newSize.height / activeScale / 2;
15282
- selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
15283
- }
15284
- }
15866
+ setupFileInput() {
15867
+ this.fileInput = document.createElement('input');
15868
+ this.fileInput.type = 'file';
15869
+ this.fileInput.accept = 'image/*';
15870
+ this.fileInput.style.display = 'none';
15871
+ this.fileInput.addEventListener('change', this.handleFileSelect.bind(this));
15872
+ this.fileInput.addEventListener('cancel', this.handleCancel.bind(this));
15873
+ document.body.appendChild(this.fileInput);
15285
15874
  }
15286
- handlePointerUp(event) {
15287
- if (event.pointerType === 'mouse') {
15288
- if (this._core.store.state.isResizing) {
15289
- this._core.store.state.isResizing = false;
15290
- if (this.hasResized) {
15291
- this._core.store.selectionGroup.update();
15292
- this._core.engine.emitObjectsChange();
15293
- this._core.store.state.hasObjectsChanged = true;
15294
- }
15295
- this.reset();
15296
- }
15875
+ handleFileSelect(event) {
15876
+ const input = event.target;
15877
+ if (input.files && input.files[0]) {
15878
+ const file = input.files[0];
15879
+ imageCompression(file, {
15880
+ maxWidthOrHeight: this.maxCompressionSize,
15881
+ })
15882
+ .then(compressedFile => {
15883
+ this.readFile(compressedFile);
15884
+ })
15885
+ .catch(error => {
15886
+ console.error('Error during image compression or processing:', error);
15887
+ this.handleCancel();
15888
+ });
15297
15889
  }
15298
- if (event.pointerType === 'touch') {
15299
- if (this._core.store.state.isResizing) {
15300
- this._core.store.state.isResizing = false;
15301
- if (this.hasResized) {
15302
- this._core.store.selectionGroup.update();
15303
- this._core.engine.emitObjectsChange();
15304
- this._core.store.state.hasObjectsChanged = true;
15305
- }
15306
- this.reset();
15307
- clearTimeout(this._core.store.state.longTouchTimeout);
15308
- }
15890
+ else {
15891
+ console.info('File selection cancelled by user.');
15892
+ this.handleCancel();
15893
+ }
15894
+ if (input) {
15895
+ input.value = '';
15309
15896
  }
15310
15897
  }
15898
+ readFile(file) {
15899
+ const reader = new FileReader();
15900
+ reader.onload = e => {
15901
+ const img = new Image();
15902
+ img.src = e.target?.result;
15903
+ img.onload = () => this.createKritzelImage(img);
15904
+ };
15905
+ reader.readAsDataURL(file);
15906
+ }
15907
+ createKritzelImage(img) {
15908
+ const image = KritzelImage.create(this._core);
15909
+ const { scaledWidth, scaledHeight } = image.calculateScaledDimensions(img);
15910
+ image.src = img.src;
15911
+ image.width = scaledWidth;
15912
+ image.height = scaledHeight;
15913
+ image.zIndex = this._core.store.currentZIndex;
15914
+ image.centerInViewport();
15915
+ this.addImageToStore(image);
15916
+ return image;
15917
+ }
15918
+ addImageToStore(image) {
15919
+ this._core.addObject(image);
15920
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
15921
+ this._core.selectObjects([image]);
15922
+ this._core.engine.emitObjectsChange();
15923
+ }
15924
+ handleCancel() {
15925
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
15926
+ }
15311
15927
  }
15312
15928
 
15313
- class KritzelRotationHandler extends KritzelBaseHandler {
15314
- initialRotation = 0;
15315
- rotation = 0;
15316
- unchangedObjects = [];
15317
- initialSelectionGroupRotation = 0;
15929
+ class KritzelTextTool extends KritzelBaseTool {
15930
+ fontFamily = 'Arial';
15931
+ fontSize = 16;
15932
+ fontColor = '#000000';
15933
+ palette = [
15934
+ '#000000',
15935
+ '#FFFFFF',
15936
+ '#FF0000',
15937
+ '#00FF00',
15938
+ '#0000FF',
15939
+ '#FFFF00',
15940
+ '#FF00FF',
15941
+ '#00FFFF',
15942
+ '#808080',
15943
+ '#C0C0C0',
15944
+ '#800000',
15945
+ '#008000',
15946
+ '#000080',
15947
+ '#808000',
15948
+ '#800080',
15949
+ ];
15318
15950
  constructor(core) {
15319
15951
  super(core);
15320
15952
  }
15321
- reset() {
15322
- this.initialRotation = 0;
15323
- this.rotation = 0;
15324
- this.unchangedObjects = [];
15325
- }
15326
15953
  handlePointerDown(event) {
15954
+ if (event.cancelable) {
15955
+ event.preventDefault();
15956
+ }
15327
15957
  if (event.pointerType === 'mouse') {
15328
- if (KritzelEventHelper.isLeftClick(event)) {
15329
- const selectionGroup = this._core.store.selectionGroup;
15330
- if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
15331
- const clientX = event.clientX - this._core.store.offsetX;
15332
- const clientY = event.clientY - this._core.store.offsetY;
15333
- this._core.store.state.isRotating = true;
15334
- const centerX = selectionGroup.translateX + selectionGroup.width / 2 / this._core.store.state.scale;
15335
- const centerY = selectionGroup.translateY + selectionGroup.height / 2 / this._core.store.state.scale;
15336
- const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
15337
- const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
15338
- this.initialSelectionGroupRotation = selectionGroup.rotation;
15339
- this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
15340
- this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
15341
- }
15958
+ const path = event.composedPath().slice(1);
15959
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
15960
+ const object = this._core.findObjectById(objectElement?.id);
15961
+ const activeText = this._core.store.activeText;
15962
+ if (activeText === null && object instanceof KritzelText) {
15963
+ object.edit(event);
15964
+ return;
15342
15965
  }
15343
- }
15966
+ if (activeText !== null && object instanceof KritzelText) {
15967
+ activeText.save();
15968
+ object.edit(event);
15969
+ return;
15970
+ }
15971
+ if (activeText !== null && object instanceof KritzelText === false) {
15972
+ this._core.resetActiveText();
15973
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
15974
+ return;
15975
+ }
15976
+ if (KritzelEventHelper.isLeftClick(event) === false) {
15977
+ return;
15978
+ }
15979
+ const clientX = event.clientX - this._core.store.offsetX;
15980
+ const clientY = event.clientY - this._core.store.offsetY;
15981
+ const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
15982
+ text.fontColor = this.fontColor;
15983
+ text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
15984
+ text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
15985
+ text.zIndex = this._core.store.currentZIndex;
15986
+ this._core.store.state.objects.insert(text);
15987
+ this._core.rerender();
15988
+ text.edit(event);
15989
+ }
15344
15990
  if (event.pointerType === 'touch') {
15345
15991
  const activePointers = Array.from(this._core.store.state.pointers.values());
15346
- const firstTouch = activePointers[0];
15347
- if (!firstTouch) {
15992
+ const path = event.composedPath().slice(1);
15993
+ const objectElement = path.find(element => element.classList && element.classList.contains('object'));
15994
+ const object = this._core.findObjectById(objectElement?.id);
15995
+ const activeText = this._core.store.activeText;
15996
+ if (activeText === null && object instanceof KritzelText) {
15997
+ object.edit(event);
15348
15998
  return;
15349
15999
  }
15350
- if (activePointers.length === 1) {
16000
+ if (activeText !== null && object instanceof KritzelText) {
16001
+ activeText.save();
16002
+ object.edit(event);
16003
+ return;
16004
+ }
16005
+ if (activeText !== null && object instanceof KritzelText === false) {
16006
+ this._core.resetActiveText();
16007
+ this._core.store.setState('activeTool', KritzelToolRegistry.getTool('selection'));
16008
+ return;
16009
+ }
16010
+ if (activePointers.length > 1) {
16011
+ return;
16012
+ }
16013
+ const clientX = Math.round(activePointers[0].clientX - this._core.store.offsetX);
16014
+ const clientY = Math.round(activePointers[0].clientY - this._core.store.offsetY);
16015
+ const text = KritzelText.create(this._core, this.fontSize, this.fontFamily);
16016
+ text.fontColor = this.fontColor;
16017
+ text.translateX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
16018
+ text.translateY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
16019
+ text.zIndex = this._core.store.currentZIndex;
16020
+ this._core.store.state.objects.insert(text);
16021
+ this._core.rerender();
16022
+ text.edit(event);
16023
+ }
16024
+ }
16025
+ handlePointerUp(event) {
16026
+ if (event.cancelable) {
16027
+ event.preventDefault();
16028
+ }
16029
+ this._core.store.activeText?.edit(event);
16030
+ }
16031
+ }
16032
+
16033
+ class KritzelIconRegistry {
16034
+ static registry = new Map();
16035
+ static register(name, svgContent) {
16036
+ if (this.registry.has(name)) {
16037
+ console.warn(`[IconRegistry] Icon "${name}" is already registered. It will be overwritten.`);
16038
+ }
16039
+ this.registry.set(name, svgContent);
16040
+ }
16041
+ static get(name) {
16042
+ return this.registry.get(name);
16043
+ }
16044
+ static registerIcons(icons) {
16045
+ for (const name in icons) {
16046
+ if (Object.prototype.hasOwnProperty.call(icons, name)) {
16047
+ this.register(name, icons[name]);
16048
+ }
16049
+ }
16050
+ }
16051
+ static has(name) {
16052
+ return this.registry.has(name);
16053
+ }
16054
+ }
16055
+ KritzelIconRegistry.registerIcons({
16056
+ 'cursor': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z"/></svg>',
16057
+ 'pen': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/></svg>',
16058
+ 'arrow': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="19" x2="19" y2="5"/><path d="M15 5h4v4"/></svg>',
16059
+ 'highlighter': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-highlighter-icon lucide-highlighter"><path d="m9 11-6 6v3h9l3-3"/><path d="m22 12-4.6 4.6a2 2 0 0 1-2.8 0l-5.2-5.2a2 2 0 0 1 0-2.8L14 4"/></svg>',
16060
+ 'eraser': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>',
16061
+ 'type': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="4 7 4 4 20 4 20 7"/><line x1="9" x2="15" y1="20" y2="20"/><line x1="12" x2="12" y1="4" y2="20"/></svg>',
16062
+ 'image': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" ry="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21"/></svg>',
16063
+ 'chevron-down': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>',
16064
+ 'chevron-up': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="m18 15-6-6-6 6"/></svg>',
16065
+ 'copy': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy-icon lucide-copy"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>',
16066
+ 'paste': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-clipboard-paste-icon lucide-clipboard-paste"><path d="M11 14h10"/><path d="M16 4h2a2 2 0 0 1 2 2v1.344"/><path d="m17 18 4-4-4-4"/><path d="M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 1.793-1.113"/><rect x="8" y="2" width="8" height="4" rx="1"/></svg>',
16067
+ 'cut': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-scissors-icon lucide-scissors"><circle cx="6" cy="6" r="3"/><path d="M8.12 8.12 12 12"/><path d="M20 4 8.12 15.88"/><circle cx="6" cy="18" r="3"/><path d="M14.8 14.8 20 20"/></svg>',
16068
+ 'delete': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash2-icon lucide-trash-2"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/><line x1="10" x2="10" y1="11" y2="17"/><line x1="14" x2="14" y1="11" y2="17"/></svg>',
16069
+ 'bring-to-front': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-up-to-line-icon lucide-arrow-up-to-line"><path d="M5 3h14"/><path d="m18 13-6-6-6 6"/><path d="M12 7v14"/></svg>',
16070
+ 'send-to-back': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-arrow-down-to-line-icon lucide-arrow-down-to-line"><path d="M12 17V3"/><path d="m6 11 6 6 6-6"/><path d="M19 21H5"/></svg>',
16071
+ 'select-all': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-square-mouse-pointer-icon lucide-square-mouse-pointer"><path d="M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"/><path d="M21 11V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h6"/></svg>',
16072
+ 'download': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-download-icon lucide-download"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" x2="12" y1="15" y2="3"/></svg>',
16073
+ 'undo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-undo-icon lucide-undo"><path d="M3 7v6h6"/><path d="M21 17a9 9 0 0 0-9-9 9 9 0 0 0-6 2.3L3 13"/></svg>',
16074
+ 'redo': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-redo-icon lucide-redo"><path d="M21 7v6h-6"/><path d="M3 17a9 9 0 0 1 9-9 9 9 0 0 1 6 2.3l3 2.7"/></svg>',
16075
+ 'plus': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>',
16076
+ 'ellipsis-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis-vertical-icon lucide-ellipsis-vertical"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>',
16077
+ 'x': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x-icon lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>',
16078
+ 'check': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check-icon lucide-check"><path d="M20 6 9 17l-5-5"/></svg>',
16079
+ 'move-vertical': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-move-vertical-icon lucide-move-vertical" version="1.1" id="svg3"> <defs id="defs3" /> <path d="M12 2v20" id="path1" /> <path style="fill:#ffffff;stroke-width:2.5;stroke-dasharray:none;stroke-linejoin:round;paint-order:stroke fill markers" d="m 11.735575,22.661865 c -0.09259,-0.02798 -0.204674,-0.07661 -0.249076,-0.108068 -0.04441,-0.03147 -1.167275,-0.979853 -2.4952713,-2.10755 -1.8557024,-1.57581 -2.4300904,-2.079639 -2.4817336,-2.17687 -0.086514,-0.162885 -0.089504,-0.422449 -0.00664,-0.576334 0.1483053,-0.275409 0.437667,-0.436207 0.7830634,-0.435147 0.3692925,0.0011 0.3517326,-0.01122 2.168748,1.525599 L 11.12348,20.194964 V 11.999996 3.8050256 L 9.4546663,5.2164943 C 7.6376509,6.7533118 7.6552109,6.7409594 7.2859184,6.7420935 6.6681409,6.7439906 6.253658,6.1955854 6.5159903,5.723396 6.5738626,5.6192278 7.1368766,5.1267427 9.0629381,3.4955044 11.738128,1.2298067 11.640395,1.3026868 12.00355,1.3026868 c 0.363154,0 0.265421,-0.07288 2.940611,2.1928176 1.926062,1.6312383 2.489076,2.1237234 2.546948,2.2278916 0.262332,0.4721894 -0.15215,1.0205946 -0.769928,1.0186975 -0.369293,-0.00114 -0.351733,0.011218 -2.168748,-1.5255992 L 12.88362,3.8050256 v 8.1949704 8.194968 l 1.668813,-1.411469 c 1.817015,-1.536817 1.799455,-1.524464 2.168748,-1.525599 0.617772,-0.0019 1.032269,0.546521 0.769928,1.018687 -0.103474,0.18623 -4.919006,4.273935 -5.130582,4.355136 -0.20796,0.07981 -0.425829,0.09033 -0.624952,0.03014 z" id="path4" /> </svg>',
16080
+ 'hand': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-icon lucide-hand" version="1.1" id="svg4"> <defs id="defs4" /> <path d="M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path1" /> <path d="M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8" id="path3" /> <path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" id="path4" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 11.478261,21.222477 C 9.6854059,21.139331 8.3341788,20.647346 7.1297169,19.639159 6.9698298,19.505327 5.949644,18.508708 4.8626374,17.42445 3.0780314,15.644357 2.8775851,15.435367 2.7968392,15.270602 2.5896561,14.847836 2.616354,14.436649 2.8771894,14.033085 c 0.136522,-0.211226 0.3837159,-0.398688 0.6367632,-0.482897 0.1529977,-0.05091 0.2326803,-0.05992 0.4470908,-0.05054 0.2250407,0.0098 0.2876577,0.02439 0.4554568,0.105827 0.1747854,0.08483 0.2933718,0.192702 1.1675186,1.062064 0.7182021,0.714271 1.0062041,0.982633 1.0998825,1.024878 0.1680197,0.07577 0.4756439,0.07817 0.6368269,0.005 0.1370772,-0.06226 0.2976691,-0.220818 0.3691296,-0.364463 0.072561,-0.145855 0.071573,-0.543545 -0.00169,-0.681911 -0.028747,-0.05429 -0.2476439,-0.296131 -0.4864385,-0.537426 l -0.4341716,-0.438718 0.00753,-3.989014 0.00753,-3.9890137 0.089246,-0.1883791 c 0.244787,-0.516692 0.7711718,-0.809716 1.3059705,-0.7269981 0.3973687,0.061462 0.7569953,0.3284904 0.9363122,0.6952277 l 0.093853,0.1919473 0.014101,2.4958872 0.014101,2.4958877 0.067385,0.149123 c 0.1186861,0.262654 0.4140438,0.457222 0.6940724,0.457222 0.2764172,0 0.5690532,-0.187563 0.6965992,-0.446482 l 0.06486,-0.131661 0.0141,-3.4970626 0.0141,-3.4970623 0.08982,-0.1896121 C 11.096301,3.0422103 11.506844,2.7755634 12,2.7755634 c 0.493156,0 0.903699,0.2666469 1.122868,0.7293016 l 0.08982,0.1896121 0.0141,3.2432432 c 0.01405,3.2315947 0.01432,3.2437077 0.07397,3.3726737 0.124721,0.269649 0.355908,0.424566 0.661411,0.443206 0.237954,0.01452 0.429018,-0.0627 0.591626,-0.239109 0.223655,-0.242637 0.208338,-0.06565 0.224113,-2.5896966 l 0.0141,-2.2561693 0.09385,-0.1919473 c 0.179317,-0.3667373 0.538944,-0.6337662 0.936313,-0.6952277 0.609359,-0.09425 1.208067,0.3054956 1.370981,0.9153772 0.03013,0.1127929 0.03773,0.6662436 0.038,2.7657391 3.74e-4,2.9328416 -0.008,2.8034316 0.197044,3.0364016 0.234927,0.266892 0.603828,0.337117 0.920407,0.175213 0.181933,-0.09304 0.329759,-0.261686 0.376309,-0.4293 0.01848,-0.06654 0.02929,-0.683932 0.0295,-1.684364 1.78e-4,-0.8783075 0.01239,-1.6530128 0.02751,-1.745346 0.08579,-0.5238478 0.505382,-0.9420803 1.039546,-1.0361716 0.607538,-0.1070155 1.25615,0.3485846 1.385876,0.973471 0.02211,0.1064847 0.02843,1.1397236 0.02169,3.5455556 -0.0093,3.324725 -0.01078,3.403075 -0.07062,3.770606 -0.126399,0.776213 -0.328814,1.41352 -0.669031,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.185731,1.21966 -2.604527,1.933174 -4.300822,2.162889 -0.38234,0.05178 -2.604621,0.0785 -3.412456,0.04104 z" id="path16" /> </svg>',
16081
+ 'hand-grab': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-hand-grab-icon lucide-hand-grab" version="1.1" id="svg5"> <defs id="defs5" /> <path d="M18 11.5V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1.4" id="path1" /> <path d="M14 10V8a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" id="path2" /> <path d="M10 9.9V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v5" id="path3" /> <path d="M6 14a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path4" /> <path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-4a8 8 0 0 1-8-8 2 2 0 1 1 4 0" id="path5" /> <path style="fill:#ffffff;stroke-width:0.0423032" d="M 9.5887192,21.221364 C 8.0526856,21.128375 6.6533474,20.587008 5.4571093,19.622953 5.3019976,19.497947 5.0101058,19.225967 4.8084606,19.018552 4.2300382,18.423579 3.8474097,17.879011 3.4960953,17.150756 3.1017715,16.333345 2.9014937,15.633198 2.8052391,14.735605 c -0.097577,-0.909922 -0.019834,-1.263323 0.3569794,-1.622742 0.753018,-0.718257 1.9463784,-0.256949 2.0619296,0.797066 0.037839,0.345154 0.089701,0.477674 0.2472582,0.631806 0.2464521,0.241095 0.5958008,0.287445 0.9036474,0.119894 0.1478567,-0.08047 0.2303871,-0.16866 0.3266699,-0.349056 0.050118,-0.0939 0.05337,-0.2326 0.066784,-2.848413 L 6.7826087,8.7144536 6.8458627,8.559342 C 6.9736603,8.2459558 7.2463773,7.9734987 7.5581669,7.8477164 c 0.1253804,-0.050581 0.2118709,-0.062705 0.4512338,-0.063254 0.275392,-6.312e-4 0.3094658,0.00564 0.4867271,0.089609 0.264676,0.1253746 0.4771417,0.334346 0.6070085,0.5970252 l 0.1048543,0.212087 0.017338,0.7488899 c 0.019395,0.8377165 0.026343,0.8713745 0.2242006,1.0860245 0.1591067,0.172611 0.3522021,0.249908 0.5883018,0.235502 0.305503,-0.01864 0.53669,-0.173557 0.661411,-0.443206 0.05734,-0.123963 0.0605,-0.181852 0.07486,-1.3703236 0.01626,-1.3456629 0.0141,-1.3267758 0.182667,-1.5938092 0.257891,-0.4085268 0.77099,-0.6442307 1.228943,-0.5645435 0.471012,0.08196 0.850379,0.4102836 0.989676,0.8565175 0.04663,0.149383 0.0514,0.2860903 0.0514,1.4721724 0,0.8344844 0.01108,1.3591794 0.03064,1.4503554 0.04109,0.191585 0.179844,0.390224 0.342505,0.490321 0.27721,0.170587 0.693425,0.126085 0.92529,-0.09893 0.228986,-0.222224 0.234357,-0.25046 0.251582,-1.3227071 l 0.0152,-0.9463484 0.104855,-0.2120434 c 0.129872,-0.2626357 0.342372,-0.471626 0.607008,-0.5969817 0.177262,-0.083967 0.211335,-0.09024 0.486727,-0.089609 0.239363,5.486e-4 0.325854,0.012673 0.451234,0.063254 0.31179,0.1257823 0.584507,0.3982394 0.712304,0.7116256 0.0621,0.1522783 0.06351,0.1824139 0.07736,1.649824 0.01408,1.492435 0.0142,1.494914 0.07947,1.627432 0.0771,0.156535 0.2216,0.291061 0.395058,0.367789 0.09695,0.04289 0.169789,0.05275 0.325757,0.04411 0.230544,-0.01277 0.363779,-0.06826 0.506411,-0.21089 0.177062,-0.177061 0.198302,-0.25307 0.219359,-0.784959 0.02002,-0.505773 0.05012,-0.6549 0.175297,-0.868492 0.498409,-0.850471 1.728484,-0.8041941 2.173478,0.08177 0.131751,0.262312 0.134447,0.313526 0.122615,2.328965 -0.0104,1.771638 -0.01517,1.923481 -0.0714,2.273746 -0.123689,0.770512 -0.327108,1.411813 -0.668163,2.106456 -0.36657,0.746612 -0.72118,1.250303 -1.297841,1.843464 -1.180268,1.214041 -2.600612,1.930754 -4.287083,2.163284 -0.28697,0.03957 -0.685446,0.04782 -2.636536,0.05459 -1.26416,0.0044 -2.4698005,-0.0024 -2.6792012,-0.01507 z" id="path6" /> </svg>',
16082
+ 'mouse-pointer': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-mouse-pointer2-icon lucide-mouse-pointer-2" version="1.1" id="svg1"> <defs id="defs1" /> <path d="M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z" id="path1" /> <path style="fill:#ffffff;stroke-width:0.0972651;stroke-linejoin:round;paint-order:stroke fill markers" d="M 7.8509196,12.314844 C 6.127071,8.1920568 4.7235325,4.8125504 4.7319449,4.8048299 c 0.01847,-0.016951 15.4787861,6.0801241 15.4610371,6.0973581 -0.0069,0.0067 -1.32633,0.341226 -2.93219,0.743509 -1.858829,0.465652 -3.011462,0.772855 -3.172161,0.845453 -0.688426,0.310999 -1.245788,0.879167 -1.498145,1.527185 -0.06251,0.160512 -0.440389,1.529576 -0.839733,3.042364 -0.399346,1.512788 -0.734966,2.750433 -0.745822,2.750322 -0.01086,-1.09e-4 -1.430163,-3.373391 -3.1540114,-7.496177 z" id="path2" /> <path style="fill:#ffffff;stroke-width:0.0705053;stroke-linejoin:round;paint-order:stroke fill markers" d="M 10.777831,19.240865 C 9.3899089,15.977493 4.7567738,4.8648478 4.7760705,4.8455511 c 0.013084,-0.013084 4.7618079,1.8507077 10.7773965,4.2299406 4.467495,1.7669453 4.558837,1.8037523 4.533848,1.8269883 -0.01183,0.011 -0.96064,0.257045 -2.108467,0.546768 -3.445281,0.869623 -3.777157,0.960825 -4.075205,1.119907 -0.597122,0.31871 -1.103666,0.864485 -1.309236,1.410635 -0.09642,0.256167 -0.312339,1.032806 -0.943226,3.392713 -0.641066,2.39798 -0.629171,2.354884 -0.649929,2.354849 -0.0091,-1.5e-5 -0.10963,-0.218934 -0.223421,-0.486487 z" id="path6" /> </svg>',
16083
+ 'pointer': '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-pointer-icon lucide-pointer" version="1.1" id="svg5"> <defs id="defs5" /> <path d="M22 14a8 8 0 0 1-8 8" id="path1" /> <path d="M18 11v-1a2 2 0 0 0-2-2a2 2 0 0 0-2 2" id="path2" /> <path d="M14 10V9a2 2 0 0 0-2-2a2 2 0 0 0-2 2v1" id="path3" /> <path d="M10 9.5V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v10" id="path4" /> <path d="M18 11a2 2 0 1 1 4 0v3a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15" id="path5" /> <path style="fill:#ffffff;stroke-width:0.0930233;stroke-linejoin:round;paint-order:stroke fill markers" d="M 11.2,21.203517 C 9.5092536,21.041493 8.4838422,20.667261 7.3116279,19.784423 7.1888372,19.691945 6.139155,18.670017 4.9790008,17.513473 2.5649891,15.106974 2.6562686,15.220427 2.6869266,14.664621 c 0.020277,-0.367608 0.1272548,-0.601802 0.3822766,-0.836873 0.2609241,-0.240511 0.4631523,-0.319555 0.8191689,-0.320185 0.509345,-9.02e-4 0.5815472,0.04845 1.7102825,1.168976 0.532833,0.528958 1.0223742,0.98871 1.0878697,1.021671 0.1699959,0.08555 0.4741793,0.07658 0.6733897,-0.01985 0.303161,-0.146758 0.4720469,-0.543298 0.3791811,-0.890307 -0.036373,-0.135913 -0.1250717,-0.246717 -0.506083,-0.632208 l -0.4619807,-0.467411 0.00983,-5.002356 0.00983,-5.0023561 0.080405,-0.1739784 C 6.9849846,3.2633288 7.2288893,3.0140593 7.4738043,2.8937849 7.6531903,2.8056908 7.7297209,2.7906977 8,2.7906977 c 0.2702791,0 0.3468097,0.014993 0.5261957,0.1030872 0.244915,0.1202744 0.4888197,0.3695439 0.6027014,0.6159576 0.079998,0.1730976 0.080499,0.1905559 0.09901,3.448397 0.020472,3.6031325 0.00418,3.3665775 0.2478407,3.5980265 0.2810043,0.266918 0.6822132,0.283308 1.0083162,0.04119 0.232339,-0.172501 0.25913,-0.280173 0.275933,-1.1089849 0.01456,-0.7180841 0.01869,-0.7520168 0.117805,-0.9674419 0.454637,-0.9881395 1.789759,-0.9881395 2.244396,0 0.09912,0.2154251 0.103248,0.2493578 0.117805,0.9674419 0.0168,0.8288119 0.04359,0.9364839 0.275933,1.1089849 0.326103,0.242117 0.727312,0.225727 1.008316,-0.04119 0.160426,-0.152384 0.218977,-0.294978 0.250819,-0.610832 C 14.845142,9.2502593 15.336916,8.7840275 16,8.7840275 c 0.478554,0 0.847992,0.2240614 1.082489,0.6565225 l 0.126813,0.2338686 0.01869,0.7813954 c 0.02069,0.865066 0.03258,0.913549 0.276595,1.127795 0.239244,0.210059 0.56665,0.239738 0.88018,0.07979 0.219744,-0.112104 0.324993,-0.301479 0.383515,-0.690054 0.06437,-0.427406 0.125794,-0.56694 0.357358,-0.811785 C 19.38604,9.8862229 19.614832,9.7882106 20,9.7869898 c 0.380153,-0.0012 0.636641,0.1145641 0.905451,0.4086842 0.318537,0.348531 0.317259,0.340596 0.332734,2.064791 0.02055,2.290001 -0.04576,2.983129 -0.383655,4.009983 -0.878739,2.670467 -3.284504,4.613238 -6.090273,4.918188 -0.322415,0.03504 -3.227312,0.04717 -3.564257,0.01488 z" id="path6" /> <path style="fill:#ffffff;stroke-width:0.0705053;stroke-linejoin:round;paint-order:stroke fill markers" d="m 11.942854,21.190957 c -1.015003,-0.01704 -1.131723,-0.02688 -1.806535,-0.1522 C 9.5352183,20.927122 8.8225664,20.681951 8.319624,20.413766 7.7570149,20.113766 7.3758537,19.827607 6.7836127,19.260598 5.774345,18.294328 3.1746828,15.68636 2.994305,15.459184 2.7288345,15.124838 2.6970674,15.049109 2.7026798,14.763988 c 0.00827,-0.420163 0.1162058,-0.679868 0.3844475,-0.925026 0.2382303,-0.217729 0.416008,-0.293073 0.7303102,-0.309513 0.5101451,-0.02668 0.6329728,0.04799 1.5378542,0.934977 0.8479274,0.831158 1.2429087,1.199656 1.342282,1.252283 0.11819,0.06259 0.4442817,0.06081 0.6122724,-0.0033 0.1482348,-0.05661 0.3410696,-0.248342 0.4107195,-0.408368 0.063024,-0.144802 0.06179,-0.498 -0.00216,-0.618376 C 7.6917315,14.63641 7.4736023,14.388937 7.2336742,14.136678 L 6.797441,13.678026 6.7970753,8.6903755 6.7967098,3.7027253 6.9021002,3.502167 C 7.0320375,3.2548956 7.2343175,3.0487286 7.4743519,2.9189189 c 0.1797064,-0.097185 0.1877434,-0.098707 0.5209477,-0.098707 0.3169743,0 0.349151,0.00524 0.5076381,0.082665 0.2190605,0.1070166 0.4871239,0.3748528 0.5931342,0.592632 l 0.081613,0.1676584 0.019992,1.3834579 c 0.010995,0.7609018 0.021403,2.0560783 0.023128,2.87817 0.00341,1.6233162 0.028863,2.2876938 0.093598,2.4426258 0.089047,0.213119 0.4255042,0.419889 0.683247,0.419889 0.1563141,0 0.3731941,-0.08265 0.5187231,-0.197689 0.205546,-0.162474 0.225694,-0.250142 0.256177,-1.1146856 C 10.79418,8.8613623 10.8052,8.7492253 10.85668,8.6186115 11.053264,8.1198873 11.50183,7.7978848 12,7.7978848 c 0.49817,0 0.946736,0.3220025 1.143315,0.8207259 0.05148,0.1306138 0.0625,0.2427508 0.08413,0.8563239 0.02897,0.8216044 0.05497,0.9507864 0.218548,1.0859664 0.341252,0.282005 0.756809,0.293493 1.057036,0.02922 0.169206,-0.148941 0.234219,-0.2921 0.288276,-0.6347773 0.103115,-0.6536616 0.472737,-1.0621772 1.030077,-1.1384664 0.547738,-0.074975 0.986339,0.1608863 1.276145,0.6862578 l 0.105758,0.1917223 7.68e-4,0.3699606 c 0.0011,0.553623 0.04553,1.179579 0.09141,1.289176 0.04428,0.105777 0.219161,0.276182 0.364423,0.355092 0.06913,0.03755 0.155512,0.05067 0.333059,0.05055 0.204058,-1.3e-4 0.260695,-0.01118 0.380729,-0.07426 0.239564,-0.12591 0.320531,-0.265583 0.407786,-0.703455 0.08382,-0.420648 0.125337,-0.527172 0.279883,-0.718174 0.321102,-0.3968443 0.77117,-0.5469022 1.259123,-0.4198065 0.21344,0.055594 0.39756,0.1739295 0.578279,0.3716645 0.286251,0.313205 0.299377,0.389392 0.324444,1.883099 0.02888,1.721047 -0.03379,2.808669 -0.20314,3.525265 -0.307591,1.301561 -0.970113,2.493177 -1.91312,3.440943 -1.060413,1.065767 -2.34115,1.742909 -3.849589,2.035329 -0.348478,0.06755 -0.462014,0.07524 -1.340342,0.09078 -0.527787,0.0093 -1.416154,0.0093 -1.974148,-6.4e-5 z" id="path7" /> </svg>'
16084
+ });
16085
+
16086
+ class KritzelCursorHelper {
16087
+ static _pointerCursor = null;
16088
+ /**
16089
+ * Returns the custom pointer cursor CSS value.
16090
+ * This can be used instead of `cursor: pointer` for consistent styling.
16091
+ */
16092
+ static getPointerCursor() {
16093
+ if (!this._pointerCursor) {
16094
+ this._pointerCursor = this.getCursor({ iconName: 'pointer' });
16095
+ }
16096
+ return this._pointerCursor;
16097
+ }
16098
+ /**
16099
+ * Returns a custom cursor CSS value with support for rotation.
16100
+ * The icon is retrieved from the KritzelIconRegistry.
16101
+ */
16102
+ static getCursor(options) {
16103
+ const iconName = options.iconName === 'default' ? 'mouse-pointer' : options.iconName;
16104
+ const iconSvg = KritzelIconRegistry.get(iconName);
16105
+ if (!iconSvg) {
16106
+ console.warn(`Icon "${iconName}" not found in registry.`);
16107
+ return 'auto';
16108
+ }
16109
+ const size = options.size || 24;
16110
+ const rotation = options.rotation || 0;
16111
+ const color = options.color || 'black';
16112
+ // Default cursor (mouse-pointer) has hotspot at (4, 4), others at center
16113
+ const hotspot = options.hotspot || (options.iconName === 'default' ? { x: 4, y: 4 } : { x: size / 2, y: size / 2 });
16114
+ // Modify the SVG string to set size and color
16115
+ // We replace width and height to match the requested size
16116
+ // We replace currentColor with the requested color
16117
+ let content = iconSvg
16118
+ .replace(/width="\d+"/, `width="${size}"`)
16119
+ .replace(/height="\d+"/, `height="${size}"`)
16120
+ .replace(/currentColor/g, color);
16121
+ // Create the SVG string
16122
+ // We rotate around the center of the SVG canvas
16123
+ const center = size / 2;
16124
+ // We use a group to apply the rotation
16125
+ const svg = `
16126
+ <svg xmlns="http://www.w3.org/2000/svg" width="${size}" height="${size}" viewBox="0 0 ${size} ${size}">
16127
+ <g transform="rotate(${rotation} ${center} ${center})">
16128
+ ${content}
16129
+ </g>
16130
+ </svg>
16131
+ `;
16132
+ // Encode the SVG for use in a data URI
16133
+ // We need to be careful with encoding to ensure it works across browsers
16134
+ const encodedSvg = encodeURIComponent(svg.replace(/\s+/g, ' ').trim())
16135
+ .replace(/'/g, '%27')
16136
+ .replace(/"/g, '%22');
16137
+ const dataUri = `data:image/svg+xml;charset=utf-8,${encodedSvg}`;
16138
+ // Return the cursor style string
16139
+ return `url('${dataUri}') ${hotspot.x} ${hotspot.y}, auto`;
16140
+ }
16141
+ }
16142
+
16143
+ class KritzelBaseHandler {
16144
+ _core;
16145
+ constructor(core) {
16146
+ this._core = core;
16147
+ }
16148
+ }
16149
+
16150
+ class KritzelMoveHandler extends KritzelBaseHandler {
16151
+ dragStartX;
16152
+ dragStartY;
16153
+ startX;
16154
+ startY;
16155
+ endX;
16156
+ endY;
16157
+ hasMoved = false;
16158
+ trackedPointerId = null;
16159
+ constructor(core) {
16160
+ super(core);
16161
+ }
16162
+ reset() {
16163
+ this.dragStartX = 0;
16164
+ this.dragStartY = 0;
16165
+ this.startX = 0;
16166
+ this.startY = 0;
16167
+ this.endX = 0;
16168
+ this.endY = 0;
16169
+ this.hasMoved = false;
16170
+ this.trackedPointerId = null;
16171
+ }
16172
+ cancelPendingDrag() {
16173
+ this._core.store.state.isDragging = false;
16174
+ this.reset();
16175
+ }
16176
+ handlePointerDown(event) {
16177
+ if (event.pointerType === 'mouse') {
16178
+ if (KritzelEventHelper.isLeftClick(event)) {
15351
16179
  const selectionGroup = this._core.store.selectionGroup;
15352
- if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
15353
- const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
15354
- const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
15355
- this._core.store.state.isRotating = true;
15356
- const centerX = selectionGroup.translateX + selectionGroup.width / 2 / this._core.store.state.scale;
15357
- const centerY = selectionGroup.translateY + selectionGroup.height / 2 / this._core.store.state.scale;
15358
- const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
15359
- const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
15360
- this.initialSelectionGroupRotation = selectionGroup.rotation;
15361
- this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
15362
- this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
15363
- clearTimeout(this._core.store.state.longTouchTimeout);
16180
+ if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected && !this._core.store.state.isLineHandleSelected) {
16181
+ const clientX = event.clientX - this._core.store.offsetX;
16182
+ const clientY = event.clientY - this._core.store.offsetY;
16183
+ this._core.store.state.isDragging = true;
16184
+ this.dragStartX = clientX;
16185
+ this.dragStartY = clientY;
16186
+ this.startX = this.dragStartX;
16187
+ this.startY = this.dragStartY;
16188
+ this.trackedPointerId = event.pointerId;
16189
+ }
16190
+ else {
16191
+ this.trackedPointerId = null;
16192
+ }
16193
+ }
16194
+ else {
16195
+ this.trackedPointerId = null;
16196
+ }
16197
+ }
16198
+ if (event.pointerType === 'touch') {
16199
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16200
+ if (this._core.store.state.pointers.size === 1) {
16201
+ if (this._core.store.state.isScaling) {
16202
+ this.trackedPointerId = null;
16203
+ return;
16204
+ }
16205
+ const selectionGroup = this._core.store.selectionGroup;
16206
+ if (selectionGroup?.isSelected && !this._core.store.state.isResizeHandleSelected && !this._core.store.state.isRotationHandleSelected && !this._core.store.state.isLineHandleSelected) {
16207
+ const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
16208
+ const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
16209
+ this.dragStartX = x;
16210
+ this.dragStartY = y;
16211
+ this.startX = x;
16212
+ this.startY = y;
16213
+ this.trackedPointerId = event.pointerId;
16214
+ }
16215
+ else {
16216
+ this.trackedPointerId = null;
15364
16217
  }
15365
16218
  }
15366
16219
  }
15367
16220
  }
15368
16221
  handlePointerMove(event) {
16222
+ if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
16223
+ return;
16224
+ }
15369
16225
  if (event.pointerType === 'mouse') {
15370
16226
  const selectionGroup = this._core.store.selectionGroup;
15371
- if (this._core.store.state.isRotating && selectionGroup) {
16227
+ if (this._core.store.state.isDragging && selectionGroup) {
15372
16228
  const clientX = event.clientX - this._core.store.offsetX;
15373
16229
  const clientY = event.clientY - this._core.store.offsetY;
15374
- const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / this._core.store.state.scale;
15375
- const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / this._core.store.state.scale;
15376
- const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
15377
- const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
15378
- const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
15379
- this.rotation = currentRotation - this.initialRotation;
15380
- selectionGroup.rotate(this.rotation);
16230
+ this.endX = clientX;
16231
+ this.endY = clientY;
16232
+ const moveDeltaX = Math.abs(clientX - this.startX);
16233
+ const moveDeltaY = Math.abs(clientY - this.startY);
16234
+ const moveThreshold = 5;
16235
+ if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16236
+ selectionGroup.move(clientX, clientY, this.dragStartX, this.dragStartY);
16237
+ this.dragStartX = clientX;
16238
+ this.dragStartY = clientY;
16239
+ this.hasMoved = true;
16240
+ }
15381
16241
  }
15382
16242
  }
15383
16243
  if (event.pointerType === 'touch') {
15384
16244
  const activePointers = Array.from(this._core.store.state.pointers.values());
15385
- const firstTouch = activePointers[0];
15386
- if (!firstTouch) {
15387
- return;
15388
- }
15389
16245
  const selectionGroup = this._core.store.selectionGroup;
15390
- if (this._core.store.state.isRotating && selectionGroup) {
15391
- const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
15392
- const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
15393
- const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / this._core.store.state.scale;
15394
- const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / this._core.store.state.scale;
15395
- const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
15396
- const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
15397
- const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
15398
- this.rotation = currentRotation - this.initialRotation;
15399
- selectionGroup.rotate(this.rotation);
15400
- clearTimeout(this._core.store.state.longTouchTimeout);
16246
+ if (this._core.store.state.pointers.size === 1 &&
16247
+ selectionGroup &&
16248
+ !this._core.store.state.isResizeHandleSelected &&
16249
+ !this._core.store.state.isRotationHandleSelected &&
16250
+ !this._core.store.state.isScaling) {
16251
+ const x = Math.round(activePointers[0].clientX - this._core.store.offsetX);
16252
+ const y = Math.round(activePointers[0].clientY - this._core.store.offsetY);
16253
+ this._core.store.state.isDragging = true;
16254
+ this.endX = x;
16255
+ this.endY = y;
16256
+ const moveDeltaX = Math.abs(x - this.startX);
16257
+ const moveDeltaY = Math.abs(y - this.startY);
16258
+ const moveThreshold = 5;
16259
+ if (moveDeltaX > moveThreshold || moveDeltaY > moveThreshold) {
16260
+ clearTimeout(this._core.store.state.longTouchTimeout);
16261
+ selectionGroup.move(x, y, this.dragStartX, this.dragStartY);
16262
+ this.dragStartX = x;
16263
+ this.dragStartY = y;
16264
+ this.hasMoved = true;
16265
+ }
15401
16266
  }
15402
16267
  }
15403
16268
  }
15404
16269
  handlePointerUp(event) {
16270
+ if (this.trackedPointerId === null || this.trackedPointerId !== event.pointerId) {
16271
+ return;
16272
+ }
15405
16273
  if (event.pointerType === 'mouse') {
15406
- if (this._core.store.state.isRotating) {
15407
- this._core.store.selectionGroup.update();
15408
- this._core.engine.emitObjectsChange();
15409
- this._core.store.state.isRotating = false;
15410
- this._core.store.state.hasObjectsChanged = true;
15411
- this.reset();
16274
+ if (this._core.store.state.isDragging) {
16275
+ this._core.store.state.isDragging = false;
16276
+ if (this.hasMoved) {
16277
+ this._core.store.selectionGroup.update();
16278
+ this._core.engine.emitObjectsChange();
16279
+ this._core.store.state.hasObjectsChanged = true;
16280
+ }
15412
16281
  }
15413
16282
  }
15414
16283
  if (event.pointerType === 'touch') {
15415
- if (this._core.store.state.isRotating) {
15416
- this._core.store.selectionGroup.update();
15417
- this._core.engine.emitObjectsChange();
15418
- this._core.store.state.isRotating = false;
15419
- this._core.store.state.hasObjectsChanged = true;
15420
- this.reset();
15421
- clearTimeout(this._core.store.state.longTouchTimeout);
16284
+ if (this._core.store.state.isDragging) {
16285
+ this._core.store.state.isDragging = false;
16286
+ if (this.hasMoved) {
16287
+ this._core.store.selectionGroup.update();
16288
+ this._core.engine.emitObjectsChange();
16289
+ this._core.store.state.hasObjectsChanged = true;
16290
+ }
15422
16291
  }
15423
16292
  }
16293
+ this.reset();
15424
16294
  }
15425
16295
  }
15426
16296
 
15427
- class KritzelSelectionBox extends KritzelBaseObject {
15428
- __class__ = 'KritzelSelectionBox';
15429
- static create(core) {
15430
- const object = new KritzelSelectionBox();
15431
- object._core = core;
15432
- object.id = object.generateId();
15433
- object.workspaceId = core.store.state.activeWorkspace.id;
15434
- object.scale = core.store.state.scale;
15435
- object.zIndex = 99999;
15436
- object.backgroundColor = 'var(--kritzel-selection-box-background-color, rgba(0, 122, 255, 0.2))';
15437
- object.borderColor = 'var(--kritzel-selection-box-border-color, rgba(0, 122, 255, 0.5))';
15438
- object.borderWidth = 2;
15439
- object.height = 0;
15440
- object.width = 0;
15441
- return object;
15442
- }
15443
- }
16297
+ var KritzelHandleType;
16298
+ (function (KritzelHandleType) {
16299
+ KritzelHandleType["TopLeft"] = "top-left";
16300
+ KritzelHandleType["TopRight"] = "top-right";
16301
+ KritzelHandleType["BottomLeft"] = "bottom-left";
16302
+ KritzelHandleType["BottomRight"] = "bottom-right";
16303
+ })(KritzelHandleType || (KritzelHandleType = {}));
15444
16304
 
15445
- class KritzelSelectionGroup extends KritzelBaseObject {
15446
- __class__ = 'KritzelSelectionGroup';
15447
- // Store only object IDs instead of full objects
15448
- objectIds = [];
15449
- // Store snapshots of object state for transformations (rotation, resize)
15450
- unchangedObjectSnapshots = new Map();
15451
- snapshotRotation = 0;
15452
- minX;
15453
- maxX;
15454
- minY;
15455
- maxY;
15456
- // Getter to retrieve actual objects from the store by their IDs
15457
- get objects() {
15458
- return this.objectIds
15459
- .map(id => {
15460
- const found = this._core.store.state.objects.filter(obj => obj.id === id);
15461
- return found.length > 0 ? found[0] : null;
15462
- })
15463
- .filter(obj => obj !== null);
15464
- }
15465
- get length() {
15466
- return this.objectIds.length;
16305
+ class KritzelResizeHandler extends KritzelBaseHandler {
16306
+ initialMouseX = 0;
16307
+ initialMouseY = 0;
16308
+ initialSize = { x: 0, y: 0, width: 0, height: 0 };
16309
+ newSize = { x: 0, y: 0, width: 0, height: 0 };
16310
+ hasResized = false;
16311
+ constructor(core) {
16312
+ super(core);
15467
16313
  }
15468
- static create(core) {
15469
- const object = new KritzelSelectionGroup();
15470
- object._core = core;
15471
- object.id = object.generateId();
15472
- object.workspaceId = core.store.state.activeWorkspace.id;
15473
- object.scale = core.store.state.scale;
15474
- object.zIndex = 99999;
15475
- return object;
16314
+ reset() {
16315
+ this.initialMouseX = 0;
16316
+ this.initialMouseY = 0;
16317
+ this.initialSize = { x: 0, y: 0, width: 0, height: 0 };
16318
+ this.newSize = { x: 0, y: 0, width: 0, height: 0 };
16319
+ this.hasResized = false;
15476
16320
  }
15477
- addOrRemove(object) {
15478
- const index = this.objectIds.findIndex(id => id === object.id);
15479
- if (index === -1) {
15480
- this.objectIds.push(object.id);
16321
+ handlePointerDown(event) {
16322
+ if (event.pointerType === 'mouse') {
16323
+ if (KritzelEventHelper.isLeftClick(event)) {
16324
+ const selectionGroup = this._core.store.selectionGroup;
16325
+ if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
16326
+ const clientX = event.clientX - this._core.store.offsetX;
16327
+ const clientY = event.clientY - this._core.store.offsetY;
16328
+ this._core.store.state.isResizing = true;
16329
+ this.initialMouseX = clientX;
16330
+ this.initialMouseY = clientY;
16331
+ this.initialSize.width = selectionGroup.width;
16332
+ this.initialSize.height = selectionGroup.height;
16333
+ this.initialSize.x = selectionGroup.translateX;
16334
+ this.initialSize.y = selectionGroup.translateY;
16335
+ }
16336
+ }
15481
16337
  }
15482
- else {
15483
- this.objectIds.splice(index, 1);
16338
+ if (event.pointerType === 'touch') {
16339
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16340
+ const firstTouch = activePointers[0];
16341
+ if (!firstTouch) {
16342
+ return;
16343
+ }
16344
+ if (activePointers.length === 1) {
16345
+ const selectionGroup = this._core.store.selectionGroup;
16346
+ if (selectionGroup && this._core.store.state.isResizeHandleSelected) {
16347
+ const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
16348
+ const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
16349
+ this._core.store.state.isResizing = true;
16350
+ this.initialMouseX = clientX;
16351
+ this.initialMouseY = clientY;
16352
+ this.initialSize.width = selectionGroup.width;
16353
+ this.initialSize.height = selectionGroup.height;
16354
+ this.initialSize.x = selectionGroup.translateX;
16355
+ this.initialSize.y = selectionGroup.translateY;
16356
+ clearTimeout(this._core.store.state.longTouchTimeout);
16357
+ }
16358
+ }
15484
16359
  }
15485
- this.captureUnchangedSnapshots();
15486
- this.refreshObjectDimensions();
15487
16360
  }
15488
- deselectAllChildren() {
15489
- this.objects.forEach(obj => (obj.isSelected = false));
15490
- }
15491
- updateWorkspaceId(workspaceId) {
15492
- this.workspaceId = workspaceId;
15493
- this.objects.forEach(obj => (obj.workspaceId = workspaceId));
15494
- }
15495
- updateZIndices(startZIndex) {
15496
- this.objects.forEach((obj, i) => {
15497
- obj.zIndex = startZIndex + i;
15498
- });
15499
- }
15500
- updatePosition(x, y) {
15501
- this.objects.forEach(obj => {
15502
- const deltaX = obj.translateX - this.translateX;
15503
- const deltaY = obj.translateY - this.translateY;
15504
- obj.updatePosition(x + deltaX, y + deltaY);
15505
- });
15506
- // Update snapshots
15507
- this.unchangedObjectSnapshots.forEach(snapshot => {
15508
- const deltaX = snapshot.translateX - this.translateX;
15509
- const deltaY = snapshot.translateY - this.translateY;
15510
- snapshot.translateX = x + deltaX;
15511
- snapshot.translateY = x + deltaY;
15512
- });
15513
- this.translateX = x;
15514
- this.translateY = y;
15515
- this._core.store.state.objects.update(this);
15516
- }
15517
- /**
15518
- * Capture snapshots of current object states for undo/redo operations
15519
- */
15520
- captureUnchangedSnapshots() {
15521
- this.unchangedObjectSnapshots.clear();
15522
- this.snapshotRotation = this.rotation;
15523
- this.objects.forEach(obj => {
15524
- this.unchangedObjectSnapshots.set(obj.id, {
15525
- id: obj.id,
15526
- translateX: obj.translateX,
15527
- translateY: obj.translateY,
15528
- rotation: obj.rotation,
15529
- width: obj.width,
15530
- height: obj.height,
15531
- totalWidth: obj.totalWidth,
15532
- totalHeight: obj.totalHeight,
15533
- scale: obj.scale,
15534
- });
15535
- });
15536
- }
15537
- serialize() {
15538
- const { _core, _elementRef, element, totalWidth, totalHeight, unchangedObjectSnapshots, ...remainingProps } = this;
15539
- const clonedProps = structuredClone(remainingProps);
15540
- if (element && typeof element === 'object' && 'nodeType' in element && element.nodeType === 1) {
15541
- clonedProps.element = element.cloneNode(true);
16361
+ handlePointerMove(event) {
16362
+ if (event.pointerType === 'mouse') {
16363
+ const selectionGroup = this._core.store.selectionGroup;
16364
+ if (this._core.store.state.isResizing && selectionGroup) {
16365
+ const clientX = event.clientX - this._core.store.offsetX;
16366
+ const clientY = event.clientY - this._core.store.offsetY;
16367
+ const dx = clientX - this.initialMouseX;
16368
+ const dy = clientY - this.initialMouseY;
16369
+ const resizeDeltaX = Math.abs(dx);
16370
+ const resizeDeltaY = Math.abs(dy);
16371
+ const resizeThreshold = 5;
16372
+ if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
16373
+ this.hasResized = true;
16374
+ }
16375
+ if (!this.hasResized) {
16376
+ return;
16377
+ }
16378
+ const rotation = selectionGroup.rotation;
16379
+ const sin = Math.sin(rotation);
16380
+ const cos = Math.cos(rotation);
16381
+ const objectScale = selectionGroup.scale || 1;
16382
+ const currentScale = this._core.store.state.scale;
16383
+ // Calculate delta in local unrotated space
16384
+ // We rotate the screen delta by -rotation to align with the object's axes
16385
+ const localDx = (dx * cos + dy * sin) / currentScale;
16386
+ const localDy = (-dx * sin + dy * cos) / currentScale;
16387
+ // Calculate the center of the selection group before resize
16388
+ const initialCenterX = this.initialSize.x + this.initialSize.width / objectScale / 2;
16389
+ const initialCenterY = this.initialSize.y + this.initialSize.height / objectScale / 2;
16390
+ // The center moves by half of the screen delta (scaled)
16391
+ // This is true regardless of rotation because the resize happens symmetrically around the center
16392
+ // relative to the fixed point logic
16393
+ const newCenterX = initialCenterX + dx / currentScale / 2;
16394
+ const newCenterY = initialCenterY + dy / currentScale / 2;
16395
+ switch (this._core.store.state.resizeHandleType) {
16396
+ case KritzelHandleType.TopLeft:
16397
+ this.newSize.width = this.initialSize.width - localDx * objectScale;
16398
+ this.newSize.height = this.initialSize.height - localDy * objectScale;
16399
+ break;
16400
+ case KritzelHandleType.TopRight:
16401
+ this.newSize.width = this.initialSize.width + localDx * objectScale;
16402
+ this.newSize.height = this.initialSize.height - localDy * objectScale;
16403
+ break;
16404
+ case KritzelHandleType.BottomLeft:
16405
+ this.newSize.width = this.initialSize.width - localDx * objectScale;
16406
+ this.newSize.height = this.initialSize.height + localDy * objectScale;
16407
+ break;
16408
+ case KritzelHandleType.BottomRight:
16409
+ this.newSize.width = this.initialSize.width + localDx * objectScale;
16410
+ this.newSize.height = this.initialSize.height + localDy * objectScale;
16411
+ break;
16412
+ }
16413
+ this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
16414
+ this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
16415
+ selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
16416
+ }
15542
16417
  }
15543
- // Convert Map to plain object for serialization
15544
- clonedProps.unchangedObjectSnapshots = Object.fromEntries(this.unchangedObjectSnapshots);
15545
- return clonedProps;
15546
- }
15547
- deserialize(object) {
15548
- // First, deserialize all base properties using parent's deserialize
15549
- super.deserialize(object);
15550
- // Restore the Map from serialized object
15551
- if (object.unchangedObjectSnapshots) {
15552
- this.unchangedObjectSnapshots = new Map(Object.entries(object.unchangedObjectSnapshots));
16418
+ if (event.pointerType === 'touch') {
16419
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16420
+ const oneFingerTouch = activePointers[0];
16421
+ if (!oneFingerTouch) {
16422
+ return;
16423
+ }
16424
+ const selectionGroup = this._core.store.selectionGroup;
16425
+ if (this._core.store.state.isResizing && selectionGroup) {
16426
+ const clientX = Math.round(oneFingerTouch.clientX - this._core.store.offsetX);
16427
+ const clientY = Math.round(oneFingerTouch.clientY - this._core.store.offsetY);
16428
+ const dx = clientX - this.initialMouseX;
16429
+ const dy = clientY - this.initialMouseY;
16430
+ const resizeDeltaX = Math.abs(dx);
16431
+ const resizeDeltaY = Math.abs(dy);
16432
+ const resizeThreshold = 5;
16433
+ if (resizeDeltaX > resizeThreshold || resizeDeltaY > resizeThreshold) {
16434
+ clearTimeout(this._core.store.state.longTouchTimeout);
16435
+ this.hasResized = true;
16436
+ }
16437
+ if (!this.hasResized) {
16438
+ return;
16439
+ }
16440
+ const rotation = selectionGroup.rotation;
16441
+ const sin = Math.sin(rotation);
16442
+ const cos = Math.cos(rotation);
16443
+ const objectScale = selectionGroup.scale || 1;
16444
+ const currentScale = this._core.store.state.scale;
16445
+ // Calculate delta in local unrotated space
16446
+ // We rotate the screen delta by -rotation to align with the object's axes
16447
+ const localDx = (dx * cos + dy * sin) / currentScale;
16448
+ const localDy = (-dx * sin + dy * cos) / currentScale;
16449
+ // Calculate the center of the selection group before resize
16450
+ const initialCenterX = this.initialSize.x + this.initialSize.width / objectScale / 2;
16451
+ const initialCenterY = this.initialSize.y + this.initialSize.height / objectScale / 2;
16452
+ // The center moves by half of the screen delta (scaled)
16453
+ // This is true regardless of rotation because the resize happens symmetrically around the center
16454
+ // relative to the fixed point logic
16455
+ const newCenterX = initialCenterX + dx / currentScale / 2;
16456
+ const newCenterY = initialCenterY + dy / currentScale / 2;
16457
+ switch (this._core.store.state.resizeHandleType) {
16458
+ case KritzelHandleType.TopLeft:
16459
+ this.newSize.width = this.initialSize.width - localDx * objectScale;
16460
+ this.newSize.height = this.initialSize.height - localDy * objectScale;
16461
+ break;
16462
+ case KritzelHandleType.TopRight:
16463
+ this.newSize.width = this.initialSize.width + localDx * objectScale;
16464
+ this.newSize.height = this.initialSize.height - localDy * objectScale;
16465
+ break;
16466
+ case KritzelHandleType.BottomLeft:
16467
+ this.newSize.width = this.initialSize.width - localDx * objectScale;
16468
+ this.newSize.height = this.initialSize.height + localDy * objectScale;
16469
+ break;
16470
+ case KritzelHandleType.BottomRight:
16471
+ this.newSize.width = this.initialSize.width + localDx * objectScale;
16472
+ this.newSize.height = this.initialSize.height + localDy * objectScale;
16473
+ break;
16474
+ }
16475
+ this.newSize.x = newCenterX - this.newSize.width / objectScale / 2;
16476
+ this.newSize.y = newCenterY - this.newSize.height / objectScale / 2;
16477
+ selectionGroup.resize(this.newSize.x, this.newSize.y, this.newSize.width, this.newSize.height);
16478
+ }
15553
16479
  }
15554
- return this;
15555
- }
15556
- update() {
15557
- // Only update the selection group itself
15558
- // Child objects are already updated during move/resize/rotate operations
15559
- // Updating them again here would create redundant y.js updates
15560
- this._core.store.state.objects.update(this);
15561
16480
  }
15562
- move(startX, startY, endX, endY) {
15563
- const deltaX = (startX - endX) / this._core.store.state.scale;
15564
- const deltaY = (startY - endY) / this._core.store.state.scale;
15565
- this.translateX += deltaX;
15566
- this.translateY += deltaY;
15567
- this._core.store.state.objects.transaction(() => {
15568
- this._core.store.state.objects.update(this);
15569
- this.objects.forEach(obj => {
15570
- obj.move(startX, startY, endX, endY);
15571
- });
15572
- });
15573
- // Update snapshots
15574
- this.unchangedObjectSnapshots.forEach(snapshot => {
15575
- snapshot.translateX += deltaX;
15576
- snapshot.translateY += deltaY;
15577
- });
16481
+ handlePointerUp(event) {
16482
+ if (event.pointerType === 'mouse') {
16483
+ if (this._core.store.state.isResizing) {
16484
+ this._core.store.state.isResizing = false;
16485
+ if (this.hasResized) {
16486
+ this._core.store.selectionGroup.update();
16487
+ this._core.engine.emitObjectsChange();
16488
+ this._core.store.state.hasObjectsChanged = true;
16489
+ }
16490
+ this.reset();
16491
+ }
16492
+ }
16493
+ if (event.pointerType === 'touch') {
16494
+ if (this._core.store.state.isResizing) {
16495
+ this._core.store.state.isResizing = false;
16496
+ if (this.hasResized) {
16497
+ this._core.store.selectionGroup.update();
16498
+ this._core.engine.emitObjectsChange();
16499
+ this._core.store.state.hasObjectsChanged = true;
16500
+ }
16501
+ this.reset();
16502
+ clearTimeout(this._core.store.state.longTouchTimeout);
16503
+ }
16504
+ }
15578
16505
  }
15579
- resize(x, y, width, height) {
15580
- const widthScaleFactor = width / this.width;
15581
- const heightScaleFactor = height / this.height;
15582
- // Calculate old center
15583
- const oldCenterX = this.translateX + this.totalWidth / 2 / this.scale;
15584
- const oldCenterY = this.translateY + this.totalHeight / 2 / this.scale;
15585
- // Calculate new center
15586
- const newTotalWidth = width + this.padding * 2;
15587
- const newTotalHeight = height + this.padding * 2;
15588
- const newCenterX = x + newTotalWidth / 2 / this.scale;
15589
- const newCenterY = y + newTotalHeight / 2 / this.scale;
15590
- const rotation = this.rotation;
15591
- const cos = Math.cos(-rotation);
15592
- const sin = Math.sin(-rotation);
15593
- const cosR = Math.cos(rotation);
15594
- const sinR = Math.sin(rotation);
15595
- this._core.store.state.objects.transaction(() => {
15596
- this.objects.forEach(child => {
15597
- // Calculate child center
15598
- const childCenterX = child.translateX + child.totalWidth / 2 / child.scale;
15599
- const childCenterY = child.translateY + child.totalHeight / 2 / child.scale;
15600
- // Vector from old group center to child center
15601
- const dx = childCenterX - oldCenterX;
15602
- const dy = childCenterY - oldCenterY;
15603
- // Rotate to local space (align with group axes)
15604
- const localX = dx * cos - dy * sin;
15605
- const localY = dx * sin + dy * cos;
15606
- // Scale in local space
15607
- const scaledLocalX = localX * widthScaleFactor;
15608
- const scaledLocalY = localY * heightScaleFactor;
15609
- // Rotate back to world space
15610
- const rotatedX = scaledLocalX * cosR - scaledLocalY * sinR;
15611
- const rotatedY = scaledLocalX * sinR + scaledLocalY * cosR;
15612
- // New child center
15613
- const newChildCenterX = newCenterX + rotatedX;
15614
- const newChildCenterY = newCenterY + rotatedY;
15615
- // New child top-left
15616
- // Calculate relative rotation
15617
- const relativeRotation = child.rotation - rotation;
15618
- const cosRel = Math.cos(relativeRotation);
15619
- const sinRel = Math.sin(relativeRotation);
15620
- // Project the group's scale factors onto the child's local axes
15621
- // We use absolute values because scaling is magnitude-based
15622
- // If the child is aligned (0 deg), cos=1, sin=0 -> scales match
15623
- // If the child is 90 deg, cos=0, sin=1 -> scales swap
15624
- const newChildWidthScale = Math.sqrt(Math.pow(widthScaleFactor * cosRel, 2) + Math.pow(heightScaleFactor * sinRel, 2));
15625
- const newChildHeightScale = Math.sqrt(Math.pow(widthScaleFactor * sinRel, 2) + Math.pow(heightScaleFactor * cosRel, 2));
15626
- const updatedWidth = child.width * newChildWidthScale;
15627
- const updatedHeight = child.height * newChildHeightScale;
15628
- const updatedTotalWidth = updatedWidth + child.padding * 2;
15629
- const updatedTotalHeight = updatedHeight + child.padding * 2;
15630
- const updatedX = newChildCenterX - updatedTotalWidth / 2 / child.scale;
15631
- const updatedY = newChildCenterY - updatedTotalHeight / 2 / child.scale;
15632
- child.resize(updatedX, updatedY, updatedWidth, updatedHeight);
15633
- });
15634
- // Refresh dimensions and update the SelectionGroup to propagate changes to other tabs
15635
- this.refreshObjectDimensions();
15636
- this.captureUnchangedSnapshots();
15637
- this._core.store.state.objects.update(this);
15638
- });
16506
+ }
16507
+
16508
+ class KritzelRotationHandler extends KritzelBaseHandler {
16509
+ initialRotation = 0;
16510
+ rotation = 0;
16511
+ unchangedObjects = [];
16512
+ initialSelectionGroupRotation = 0;
16513
+ constructor(core) {
16514
+ super(core);
15639
16515
  }
15640
- rotate(value) {
15641
- this.rotation = value;
15642
- const centerX = this.translateX + this.totalWidth / 2 / this.scale;
15643
- const centerY = this.translateY + this.totalHeight / 2 / this.scale;
15644
- const angle = value - this.snapshotRotation;
15645
- const cos = Math.cos(angle);
15646
- const sin = Math.sin(angle);
15647
- this._core.store.state.objects.transaction(() => {
15648
- // Update the SelectionGroup itself to propagate rotation to other tabs
15649
- this._core.store.state.objects.update(this);
15650
- this.objects.forEach(child => {
15651
- const unchangedSnapshot = this.unchangedObjectSnapshots.get(child.id);
15652
- if (!unchangedSnapshot)
15653
- return;
15654
- const offsetX = this.getOffsetXToCenterFromSnapshot(unchangedSnapshot);
15655
- const offsetY = this.getOffsetYToCenterFromSnapshot(unchangedSnapshot);
15656
- const rotatedX = cos * offsetX - sin * offsetY;
15657
- const rotatedY = sin * offsetX + cos * offsetY;
15658
- child.translateX = centerX + rotatedX - child.totalWidth / 2 / child.scale;
15659
- child.translateY = centerY + rotatedY - child.totalHeight / 2 / child.scale;
15660
- child.rotate(this.objects.length === 1 ? value : unchangedSnapshot.rotation + angle);
15661
- });
15662
- });
16516
+ reset() {
16517
+ this.initialRotation = 0;
16518
+ this.rotation = 0;
16519
+ this.unchangedObjects = [];
15663
16520
  }
15664
- copy() {
15665
- const selectionGroup = KritzelSelectionGroup.create(this._core);
15666
- this.objects
15667
- .sort((a, b) => a.zIndex - b.zIndex)
15668
- .forEach(obj => {
15669
- const copiedObject = obj.copy();
15670
- selectionGroup.addOrRemove(copiedObject);
15671
- });
15672
- selectionGroup.captureUnchangedSnapshots();
15673
- if (this.objects.length === 1) {
15674
- selectionGroup.rotation = this.objects[0].rotation;
16521
+ handlePointerDown(event) {
16522
+ if (event.pointerType === 'mouse') {
16523
+ if (KritzelEventHelper.isLeftClick(event)) {
16524
+ const selectionGroup = this._core.store.selectionGroup;
16525
+ if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
16526
+ const clientX = event.clientX - this._core.store.offsetX;
16527
+ const clientY = event.clientY - this._core.store.offsetY;
16528
+ this._core.store.state.isRotating = true;
16529
+ const objectScale = selectionGroup.scale || 1;
16530
+ const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
16531
+ const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
16532
+ const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
16533
+ const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
16534
+ this.initialSelectionGroupRotation = selectionGroup.rotation;
16535
+ this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
16536
+ this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
16537
+ }
16538
+ }
16539
+ }
16540
+ if (event.pointerType === 'touch') {
16541
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16542
+ const firstTouch = activePointers[0];
16543
+ if (!firstTouch) {
16544
+ return;
16545
+ }
16546
+ if (activePointers.length === 1) {
16547
+ const selectionGroup = this._core.store.selectionGroup;
16548
+ if (selectionGroup && this._core.store.state.isRotationHandleSelected) {
16549
+ const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
16550
+ const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
16551
+ this._core.store.state.isRotating = true;
16552
+ const objectScale = selectionGroup.scale || 1;
16553
+ const centerX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
16554
+ const centerY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
16555
+ const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
16556
+ const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
16557
+ this.initialSelectionGroupRotation = selectionGroup.rotation;
16558
+ this.initialRotation = Math.atan2(centerY - cursorY, centerX - cursorX) - selectionGroup.rotation;
16559
+ this.unchangedObjects = selectionGroup.objects.map(obj => obj.clone());
16560
+ clearTimeout(this._core.store.state.longTouchTimeout);
16561
+ }
16562
+ }
15675
16563
  }
15676
- return selectionGroup;
15677
16564
  }
15678
- refreshObjectDimensions() {
15679
- if (this.objects.length === 1) {
15680
- const obj = this.objects[0];
15681
- this.minX = obj.boundingBox.x / this.scale;
15682
- this.maxX = obj.boundingBox.x / this.scale + obj.boundingBox.width;
15683
- this.minY = obj.boundingBox.y / this.scale;
15684
- this.maxY = obj.boundingBox.y / this.scale + obj.boundingBox.height;
15685
- this.translateX = (this.minX - this.padding) * this.scale;
15686
- this.translateY = (this.minY - this.padding) * this.scale;
15687
- this.width = (this.maxX - this.minX - this.padding) * this.scale;
15688
- this.height = (this.maxY - this.minY - this.padding) * this.scale;
16565
+ handlePointerMove(event) {
16566
+ if (event.pointerType === 'mouse') {
16567
+ const selectionGroup = this._core.store.selectionGroup;
16568
+ if (this._core.store.state.isRotating && selectionGroup) {
16569
+ const clientX = event.clientX - this._core.store.offsetX;
16570
+ const clientY = event.clientY - this._core.store.offsetY;
16571
+ const objectScale = selectionGroup.scale || 1;
16572
+ const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
16573
+ const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
16574
+ const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
16575
+ const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
16576
+ const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
16577
+ this.rotation = currentRotation - this.initialRotation;
16578
+ selectionGroup.rotate(this.rotation);
16579
+ }
15689
16580
  }
15690
- else {
15691
- const rotation = this.rotation;
15692
- const cos = Math.cos(-rotation);
15693
- const sin = Math.sin(-rotation);
15694
- let minX = Infinity;
15695
- let maxX = -Infinity;
15696
- let minY = Infinity;
15697
- let maxY = -Infinity;
15698
- this.objects.forEach(obj => {
15699
- const polygon = obj.rotatedPolygon;
15700
- const corners = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
15701
- corners.forEach(corner => {
15702
- // Rotate corner into local space (aligned with group rotation)
15703
- const rx = corner.x * cos - corner.y * sin;
15704
- const ry = corner.x * sin + corner.y * cos;
15705
- if (rx < minX)
15706
- minX = rx;
15707
- if (rx > maxX)
15708
- maxX = rx;
15709
- if (ry < minY)
15710
- minY = ry;
15711
- if (ry > maxY)
15712
- maxY = ry;
15713
- });
15714
- });
15715
- // Dimensions in world units (unrotated)
15716
- const worldWidth = maxX - minX;
15717
- const worldHeight = maxY - minY;
15718
- this.width = (worldWidth - this.padding) * this.scale;
15719
- this.height = (worldHeight - this.padding) * this.scale;
15720
- // Center of the box in rotated space
15721
- const cRx = (minX + maxX) / 2;
15722
- const cRy = (minY + maxY) / 2;
15723
- // Rotate center back to world space
15724
- const cosR = Math.cos(rotation);
15725
- const sinR = Math.sin(rotation);
15726
- const cx = cRx * cosR - cRy * sinR;
15727
- const cy = cRx * sinR + cRy * cosR;
15728
- this.translateX = cx - (this.width / this.scale + 2 * this.padding) / 2;
15729
- this.translateY = cy - (this.height / this.scale + 2 * this.padding) / 2;
16581
+ if (event.pointerType === 'touch') {
16582
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16583
+ const firstTouch = activePointers[0];
16584
+ if (!firstTouch) {
16585
+ return;
16586
+ }
16587
+ const selectionGroup = this._core.store.selectionGroup;
16588
+ if (this._core.store.state.isRotating && selectionGroup) {
16589
+ const clientX = Math.round(firstTouch.clientX - this._core.store.offsetX);
16590
+ const clientY = Math.round(firstTouch.clientY - this._core.store.offsetY);
16591
+ const objectScale = selectionGroup.scale || 1;
16592
+ const groupCenterX = selectionGroup.translateX + selectionGroup.width / 2 / objectScale;
16593
+ const groupCenterY = selectionGroup.translateY + selectionGroup.height / 2 / objectScale;
16594
+ const cursorX = (clientX - this._core.store.state.translateX) / this._core.store.state.scale;
16595
+ const cursorY = (clientY - this._core.store.state.translateY) / this._core.store.state.scale;
16596
+ const currentRotation = Math.atan2(groupCenterY - cursorY, groupCenterX - cursorX);
16597
+ this.rotation = currentRotation - this.initialRotation;
16598
+ selectionGroup.rotate(this.rotation);
16599
+ clearTimeout(this._core.store.state.longTouchTimeout);
16600
+ }
15730
16601
  }
15731
- this._core.store.state.objects.update(this);
15732
16602
  }
15733
- getOffsetXToCenterFromSnapshot(snapshot) {
15734
- const objCenterX = snapshot.translateX + snapshot.totalWidth / snapshot.scale / 2;
15735
- const groupCenterX = this.translateX + this.totalWidth / this.scale / 2;
15736
- return objCenterX - groupCenterX;
16603
+ handlePointerUp(event) {
16604
+ if (event.pointerType === 'mouse') {
16605
+ if (this._core.store.state.isRotating) {
16606
+ this._core.store.selectionGroup.update();
16607
+ this._core.engine.emitObjectsChange();
16608
+ this._core.store.state.isRotating = false;
16609
+ this._core.store.state.hasObjectsChanged = true;
16610
+ this.reset();
16611
+ }
16612
+ }
16613
+ if (event.pointerType === 'touch') {
16614
+ if (this._core.store.state.isRotating) {
16615
+ this._core.store.selectionGroup.update();
16616
+ this._core.engine.emitObjectsChange();
16617
+ this._core.store.state.isRotating = false;
16618
+ this._core.store.state.hasObjectsChanged = true;
16619
+ this.reset();
16620
+ clearTimeout(this._core.store.state.longTouchTimeout);
16621
+ }
16622
+ }
15737
16623
  }
15738
- getOffsetYToCenterFromSnapshot(snapshot) {
15739
- const objCenterY = snapshot.translateY + snapshot.totalHeight / snapshot.scale / 2;
15740
- const groupCenterY = this.translateY + this.totalHeight / this.scale / 2;
15741
- return objCenterY - groupCenterY;
16624
+ }
16625
+
16626
+ class KritzelSelectionBox extends KritzelBaseObject {
16627
+ __class__ = 'KritzelSelectionBox';
16628
+ static create(core) {
16629
+ const object = new KritzelSelectionBox();
16630
+ object._core = core;
16631
+ object.id = object.generateId();
16632
+ object.workspaceId = core.store.state.activeWorkspace.id;
16633
+ object.scale = core.store.state.scale;
16634
+ object.zIndex = 99999;
16635
+ object.backgroundColor = 'var(--kritzel-selection-box-background-color, rgba(0, 122, 255, 0.2))';
16636
+ object.borderColor = 'var(--kritzel-selection-box-border-color, rgba(0, 122, 255, 0.5))';
16637
+ object.borderWidth = 2;
16638
+ object.height = 0;
16639
+ object.width = 0;
16640
+ return object;
15742
16641
  }
15743
16642
  }
15744
16643
 
@@ -16000,12 +16899,393 @@ class KritzelHoverHandler extends KritzelBaseHandler {
16000
16899
  }
16001
16900
  }
16002
16901
 
16902
+ class KritzelLineHandleHandler extends KritzelBaseHandler {
16903
+ initialMouseX = 0;
16904
+ initialMouseY = 0;
16905
+ initialStartX = 0;
16906
+ initialStartY = 0;
16907
+ initialEndX = 0;
16908
+ initialEndY = 0;
16909
+ initialControlX;
16910
+ initialControlY;
16911
+ initialTranslateX = 0;
16912
+ initialTranslateY = 0;
16913
+ hasMoved = false;
16914
+ /** Current snap target during drag, if any */
16915
+ currentSnapTarget = null;
16916
+ constructor(core) {
16917
+ super(core);
16918
+ }
16919
+ reset() {
16920
+ this.initialMouseX = 0;
16921
+ this.initialMouseY = 0;
16922
+ this.initialStartX = 0;
16923
+ this.initialStartY = 0;
16924
+ this.initialEndX = 0;
16925
+ this.initialEndY = 0;
16926
+ this.initialControlX = undefined;
16927
+ this.initialControlY = undefined;
16928
+ this.initialTranslateX = 0;
16929
+ this.initialTranslateY = 0;
16930
+ this.hasMoved = false;
16931
+ this.currentSnapTarget = null;
16932
+ this._core.anchorManager.clearSnapCandidate();
16933
+ }
16934
+ handlePointerDown(event) {
16935
+ if (event.pointerType === 'mouse') {
16936
+ if (KritzelEventHelper.isLeftClick(event)) {
16937
+ this.startHandleDrag(event);
16938
+ }
16939
+ }
16940
+ if (event.pointerType === 'touch') {
16941
+ const activePointers = Array.from(this._core.store.state.pointers.values());
16942
+ if (activePointers.length === 1) {
16943
+ this.startHandleDrag(event);
16944
+ }
16945
+ }
16946
+ }
16947
+ startHandleDrag(event) {
16948
+ const line = this.getSelectedLine();
16949
+ if (!line || !this._core.store.state.isLineHandleSelected) {
16950
+ return;
16951
+ }
16952
+ const clientX = event.clientX - this._core.store.offsetX;
16953
+ const clientY = event.clientY - this._core.store.offsetY;
16954
+ this._core.store.state.isLineHandleDragging = true;
16955
+ this.initialMouseX = clientX;
16956
+ this.initialMouseY = clientY;
16957
+ // Store initial line state
16958
+ this.initialStartX = line.startX;
16959
+ this.initialStartY = line.startY;
16960
+ this.initialEndX = line.endX;
16961
+ this.initialEndY = line.endY;
16962
+ this.initialControlX = line.controlX;
16963
+ this.initialControlY = line.controlY;
16964
+ this.initialTranslateX = line.translateX;
16965
+ this.initialTranslateY = line.translateY;
16966
+ // Remove existing anchor for this endpoint when starting to drag
16967
+ const handleType = this._core.store.state.lineHandleType;
16968
+ if (handleType === 'start' || handleType === 'end') {
16969
+ this._core.anchorManager.removeAnchor(line.id, handleType);
16970
+ }
16971
+ clearTimeout(this._core.store.state.longTouchTimeout);
16972
+ }
16973
+ handlePointerMove(event) {
16974
+ const line = this.getSelectedLine();
16975
+ if (!line || !this._core.store.state.isLineHandleDragging) {
16976
+ return;
16977
+ }
16978
+ const clientX = event.clientX - this._core.store.offsetX;
16979
+ const clientY = event.clientY - this._core.store.offsetY;
16980
+ const dx = clientX - this.initialMouseX;
16981
+ const dy = clientY - this.initialMouseY;
16982
+ const moveThreshold = 3;
16983
+ if (Math.abs(dx) > moveThreshold || Math.abs(dy) > moveThreshold) {
16984
+ this.hasMoved = true;
16985
+ }
16986
+ if (!this.hasMoved) {
16987
+ return;
16988
+ }
16989
+ const viewportScale = this._core.store.state.scale;
16990
+ const objectScale = line.scale || 1;
16991
+ const rotation = line.rotation;
16992
+ // Convert mouse delta to world coordinates
16993
+ const worldDx = dx / viewportScale;
16994
+ const worldDy = dy / viewportScale;
16995
+ // Rotate the delta into the line's local coordinate system
16996
+ const cos = Math.cos(-rotation);
16997
+ const sin = Math.sin(-rotation);
16998
+ const localDx = (worldDx * cos - worldDy * sin) * objectScale;
16999
+ const localDy = (worldDx * sin + worldDy * cos) * objectScale;
17000
+ const handleType = this._core.store.state.lineHandleType;
17001
+ if (handleType === 'start') {
17002
+ // Calculate world position directly from mouse position for snap detection
17003
+ const worldX = (clientX - this._core.store.state.translateX) / viewportScale;
17004
+ const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
17005
+ // Get other endpoint's anchor to prevent anchoring both to same object
17006
+ const otherAnchorId = line.endAnchor?.objectId;
17007
+ // Check for snap target
17008
+ const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
17009
+ this.currentSnapTarget = snapTarget;
17010
+ if (snapTarget) {
17011
+ // Snap to target center - convert world coords to local
17012
+ const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
17013
+ this.updateLineEndpoint(line, localCoords.x, localCoords.y, this.initialEndX, this.initialEndY);
17014
+ // Get the other endpoint's world position for edge intersection calculation
17015
+ const otherEndpointWorld = this.lineLocalToWorld(line, this.initialEndX, this.initialEndY);
17016
+ // Calculate edge intersection point using AnchorManager to support curves
17017
+ const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
17018
+ let edgeX;
17019
+ let edgeY;
17020
+ let t;
17021
+ if (targetObject) {
17022
+ const clipInfo = this._core.anchorManager.computeAnchorClipInfo(line, 'start', targetObject);
17023
+ if (clipInfo) {
17024
+ edgeX = clipInfo.worldX;
17025
+ edgeY = clipInfo.worldY;
17026
+ t = clipInfo.t;
17027
+ }
17028
+ }
17029
+ // Calculate control point in world coordinates if it exists
17030
+ let controlWorld = undefined;
17031
+ if (line.controlX !== undefined && line.controlY !== undefined) {
17032
+ controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
17033
+ }
17034
+ // Update snap indicator
17035
+ this._core.anchorManager.setSnapCandidate({
17036
+ objectId: snapTarget.objectId,
17037
+ endpoint: 'start',
17038
+ centerX: snapTarget.centerX,
17039
+ centerY: snapTarget.centerY,
17040
+ lineEndpointX: otherEndpointWorld.x,
17041
+ lineEndpointY: otherEndpointWorld.y,
17042
+ controlX: controlWorld?.x,
17043
+ controlY: controlWorld?.y,
17044
+ t,
17045
+ edgeX,
17046
+ edgeY,
17047
+ lineStroke: line.stroke,
17048
+ lineStrokeWidth: line.strokeWidth / line.scale,
17049
+ arrowOffset: line.hasStartArrow ? line.getArrowSize('start') / line.scale : undefined,
17050
+ arrowStyle: line.hasStartArrow ? line.arrows?.start?.style : undefined,
17051
+ arrowFill: line.hasStartArrow ? line.getArrowFill('start') : undefined,
17052
+ });
17053
+ }
17054
+ else {
17055
+ // No snap - use regular position
17056
+ const newStartX = this.initialStartX + localDx;
17057
+ const newStartY = this.initialStartY + localDy;
17058
+ this.updateLineEndpoint(line, newStartX, newStartY, this.initialEndX, this.initialEndY);
17059
+ // Clear snap indicator
17060
+ this._core.anchorManager.clearSnapCandidate();
17061
+ }
17062
+ }
17063
+ else if (handleType === 'end') {
17064
+ // Calculate world position directly from mouse position for snap detection
17065
+ const worldX = (clientX - this._core.store.state.translateX) / viewportScale;
17066
+ const worldY = (clientY - this._core.store.state.translateY) / viewportScale;
17067
+ // Get other endpoint's anchor to prevent anchoring both to same object
17068
+ const otherAnchorId = line.startAnchor?.objectId;
17069
+ // Check for snap target
17070
+ const snapTarget = this._core.anchorManager.findSnapTarget(worldX, worldY, line.id, otherAnchorId);
17071
+ this.currentSnapTarget = snapTarget;
17072
+ if (snapTarget) {
17073
+ // Snap to target center - convert world coords to local
17074
+ const localCoords = this.worldToLineLocal(line, snapTarget.centerX, snapTarget.centerY);
17075
+ this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, localCoords.x, localCoords.y);
17076
+ // Get the other endpoint's world position for edge intersection calculation
17077
+ const otherEndpointWorld = this.lineLocalToWorld(line, this.initialStartX, this.initialStartY);
17078
+ // Calculate edge intersection point using AnchorManager to support curves
17079
+ const targetObject = this._core.store.allNonSelectionObjects.find(obj => obj.id === snapTarget.objectId);
17080
+ let edgeX;
17081
+ let edgeY;
17082
+ let t;
17083
+ if (targetObject) {
17084
+ const clipInfo = this._core.anchorManager.computeAnchorClipInfo(line, 'end', targetObject);
17085
+ if (clipInfo) {
17086
+ edgeX = clipInfo.worldX;
17087
+ edgeY = clipInfo.worldY;
17088
+ t = clipInfo.t;
17089
+ }
17090
+ }
17091
+ // Calculate control point in world coordinates if it exists
17092
+ let controlWorld = undefined;
17093
+ if (line.controlX !== undefined && line.controlY !== undefined) {
17094
+ controlWorld = this.lineLocalToWorld(line, line.controlX, line.controlY);
17095
+ }
17096
+ // Update snap indicator
17097
+ this._core.anchorManager.setSnapCandidate({
17098
+ objectId: snapTarget.objectId,
17099
+ endpoint: 'end',
17100
+ centerX: snapTarget.centerX,
17101
+ centerY: snapTarget.centerY,
17102
+ lineEndpointX: otherEndpointWorld.x,
17103
+ lineEndpointY: otherEndpointWorld.y,
17104
+ controlX: controlWorld?.x,
17105
+ controlY: controlWorld?.y,
17106
+ t,
17107
+ edgeX,
17108
+ edgeY,
17109
+ lineStroke: line.stroke,
17110
+ lineStrokeWidth: line.strokeWidth / line.scale,
17111
+ arrowOffset: line.hasEndArrow ? line.getArrowSize('end') / line.scale : undefined,
17112
+ arrowStyle: line.hasEndArrow ? line.arrows?.end?.style : undefined,
17113
+ arrowFill: line.hasEndArrow ? line.getArrowFill('end') : undefined,
17114
+ });
17115
+ }
17116
+ else {
17117
+ // No snap - use regular position
17118
+ const newEndX = this.initialEndX + localDx;
17119
+ const newEndY = this.initialEndY + localDy;
17120
+ this.updateLineEndpoint(line, this.initialStartX, this.initialStartY, newEndX, newEndY);
17121
+ // Clear snap indicator
17122
+ this._core.anchorManager.clearSnapCandidate();
17123
+ }
17124
+ }
17125
+ else if (handleType === 'center') {
17126
+ const startControlX = this.initialControlX ?? (this.initialStartX + this.initialEndX) / 2;
17127
+ const startControlY = this.initialControlY ?? (this.initialStartY + this.initialEndY) / 2;
17128
+ const newControlX = startControlX + localDx * 2;
17129
+ const newControlY = startControlY + localDy * 2;
17130
+ // Check distance to line segment for snapping back to straight
17131
+ const x1 = line.startX;
17132
+ const y1 = line.startY;
17133
+ const x2 = line.endX;
17134
+ const y2 = line.endY;
17135
+ const A = newControlX - x1;
17136
+ const B = newControlY - y1;
17137
+ const C = x2 - x1;
17138
+ const D = y2 - y1;
17139
+ const dot = A * C + B * D;
17140
+ const len_sq = C * C + D * D;
17141
+ let param = -1;
17142
+ if (len_sq !== 0) {
17143
+ param = dot / len_sq;
17144
+ }
17145
+ let xx, yy;
17146
+ if (param < 0) {
17147
+ xx = x1;
17148
+ yy = y1;
17149
+ }
17150
+ else if (param > 1) {
17151
+ xx = x2;
17152
+ yy = y2;
17153
+ }
17154
+ else {
17155
+ xx = x1 + param * C;
17156
+ yy = y1 + param * D;
17157
+ }
17158
+ const dx = newControlX - xx;
17159
+ const dy = newControlY - yy;
17160
+ const distance = Math.sqrt(dx * dx + dy * dy);
17161
+ // Threshold for snapping back to straight line
17162
+ const snapToStraightThreshold = 15;
17163
+ if (distance < snapToStraightThreshold) {
17164
+ line.updateControlPoint(undefined, undefined);
17165
+ }
17166
+ else {
17167
+ line.updateControlPoint(newControlX, newControlY);
17168
+ }
17169
+ }
17170
+ this._core.rerender();
17171
+ }
17172
+ /**
17173
+ * Converts world coordinates to a line's local coordinate system.
17174
+ */
17175
+ worldToLineLocal(line, worldX, worldY) {
17176
+ const dx = (worldX - line.translateX) * line.scale;
17177
+ const dy = (worldY - line.translateY) * line.scale;
17178
+ const cx = line.totalWidth / 2;
17179
+ const cy = line.totalHeight / 2;
17180
+ const cos = Math.cos(-line.rotation);
17181
+ const sin = Math.sin(-line.rotation);
17182
+ const rotatedX = (dx - cx) * cos - (dy - cy) * sin + cx;
17183
+ const rotatedY = (dx - cx) * sin + (dy - cy) * cos + cy;
17184
+ return {
17185
+ x: rotatedX + line.x,
17186
+ y: rotatedY + line.y,
17187
+ };
17188
+ }
17189
+ /**
17190
+ * Converts a line's local coordinate system to world coordinates.
17191
+ */
17192
+ lineLocalToWorld(line, localX, localY) {
17193
+ const px = localX - line.x;
17194
+ const py = localY - line.y;
17195
+ const cx = line.totalWidth / 2;
17196
+ const cy = line.totalHeight / 2;
17197
+ const cos = Math.cos(line.rotation);
17198
+ const sin = Math.sin(line.rotation);
17199
+ const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
17200
+ const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
17201
+ return {
17202
+ x: rotatedX / line.scale + line.translateX,
17203
+ y: rotatedY / line.scale + line.translateY,
17204
+ };
17205
+ }
17206
+ updateLineEndpoint(line, startX, startY, endX, endY) {
17207
+ // Calculate new bounding box using the static method that accounts for curve extrema
17208
+ const { minX, minY, maxX, maxY } = KritzelLine.calculateBoundingBox(startX, startY, endX, endY, line.controlX, line.controlY, line.strokeWidth);
17209
+ const newWidth = maxX - minX;
17210
+ const newHeight = maxY - minY;
17211
+ // Calculate old bounding box
17212
+ const { minX: oldMinX, minY: oldMinY, maxX: oldMaxX, maxY: oldMaxY, } = KritzelLine.calculateBoundingBox(this.initialStartX, this.initialStartY, this.initialEndX, this.initialEndY, this.initialControlX, this.initialControlY, line.strokeWidth);
17213
+ const oldWidth = oldMaxX - oldMinX;
17214
+ const oldHeight = oldMaxY - oldMinY;
17215
+ // Calculate center shift in local space
17216
+ const shiftX = minX - oldMinX;
17217
+ const shiftY = minY - oldMinY;
17218
+ const deltaCx = shiftX + (newWidth - oldWidth) / 2;
17219
+ const deltaCy = shiftY + (newHeight - oldHeight) / 2;
17220
+ // Convert the center shift to world coordinates
17221
+ const rotation = line.rotation;
17222
+ const cos = Math.cos(rotation);
17223
+ const sin = Math.sin(rotation);
17224
+ const scale = 1 / line.scale;
17225
+ const rotatedDeltaCx = deltaCx * cos - deltaCy * sin;
17226
+ const rotatedDeltaCy = deltaCx * sin + deltaCy * cos;
17227
+ // Calculate new translate
17228
+ const newTranslateX = this.initialTranslateX + scale * (oldWidth - newWidth) / 2 + scale * rotatedDeltaCx;
17229
+ const newTranslateY = this.initialTranslateY + scale * (oldHeight - newHeight) / 2 + scale * rotatedDeltaCy;
17230
+ // Update the line properties
17231
+ line.startX = startX;
17232
+ line.startY = startY;
17233
+ line.endX = endX;
17234
+ line.endY = endY;
17235
+ line.x = minX;
17236
+ line.y = minY;
17237
+ line.width = newWidth;
17238
+ line.height = newHeight;
17239
+ // Adjust translateX/Y to compensate for the bounding box shift
17240
+ line.translateX = newTranslateX;
17241
+ line.translateY = newTranslateY;
17242
+ // Clear cached adjusted points
17243
+ line._adjustedPoints = null;
17244
+ this._core.store.state.objects.update(line);
17245
+ }
17246
+ handlePointerUp(_event) {
17247
+ if (this._core.store.state.isLineHandleDragging) {
17248
+ const line = this.getSelectedLine();
17249
+ const handleType = this._core.store.state.lineHandleType;
17250
+ if (line && this.hasMoved) {
17251
+ // If we were snapped to a target, create the anchor
17252
+ if (this.currentSnapTarget && (handleType === 'start' || handleType === 'end')) {
17253
+ this._core.anchorManager.setAnchor(line.id, handleType, this.currentSnapTarget.objectId);
17254
+ }
17255
+ // Refresh the selection group dimensions after line endpoint change
17256
+ const selectionGroup = this._core.store.selectionGroup;
17257
+ if (selectionGroup) {
17258
+ selectionGroup.refreshObjectDimensions();
17259
+ selectionGroup.update();
17260
+ this._core.engine.emitObjectsChange();
17261
+ this._core.store.state.hasObjectsChanged = true;
17262
+ }
17263
+ }
17264
+ this._core.store.state.isLineHandleDragging = false;
17265
+ this.reset();
17266
+ this._core.rerender();
17267
+ }
17268
+ }
17269
+ getSelectedLine() {
17270
+ const selectionGroup = this._core.store.selectionGroup;
17271
+ if (!selectionGroup || selectionGroup.objects.length !== 1) {
17272
+ return null;
17273
+ }
17274
+ const object = selectionGroup.objects[0];
17275
+ if (object instanceof KritzelLine) {
17276
+ return object;
17277
+ }
17278
+ return null;
17279
+ }
17280
+ }
17281
+
16003
17282
  class KritzelSelectionTool extends KritzelBaseTool {
16004
17283
  selectionHandler;
16005
17284
  moveHandler;
16006
17285
  hoverHandler;
16007
17286
  resizeHandler;
16008
17287
  rotationHandler;
17288
+ lineHandleHandler;
16009
17289
  constructor(core) {
16010
17290
  super(core);
16011
17291
  this.selectionHandler = new KritzelSelectionHandler(this._core);
@@ -16013,6 +17293,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16013
17293
  this.hoverHandler = new KritzelHoverHandler(this._core);
16014
17294
  this.resizeHandler = new KritzelResizeHandler(this._core);
16015
17295
  this.rotationHandler = new KritzelRotationHandler(this._core);
17296
+ this.lineHandleHandler = new KritzelLineHandleHandler(this._core);
16016
17297
  }
16017
17298
  handlePointerDown(event) {
16018
17299
  if (event.cancelable) {
@@ -16022,14 +17303,17 @@ class KritzelSelectionTool extends KritzelBaseTool {
16022
17303
  if (KritzelEventHelper.isLeftClick(event)) {
16023
17304
  this._core.store.state.isResizeHandleSelected = this.isHandleSelected(event);
16024
17305
  this._core.store.state.isRotationHandleSelected = this.isRotationHandleSelected(event);
17306
+ this._core.store.state.isLineHandleSelected = this.isLineHandleSelected(event);
16025
17307
  this._core.store.state.resizeHandleType = this.getHandleType(event);
17308
+ this._core.store.state.lineHandleType = this.getLineHandleType(event);
16026
17309
  const selectedObject = this.getSelectedObject(event);
16027
17310
  const selectionGroup = this._core.store.selectionGroup;
16028
17311
  const isDifferentObject = selectedObject && selectionGroup && selectedObject.id !== selectionGroup.id;
16029
17312
  if ((selectedObject === null || isDifferentObject) &&
16030
17313
  selectionGroup &&
16031
17314
  !this._core.store.state.isResizeHandleSelected &&
16032
- !this._core.store.state.isRotationHandleSelected) {
17315
+ !this._core.store.state.isRotationHandleSelected &&
17316
+ !this._core.store.state.isLineHandleSelected) {
16033
17317
  this._core.removeSelectionGroup();
16034
17318
  this._core.rerender();
16035
17319
  }
@@ -16037,6 +17321,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16037
17321
  return;
16038
17322
  }
16039
17323
  }
17324
+ this.lineHandleHandler.handlePointerDown(event);
16040
17325
  this.moveHandler.handlePointerDown(event);
16041
17326
  this.selectionHandler.handlePointerDown(event);
16042
17327
  this.resizeHandler.handlePointerDown(event);
@@ -16050,7 +17335,9 @@ class KritzelSelectionTool extends KritzelBaseTool {
16050
17335
  if (this._core.store.state.pointers.size === 1) {
16051
17336
  this._core.store.state.isResizeHandleSelected = this.isHandleSelected(event);
16052
17337
  this._core.store.state.isRotationHandleSelected = this.isRotationHandleSelected(event);
17338
+ this._core.store.state.isLineHandleSelected = this.isLineHandleSelected(event);
16053
17339
  this._core.store.state.resizeHandleType = this.getHandleType(event);
17340
+ this._core.store.state.lineHandleType = this.getLineHandleType(event);
16054
17341
  const selectedObject = this.getSelectedObject(event);
16055
17342
  const selectionGroup = this._core.store.selectionGroup;
16056
17343
  const isDifferentObject = selectedObject && selectionGroup && selectedObject.id !== selectionGroup.id;
@@ -16060,11 +17347,13 @@ class KritzelSelectionTool extends KritzelBaseTool {
16060
17347
  if ((selectedObject === null || isDifferentObject) &&
16061
17348
  selectionGroup &&
16062
17349
  !this._core.store.state.isResizeHandleSelected &&
16063
- !this._core.store.state.isRotationHandleSelected) {
17350
+ !this._core.store.state.isRotationHandleSelected &&
17351
+ !this._core.store.state.isLineHandleSelected) {
16064
17352
  this._core.removeSelectionGroup();
16065
17353
  this._core.rerender();
16066
17354
  }
16067
17355
  }
17356
+ this.lineHandleHandler.handlePointerDown(event);
16068
17357
  this.rotationHandler.handlePointerDown(event);
16069
17358
  this.resizeHandler.handlePointerDown(event);
16070
17359
  this.moveHandler.handlePointerDown(event);
@@ -16077,6 +17366,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16077
17366
  event.preventDefault();
16078
17367
  }
16079
17368
  if (event.pointerType === 'mouse') {
17369
+ this.lineHandleHandler.handlePointerMove(event);
16080
17370
  this.moveHandler.handlePointerMove(event);
16081
17371
  this.hoverHandler.handlePointerMove(event);
16082
17372
  this.selectionHandler.handlePointerMove(event);
@@ -16088,6 +17378,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16088
17378
  if (this._core.store.state.isScaling === true) {
16089
17379
  return;
16090
17380
  }
17381
+ this.lineHandleHandler.handlePointerMove(event);
16091
17382
  this.rotationHandler.handlePointerMove(event);
16092
17383
  this.resizeHandler.handlePointerMove(event);
16093
17384
  this.moveHandler.handlePointerMove(event);
@@ -16100,6 +17391,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16100
17391
  event.preventDefault();
16101
17392
  }
16102
17393
  if (event.pointerType === 'mouse') {
17394
+ this.lineHandleHandler.handlePointerUp(event);
16103
17395
  this.moveHandler.handlePointerUp(event);
16104
17396
  this.resizeHandler.handlePointerUp(event);
16105
17397
  this.rotationHandler.handlePointerUp(event);
@@ -16110,6 +17402,7 @@ class KritzelSelectionTool extends KritzelBaseTool {
16110
17402
  if (this._core.store.state.isScaling === true) {
16111
17403
  return;
16112
17404
  }
17405
+ this.lineHandleHandler.handlePointerUp(event);
16113
17406
  this.rotationHandler.handlePointerUp(event);
16114
17407
  this.resizeHandler.handlePointerUp(event);
16115
17408
  this.moveHandler.handlePointerUp(event);
@@ -16139,20 +17432,38 @@ class KritzelSelectionTool extends KritzelBaseTool {
16139
17432
  const shadowRoot = this._core.store.state.host?.shadowRoot;
16140
17433
  if (!shadowRoot)
16141
17434
  return;
16142
- const elementAtPoint = shadowRoot.elementFromPoint(event.clientX, event.clientY);
16143
- const handle = elementAtPoint?.closest('.resize-handle-overlay');
17435
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
17436
+ const handle = elementsAtPoint.find(el => el.classList.contains('resize-handle-overlay'));
16144
17437
  return handle?.classList[1];
16145
17438
  }
16146
17439
  isHandleSelected(event) {
16147
17440
  const shadowRoot = this._core.store.state.host?.shadowRoot;
16148
17441
  if (!shadowRoot)
16149
17442
  return false;
16150
- const elementAtPoint = shadowRoot.elementFromPoint(event.clientX, event.clientY);
16151
- return !!elementAtPoint?.classList.contains('resize-handle-overlay');
17443
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
17444
+ return elementsAtPoint.some(el => el.classList.contains('resize-handle-overlay'));
17445
+ }
17446
+ isRotationHandleSelected(event) {
17447
+ const shadowRoot = this._core.store.state.host?.shadowRoot;
17448
+ if (!shadowRoot)
17449
+ return false;
17450
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
17451
+ return elementsAtPoint.some(el => el.classList.contains('rotation-handle-overlay'));
17452
+ }
17453
+ isLineHandleSelected(event) {
17454
+ const shadowRoot = this._core.store.state.host?.shadowRoot;
17455
+ if (!shadowRoot)
17456
+ return false;
17457
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
17458
+ return elementsAtPoint.some(el => el.classList.contains('selection-line-handle-overlay'));
16152
17459
  }
16153
- isRotationHandleSelected(event) {
16154
- const path = event.composedPath();
16155
- return !!path.find(element => element.classList && element.classList.contains('rotation-handle-overlay'));
17460
+ getLineHandleType(event) {
17461
+ const shadowRoot = this._core.store.state.host?.shadowRoot;
17462
+ if (!shadowRoot)
17463
+ return;
17464
+ const elementsAtPoint = shadowRoot.elementsFromPoint(event.clientX, event.clientY);
17465
+ const handle = elementsAtPoint.find(el => el.classList.contains('selection-line-handle-overlay'));
17466
+ return handle?.classList[1];
16156
17467
  }
16157
17468
  }
16158
17469
 
@@ -31201,6 +32512,881 @@ class KritzelAppStateMap {
31201
32512
  }
31202
32513
  }
31203
32514
 
32515
+ class KritzelClassHelper {
32516
+ static isInstanceOf(object, className) {
32517
+ return !!object && object.__class__ === className;
32518
+ }
32519
+ }
32520
+
32521
+ /**
32522
+ * Manages anchor connections between line endpoints and other objects.
32523
+ * Maintains a runtime index for efficient reverse lookups and handles
32524
+ * snap detection during line endpoint dragging.
32525
+ */
32526
+ class KritzelAnchorManager {
32527
+ _core;
32528
+ /**
32529
+ * Runtime index mapping objectId to the lines anchored to it.
32530
+ * This is derived from the anchor properties on lines and rebuilt as needed.
32531
+ */
32532
+ _anchorIndex = new Map();
32533
+ constructor(core) {
32534
+ this._core = core;
32535
+ }
32536
+ // ============================================
32537
+ // Anchor CRUD Operations
32538
+ // ============================================
32539
+ /**
32540
+ * Sets an anchor from a line endpoint to a target object's center.
32541
+ * Updates both the line's anchor property and the internal index.
32542
+ */
32543
+ setAnchor(lineId, endpoint, targetObjectId) {
32544
+ const line = this.getLineById(lineId);
32545
+ if (!line) {
32546
+ return;
32547
+ }
32548
+ // Prevent anchoring both endpoints to the same object
32549
+ if (endpoint === 'start' && line.endAnchor?.objectId === targetObjectId) {
32550
+ return;
32551
+ }
32552
+ if (endpoint === 'end' && line.startAnchor?.objectId === targetObjectId) {
32553
+ return;
32554
+ }
32555
+ // Remove existing anchor if any
32556
+ this.removeAnchor(lineId, endpoint);
32557
+ // Set the anchor on the line
32558
+ const anchor = { objectId: targetObjectId };
32559
+ if (endpoint === 'start') {
32560
+ line.startAnchor = anchor;
32561
+ }
32562
+ else {
32563
+ line.endAnchor = anchor;
32564
+ }
32565
+ // Update the index
32566
+ this.addToIndex(targetObjectId, lineId, endpoint);
32567
+ // Snap the endpoint to the target's center
32568
+ this.snapEndpointToObject(line, endpoint, targetObjectId);
32569
+ // Persist the change
32570
+ this._core.store.state.objects.update(line);
32571
+ }
32572
+ /**
32573
+ * Removes an anchor from a line endpoint.
32574
+ * Updates both the line's anchor property and the internal index.
32575
+ */
32576
+ removeAnchor(lineId, endpoint) {
32577
+ const line = this.getLineById(lineId);
32578
+ if (!line) {
32579
+ return;
32580
+ }
32581
+ const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
32582
+ if (!anchor) {
32583
+ return;
32584
+ }
32585
+ // Remove from index
32586
+ this.removeFromIndex(anchor.objectId, lineId, endpoint);
32587
+ // Clear the anchor on the line
32588
+ if (endpoint === 'start') {
32589
+ line.startAnchor = undefined;
32590
+ }
32591
+ else {
32592
+ line.endAnchor = undefined;
32593
+ }
32594
+ }
32595
+ /**
32596
+ * Gets the anchor for a specific line endpoint.
32597
+ */
32598
+ getAnchor(lineId, endpoint) {
32599
+ const line = this.getLineById(lineId);
32600
+ if (!line) {
32601
+ return null;
32602
+ }
32603
+ const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
32604
+ return anchor ?? null;
32605
+ }
32606
+ // ============================================
32607
+ // Query Operations
32608
+ // ============================================
32609
+ /**
32610
+ * Gets all lines that have an endpoint anchored to the specified object.
32611
+ */
32612
+ getLinesAnchoredTo(objectId) {
32613
+ const entries = this._anchorIndex.get(objectId);
32614
+ return entries ? Array.from(entries) : [];
32615
+ }
32616
+ // ============================================
32617
+ // Position Updates
32618
+ // ============================================
32619
+ /**
32620
+ * Updates all line endpoints that are anchored to the specified object.
32621
+ * Should be called when an object moves.
32622
+ */
32623
+ updateAnchorsForObject(objectId) {
32624
+ const entries = this.getLinesAnchoredTo(objectId);
32625
+ if (entries.length === 0) {
32626
+ return;
32627
+ }
32628
+ const targetObject = this.getObjectById(objectId);
32629
+ if (!targetObject) {
32630
+ return;
32631
+ }
32632
+ for (const entry of entries) {
32633
+ const line = this.getLineById(entry.lineId);
32634
+ if (!line) {
32635
+ continue;
32636
+ }
32637
+ this.snapEndpointToObject(line, entry.endpoint, objectId);
32638
+ }
32639
+ }
32640
+ /**
32641
+ * Snaps a line endpoint to an object's center.
32642
+ */
32643
+ snapEndpointToObject(line, endpoint, targetObjectId) {
32644
+ const targetObject = this.getObjectById(targetObjectId);
32645
+ if (!targetObject) {
32646
+ return;
32647
+ }
32648
+ // Get target center in world coordinates
32649
+ const targetCenterX = targetObject.centerX;
32650
+ const targetCenterY = targetObject.centerY;
32651
+ // Convert world coordinates to line's local coordinate system
32652
+ const localCoords = this.worldToLineLocal(line, targetCenterX, targetCenterY);
32653
+ // Update the endpoint
32654
+ line.updateEndpoint(endpoint, localCoords.x, localCoords.y);
32655
+ }
32656
+ /**
32657
+ * Converts world coordinates to a line's local coordinate system.
32658
+ */
32659
+ worldToLineLocal(line, worldX, worldY) {
32660
+ // Get line center in world coordinates
32661
+ const cx = line.centerX;
32662
+ const cy = line.centerY;
32663
+ // Translate to center
32664
+ const dx = worldX - cx;
32665
+ const dy = worldY - cy;
32666
+ // Apply inverse rotation
32667
+ const cos = Math.cos(-line.rotation);
32668
+ const sin = Math.sin(-line.rotation);
32669
+ const rotatedX = dx * cos - dy * sin;
32670
+ const rotatedY = dx * sin + dy * cos;
32671
+ // Calculate local coordinates relative to the unrotated bounding box top-left
32672
+ const relativeX = rotatedX + (line.totalWidth / 2) / line.scale;
32673
+ const relativeY = rotatedY + (line.totalHeight / 2) / line.scale;
32674
+ // Convert to internal coordinates
32675
+ const localX = relativeX * line.scale + line.x;
32676
+ const localY = relativeY * line.scale + line.y;
32677
+ return { x: localX, y: localY };
32678
+ }
32679
+ // ============================================
32680
+ // Snap Detection
32681
+ // ============================================
32682
+ /**
32683
+ * Finds the nearest object that can be snapped to within the snap threshold.
32684
+ * Returns null if no suitable snap target is found.
32685
+ *
32686
+ * @param worldX - X coordinate in world space
32687
+ * @param worldY - Y coordinate in world space
32688
+ * @param excludeLineId - ID of the line being edited (to exclude from snap targets)
32689
+ * @param otherEndpointAnchorId - ID of object anchored to the other endpoint (to prevent same-object anchoring)
32690
+ */
32691
+ findSnapTarget(worldX, worldY, excludeLineId, otherEndpointAnchorId) {
32692
+ let bestTarget = null;
32693
+ let highestZIndex = -Infinity;
32694
+ const objects = this._core.store.allNonSelectionObjects;
32695
+ for (const object of objects) {
32696
+ // Skip the line being edited
32697
+ if (object.id === excludeLineId) {
32698
+ continue;
32699
+ }
32700
+ // Skip if this is the object anchored to the other endpoint
32701
+ if (otherEndpointAnchorId && object.id === otherEndpointAnchorId) {
32702
+ continue;
32703
+ }
32704
+ // Skip non-anchorable objects
32705
+ if (!this.isAnchorable(object)) {
32706
+ continue;
32707
+ }
32708
+ // Check if point is inside the object's rotated polygon
32709
+ const polygon = object.rotatedPolygon;
32710
+ const points = [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
32711
+ if (KritzelGeometryHelper.isPointInPolygon({ x: worldX, y: worldY }, points)) {
32712
+ // If inside, check if this object is "above" the current best target
32713
+ if (object.zIndex > highestZIndex) {
32714
+ highestZIndex = object.zIndex;
32715
+ bestTarget = {
32716
+ objectId: object.id,
32717
+ centerX: object.centerX,
32718
+ centerY: object.centerY,
32719
+ };
32720
+ }
32721
+ }
32722
+ }
32723
+ return bestTarget;
32724
+ }
32725
+ /**
32726
+ * Sets the current snap candidate for visual feedback.
32727
+ */
32728
+ setSnapCandidate(candidate) {
32729
+ this._core.store.state.snapCandidate = candidate;
32730
+ this._core.rerender();
32731
+ }
32732
+ /**
32733
+ * Gets the current snap candidate.
32734
+ */
32735
+ getSnapCandidate() {
32736
+ return this._core.store.state.snapCandidate ?? null;
32737
+ }
32738
+ /**
32739
+ * Clears the snap candidate.
32740
+ */
32741
+ clearSnapCandidate() {
32742
+ this._core.store.state.snapCandidate = null;
32743
+ this._core.rerender();
32744
+ }
32745
+ // ============================================
32746
+ // Render Data
32747
+ // ============================================
32748
+ /**
32749
+ * Gets render data for anchor lines visualization.
32750
+ * Returns null if there's no selected line with anchors or if snap is in progress.
32751
+ */
32752
+ getAnchorLinesRenderData() {
32753
+ const selectionGroup = this._core.store.selectionGroup;
32754
+ if (!selectionGroup || selectionGroup.objects.length !== 1)
32755
+ return null;
32756
+ const selectedObject = selectionGroup.objects[0];
32757
+ if (!KritzelClassHelper.isInstanceOf(selectedObject, 'KritzelLine'))
32758
+ return null;
32759
+ const line = selectedObject;
32760
+ const startAnchorViz = this.computeAnchorVisualization(line, 'start');
32761
+ const endAnchorViz = this.computeAnchorVisualization(line, 'end');
32762
+ if (!startAnchorViz && !endAnchorViz)
32763
+ return null;
32764
+ const scale = this._core.store.state.scale;
32765
+ const lineStrokeWidth = line.strokeWidth / line.scale;
32766
+ const indicatorStrokeWidth = `${2 / scale}`;
32767
+ const dashLength = Math.max(lineStrokeWidth * 2, 4 / scale);
32768
+ const dashArray = `${dashLength} ${dashLength}`;
32769
+ const indicatorRadius = 8 / scale;
32770
+ return {
32771
+ lineStrokeWidth,
32772
+ indicatorStrokeWidth,
32773
+ dashArray,
32774
+ indicatorRadius,
32775
+ startAnchorViz,
32776
+ endAnchorViz,
32777
+ };
32778
+ }
32779
+ /**
32780
+ * Gets render data for snap indicator visualization.
32781
+ * Returns null if there's no snap candidate.
32782
+ */
32783
+ getSnapIndicatorRenderData() {
32784
+ const snapCandidate = this.getSnapCandidate();
32785
+ if (!snapCandidate)
32786
+ return null;
32787
+ const scale = this._core.store.state.scale;
32788
+ const indicatorRadius = 8 / scale;
32789
+ const indicatorStrokeWidth = `${2 / scale}`;
32790
+ const lineStrokeWidth = snapCandidate.lineStrokeWidth
32791
+ ? `${snapCandidate.lineStrokeWidth}`
32792
+ : `${4 / scale}`;
32793
+ const lineStrokeWidthNum = snapCandidate.lineStrokeWidth || (4 / scale);
32794
+ const dashLength = Math.max(lineStrokeWidthNum * 2, 4 / scale);
32795
+ const dashArray = `${dashLength} ${dashLength}`;
32796
+ const lineStroke = snapCandidate.lineStroke || '#000000';
32797
+ let solidLineEndX = snapCandidate.edgeX;
32798
+ let solidLineEndY = snapCandidate.edgeY;
32799
+ let arrowPoints;
32800
+ if (snapCandidate.arrowOffset && snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
32801
+ const dx = snapCandidate.lineEndpointX - snapCandidate.edgeX;
32802
+ const dy = snapCandidate.lineEndpointY - snapCandidate.edgeY;
32803
+ const length = Math.sqrt(dx * dx + dy * dy);
32804
+ if (length > snapCandidate.arrowOffset) {
32805
+ solidLineEndX = snapCandidate.edgeX + (dx / length) * snapCandidate.arrowOffset;
32806
+ solidLineEndY = snapCandidate.edgeY + (dy / length) * snapCandidate.arrowOffset;
32807
+ }
32808
+ // Calculate arrow head points
32809
+ // Direction from line endpoint to edge (arrow direction)
32810
+ const arrowDx = snapCandidate.edgeX - snapCandidate.lineEndpointX;
32811
+ const arrowDy = snapCandidate.edgeY - snapCandidate.lineEndpointY;
32812
+ const arrowLengthTotal = Math.sqrt(arrowDx * arrowDx + arrowDy * arrowDy);
32813
+ if (arrowLengthTotal > 0) {
32814
+ const ux = arrowDx / arrowLengthTotal;
32815
+ const uy = arrowDy / arrowLengthTotal;
32816
+ // Perpendicular vector
32817
+ const px = -uy;
32818
+ const py = ux;
32819
+ // Arrow dimensions
32820
+ const arrowLength = snapCandidate.arrowOffset;
32821
+ const arrowWidth = arrowLength; // 1:1 ratio
32822
+ // Arrow tip at edge
32823
+ const tipX = snapCandidate.edgeX;
32824
+ const tipY = snapCandidate.edgeY;
32825
+ // Arrow base
32826
+ const baseX = tipX - ux * arrowLength;
32827
+ const baseY = tipY - uy * arrowLength;
32828
+ // Arrow wings
32829
+ const leftX = baseX + px * arrowWidth / 2;
32830
+ const leftY = baseY + py * arrowWidth / 2;
32831
+ const rightX = baseX - px * arrowWidth / 2;
32832
+ const rightY = baseY - py * arrowWidth / 2;
32833
+ arrowPoints = `${tipX},${tipY} ${leftX},${leftY} ${rightX},${rightY}`;
32834
+ }
32835
+ }
32836
+ return {
32837
+ indicatorRadius,
32838
+ indicatorStrokeWidth,
32839
+ lineStrokeWidth,
32840
+ dashArray,
32841
+ lineStroke,
32842
+ centerX: snapCandidate.centerX,
32843
+ centerY: snapCandidate.centerY,
32844
+ lineEndpointX: snapCandidate.lineEndpointX,
32845
+ lineEndpointY: snapCandidate.lineEndpointY,
32846
+ edgeX: snapCandidate.edgeX,
32847
+ edgeY: snapCandidate.edgeY,
32848
+ arrowOffset: snapCandidate.arrowOffset,
32849
+ arrowStyle: snapCandidate.arrowStyle,
32850
+ arrowFill: snapCandidate.arrowFill,
32851
+ solidLineEndX,
32852
+ solidLineEndY,
32853
+ arrowPoints,
32854
+ snapLinePath: (() => {
32855
+ if (snapCandidate.controlX !== undefined &&
32856
+ snapCandidate.controlY !== undefined &&
32857
+ snapCandidate.t !== undefined) {
32858
+ const startT = snapCandidate.endpoint === 'start' ? 1 - snapCandidate.t : snapCandidate.t;
32859
+ // Ensure meaningful range
32860
+ if (startT >= 1)
32861
+ return undefined;
32862
+ const segment = this.extractQuadraticSegment({ x: snapCandidate.lineEndpointX, y: snapCandidate.lineEndpointY }, { x: snapCandidate.controlX, y: snapCandidate.controlY }, { x: snapCandidate.centerX, y: snapCandidate.centerY }, startT, 1);
32863
+ return `M ${segment.start.x} ${segment.start.y} Q ${segment.control.x} ${segment.control.y} ${segment.end.x} ${segment.end.y}`;
32864
+ }
32865
+ return undefined;
32866
+ })(),
32867
+ };
32868
+ }
32869
+ // ============================================
32870
+ // Cleanup Operations
32871
+ // ============================================
32872
+ /**
32873
+ * Handles cleanup when an object is deleted.
32874
+ * Detaches all lines that were anchored to the deleted object.
32875
+ */
32876
+ handleObjectDeleted(objectId) {
32877
+ const entries = this.getLinesAnchoredTo(objectId);
32878
+ for (const entry of entries) {
32879
+ this.removeAnchor(entry.lineId, entry.endpoint);
32880
+ // Update the line to persist the change
32881
+ const line = this.getLineById(entry.lineId);
32882
+ if (line) {
32883
+ this._core.store.state.objects.update(line);
32884
+ }
32885
+ }
32886
+ // Remove the object from the index
32887
+ this._anchorIndex.delete(objectId);
32888
+ }
32889
+ /**
32890
+ * Handles cleanup when a line is deleted.
32891
+ * Removes all anchor index entries for the line.
32892
+ */
32893
+ handleLineDeleted(lineId) {
32894
+ const line = this.getLineById(lineId);
32895
+ if (!line) {
32896
+ return;
32897
+ }
32898
+ if (line.startAnchor) {
32899
+ this.removeFromIndex(line.startAnchor.objectId, lineId, 'start');
32900
+ }
32901
+ if (line.endAnchor) {
32902
+ this.removeFromIndex(line.endAnchor.objectId, lineId, 'end');
32903
+ }
32904
+ }
32905
+ // ============================================
32906
+ // Index Management
32907
+ // ============================================
32908
+ /**
32909
+ * Rebuilds the anchor index from all lines in the object map.
32910
+ * Should be called after loading/deserializing objects.
32911
+ */
32912
+ rebuildIndex() {
32913
+ this._anchorIndex.clear();
32914
+ const objects = this._core.store.allObjects;
32915
+ for (const object of objects) {
32916
+ if (object instanceof KritzelLine) {
32917
+ if (object.startAnchor) {
32918
+ this.addToIndex(object.startAnchor.objectId, object.id, 'start');
32919
+ }
32920
+ if (object.endAnchor) {
32921
+ this.addToIndex(object.endAnchor.objectId, object.id, 'end');
32922
+ }
32923
+ }
32924
+ }
32925
+ }
32926
+ /**
32927
+ * Adds an entry to the anchor index.
32928
+ */
32929
+ addToIndex(objectId, lineId, endpoint) {
32930
+ if (!this._anchorIndex.has(objectId)) {
32931
+ this._anchorIndex.set(objectId, new Set());
32932
+ }
32933
+ const entries = this._anchorIndex.get(objectId);
32934
+ // Check if entry already exists
32935
+ for (const entry of entries) {
32936
+ if (entry.lineId === lineId && entry.endpoint === endpoint) {
32937
+ return;
32938
+ }
32939
+ }
32940
+ entries.add({ lineId, endpoint });
32941
+ }
32942
+ /**
32943
+ * Removes an entry from the anchor index.
32944
+ */
32945
+ removeFromIndex(objectId, lineId, endpoint) {
32946
+ const entries = this._anchorIndex.get(objectId);
32947
+ if (!entries) {
32948
+ return;
32949
+ }
32950
+ for (const entry of entries) {
32951
+ if (entry.lineId === lineId && entry.endpoint === endpoint) {
32952
+ entries.delete(entry);
32953
+ break;
32954
+ }
32955
+ }
32956
+ // Clean up empty sets
32957
+ if (entries.size === 0) {
32958
+ this._anchorIndex.delete(objectId);
32959
+ }
32960
+ }
32961
+ // ============================================
32962
+ // Helper Methods
32963
+ // ============================================
32964
+ /**
32965
+ * Gets a line by its ID.
32966
+ */
32967
+ getLineById(lineId) {
32968
+ const objects = this._core.store.state.objects.filter(o => o.id === lineId);
32969
+ if (objects.length === 0) {
32970
+ return null;
32971
+ }
32972
+ const object = objects[0];
32973
+ return object instanceof KritzelLine ? object : null;
32974
+ }
32975
+ /**
32976
+ * Gets an object by its ID.
32977
+ */
32978
+ getObjectById(objectId) {
32979
+ const objects = this._core.store.state.objects.filter(o => o.id === objectId);
32980
+ return objects.length > 0 ? objects[0] : null;
32981
+ }
32982
+ findAnchorTarget(line, endpoint) {
32983
+ const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
32984
+ if (!anchor) {
32985
+ return null;
32986
+ }
32987
+ return this._core.store.allNonSelectionObjects.find(obj => obj.id === anchor.objectId) ?? null;
32988
+ }
32989
+ // ============================================
32990
+ // Visualization Helpers
32991
+ // ============================================
32992
+ /**
32993
+ * Computes anchor visualization data for a line with anchors.
32994
+ * Returns edge intersection points for rendering dashed lines from edge to center.
32995
+ */
32996
+ computeAnchorVisualization(line, endpoint) {
32997
+ const anchor = endpoint === 'start' ? line.startAnchor : line.endAnchor;
32998
+ if (!anchor)
32999
+ return null;
33000
+ const targetObject = this.findAnchorTarget(line, endpoint);
33001
+ if (!targetObject)
33002
+ return null;
33003
+ const clipInfo = this.computeAnchorClipInfo(line, endpoint, targetObject);
33004
+ if (!clipInfo)
33005
+ return null;
33006
+ const centerX = targetObject.centerX;
33007
+ const centerY = targetObject.centerY;
33008
+ return {
33009
+ edgeX: clipInfo.worldX,
33010
+ edgeY: clipInfo.worldY,
33011
+ centerX,
33012
+ centerY,
33013
+ pathD: this.buildAnchorPath(line, endpoint, clipInfo, targetObject) ?? undefined,
33014
+ };
33015
+ }
33016
+ /**
33017
+ * Computes a clipped line path that stops at anchor edges instead of going to anchor centers.
33018
+ * When arrows are present on anchored ends, the line is shortened so the arrow tip appears at the edge.
33019
+ * Returns the SVG path 'd' attribute string, or the original path if no clipping is needed.
33020
+ * @param line The line object
33021
+ * @param offsetByViewBox If true, subtracts line.x and line.y from coordinates (for selection UI)
33022
+ */
33023
+ computeClippedLinePath(line, offsetByViewBox = false) {
33024
+ // Check for snap candidate first - this applies during drag operations even without anchors
33025
+ const snapCandidate = this.getSnapCandidate();
33026
+ const selectionGroup = this._core.store.selectionGroup;
33027
+ const hasActiveSnapCandidate = snapCandidate && selectionGroup && selectionGroup.objects.length === 1 && selectionGroup.objects[0].id === line.id;
33028
+ // If no anchors and no active snap candidate, return original path (with or without offset)
33029
+ if (!line.startAnchor && !line.endAnchor && !hasActiveSnapCandidate) {
33030
+ if (offsetByViewBox) {
33031
+ if (line.controlX !== undefined && line.controlY !== undefined) {
33032
+ return `M ${line.startX - line.x} ${line.startY - line.y} Q ${line.controlX - line.x} ${line.controlY - line.y} ${line.endX - line.x} ${line.endY - line.y}`;
33033
+ }
33034
+ return `M ${line.startX - line.x} ${line.startY - line.y} L ${line.endX - line.x} ${line.endY - line.y}`;
33035
+ }
33036
+ return line.d;
33037
+ }
33038
+ const startTarget = line.startAnchor ? this.findAnchorTarget(line, 'start') : null;
33039
+ const endTarget = line.endAnchor ? this.findAnchorTarget(line, 'end') : null;
33040
+ let startClip = startTarget ? this.computeAnchorClipInfo(line, 'start', startTarget) : null;
33041
+ let endClip = endTarget ? this.computeAnchorClipInfo(line, 'end', endTarget) : null;
33042
+ // Apply snap candidate if present and matches this line
33043
+ if (hasActiveSnapCandidate) {
33044
+ if (snapCandidate.edgeX !== undefined && snapCandidate.edgeY !== undefined) {
33045
+ const localEdge = this.lineWorldToLocal(line, snapCandidate.edgeX, snapCandidate.edgeY);
33046
+ const clipInfo = {
33047
+ worldX: snapCandidate.edgeX,
33048
+ worldY: snapCandidate.edgeY,
33049
+ localX: localEdge.x,
33050
+ localY: localEdge.y,
33051
+ t: snapCandidate.t
33052
+ };
33053
+ if (snapCandidate.endpoint === 'start') {
33054
+ startClip = clipInfo;
33055
+ }
33056
+ else {
33057
+ endClip = clipInfo;
33058
+ }
33059
+ }
33060
+ }
33061
+ // Apply offset if requested (for selection UI which uses coordinates relative to 0,0)
33062
+ const offsetX = offsetByViewBox ? line.x : 0;
33063
+ const offsetY = offsetByViewBox ? line.y : 0;
33064
+ // Handle curved lines by splitting the quadratic at the clipped parameters
33065
+ if (line.controlX !== undefined && line.controlY !== undefined) {
33066
+ let startT = startClip?.t ?? 0;
33067
+ let endT = endClip?.t ?? 1;
33068
+ // Adjust for start arrow
33069
+ if (startClip && line.hasStartArrow) {
33070
+ const arrowOffset = line.getArrowSize('start');
33071
+ const speed = this.evaluateDerivativeSpeedAtT(line, startT);
33072
+ if (speed > 0) {
33073
+ startT += arrowOffset / speed;
33074
+ }
33075
+ }
33076
+ // Adjust for end arrow
33077
+ if (endClip && line.hasEndArrow) {
33078
+ const arrowOffset = line.getArrowSize('end');
33079
+ const speed = this.evaluateDerivativeSpeedAtT(line, endT);
33080
+ if (speed > 0) {
33081
+ endT -= arrowOffset / speed;
33082
+ }
33083
+ }
33084
+ // Ensure valid range
33085
+ if (startT < 0)
33086
+ startT = 0;
33087
+ if (endT > 1)
33088
+ endT = 1;
33089
+ // Handle overlap or invalid range
33090
+ if (endT <= startT) {
33091
+ // If the arrow adjustment caused them to cross, or they were already crossed/close
33092
+ // We can either effectively hide the line, or just clamp them.
33093
+ // If we clamp them to the midpoint, the line becomes a point.
33094
+ // Let's ensure a minimal gap or just set them equal.
33095
+ const mid = (startT + endT) / 2;
33096
+ startT = mid;
33097
+ endT = mid;
33098
+ }
33099
+ const segment = this.extractQuadraticSegment({ x: line.startX, y: line.startY }, { x: line.controlX, y: line.controlY }, { x: line.endX, y: line.endY }, startT, endT);
33100
+ return `M ${segment.start.x - offsetX} ${segment.start.y - offsetY} Q ${segment.control.x - offsetX} ${segment.control.y - offsetY} ${segment.end.x - offsetX} ${segment.end.y - offsetY}`;
33101
+ }
33102
+ // Straight lines fall back to linear interpolation
33103
+ let startX = startClip?.localX ?? line.startX;
33104
+ let startY = startClip?.localY ?? line.startY;
33105
+ let endX = endClip?.localX ?? line.endX;
33106
+ let endY = endClip?.localY ?? line.endY;
33107
+ if (startClip && line.hasStartArrow) {
33108
+ const arrowOffset = line.getArrowSize('start');
33109
+ const dx = endX - startX;
33110
+ const dy = endY - startY;
33111
+ const length = Math.sqrt(dx * dx + dy * dy);
33112
+ if (length > arrowOffset) {
33113
+ startX += (dx / length) * arrowOffset;
33114
+ startY += (dy / length) * arrowOffset;
33115
+ }
33116
+ }
33117
+ if (endClip && line.hasEndArrow) {
33118
+ const arrowOffset = line.getArrowSize('end');
33119
+ const dx = startX - endX;
33120
+ const dy = startY - endY;
33121
+ const length = Math.sqrt(dx * dx + dy * dy);
33122
+ if (length > arrowOffset) {
33123
+ endX += (dx / length) * arrowOffset;
33124
+ endY += (dy / length) * arrowOffset;
33125
+ }
33126
+ }
33127
+ return `M ${startX - offsetX} ${startY - offsetY} L ${endX - offsetX} ${endY - offsetY}`;
33128
+ }
33129
+ computeAnchorClipInfo(line, endpoint, targetObject) {
33130
+ if (line.controlX !== undefined && line.controlY !== undefined) {
33131
+ return this.computeCurvedClipInfo(line, endpoint, targetObject);
33132
+ }
33133
+ return this.computeStraightClipInfo(line, endpoint, targetObject);
33134
+ }
33135
+ computeStraightClipInfo(line, endpoint, targetObject) {
33136
+ const reference = endpoint === 'start'
33137
+ ? this.lineLocalToWorld(line, line.endX, line.endY)
33138
+ : this.lineLocalToWorld(line, line.startX, line.startY);
33139
+ const edgeIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33140
+ if (!edgeIntersection) {
33141
+ return null;
33142
+ }
33143
+ const localEdge = this.lineWorldToLocal(line, edgeIntersection.x, edgeIntersection.y);
33144
+ const totalLength = Math.sqrt(Math.pow(line.endX - line.startX, 2) +
33145
+ Math.pow(line.endY - line.startY, 2));
33146
+ const distanceFromStart = Math.sqrt(Math.pow(localEdge.x - line.startX, 2) +
33147
+ Math.pow(localEdge.y - line.startY, 2));
33148
+ const t = totalLength > 0 ? distanceFromStart / totalLength : endpoint === 'start' ? 0 : 1;
33149
+ return {
33150
+ localX: localEdge.x,
33151
+ localY: localEdge.y,
33152
+ worldX: edgeIntersection.x,
33153
+ worldY: edgeIntersection.y,
33154
+ t,
33155
+ };
33156
+ }
33157
+ computeCurvedClipInfo(line, endpoint, targetObject) {
33158
+ const polygonPoints = this.getPolygonPoints(targetObject.rotatedPolygon);
33159
+ const exitPoint = this.findCurveExitPoint(line, endpoint, polygonPoints);
33160
+ if (exitPoint) {
33161
+ return exitPoint;
33162
+ }
33163
+ const reference = endpoint === 'start'
33164
+ ? this.lineLocalToWorld(line, line.endX, line.endY)
33165
+ : this.lineLocalToWorld(line, line.startX, line.startY);
33166
+ const fallbackIntersection = KritzelGeometryHelper.getLinePolygonIntersection(reference, { x: targetObject.centerX, y: targetObject.centerY }, targetObject.rotatedPolygon);
33167
+ if (!fallbackIntersection) {
33168
+ return null;
33169
+ }
33170
+ const localEdge = this.lineWorldToLocal(line, fallbackIntersection.x, fallbackIntersection.y);
33171
+ const approxT = this.approximateParameterForWorldPoint(line, fallbackIntersection);
33172
+ return {
33173
+ localX: localEdge.x,
33174
+ localY: localEdge.y,
33175
+ worldX: fallbackIntersection.x,
33176
+ worldY: fallbackIntersection.y,
33177
+ t: approxT,
33178
+ };
33179
+ }
33180
+ findCurveExitPoint(line, endpoint, polygonPoints) {
33181
+ const steps = 64;
33182
+ const initialT = endpoint === 'start' ? 0 : 1;
33183
+ const initialSample = this.evaluateLineAtT(line, initialT);
33184
+ let prevInside = KritzelGeometryHelper.isPointInPolygon({ x: initialSample.worldX, y: initialSample.worldY }, polygonPoints);
33185
+ let prevT = initialT;
33186
+ for (let i = 1; i <= steps; i++) {
33187
+ const t = endpoint === 'start' ? i / steps : 1 - i / steps;
33188
+ const sample = this.evaluateLineAtT(line, t);
33189
+ const inside = KritzelGeometryHelper.isPointInPolygon({ x: sample.worldX, y: sample.worldY }, polygonPoints);
33190
+ if (prevInside && !inside) {
33191
+ const refinedT = this.refineCurveExitParameter(line, polygonPoints, prevT, t);
33192
+ const refinedPoint = this.evaluateLineAtT(line, refinedT);
33193
+ return {
33194
+ localX: refinedPoint.localX,
33195
+ localY: refinedPoint.localY,
33196
+ worldX: refinedPoint.worldX,
33197
+ worldY: refinedPoint.worldY,
33198
+ t: refinedT,
33199
+ };
33200
+ }
33201
+ prevInside = inside;
33202
+ prevT = t;
33203
+ }
33204
+ return null;
33205
+ }
33206
+ refineCurveExitParameter(line, polygonPoints, insideT, outsideT) {
33207
+ let tInside = insideT;
33208
+ let tOutside = outsideT;
33209
+ for (let i = 0; i < 8; i++) {
33210
+ const mid = (tInside + tOutside) / 2;
33211
+ const sample = this.evaluateLineAtT(line, mid);
33212
+ const inside = KritzelGeometryHelper.isPointInPolygon({ x: sample.worldX, y: sample.worldY }, polygonPoints);
33213
+ if (inside) {
33214
+ tInside = mid;
33215
+ }
33216
+ else {
33217
+ tOutside = mid;
33218
+ }
33219
+ }
33220
+ return (tInside + tOutside) / 2;
33221
+ }
33222
+ approximateParameterForWorldPoint(line, target) {
33223
+ const steps = 80;
33224
+ let bestT = 0;
33225
+ let bestDistance = Infinity;
33226
+ for (let i = 0; i <= steps; i++) {
33227
+ const t = i / steps;
33228
+ const sample = this.evaluateLineAtT(line, t);
33229
+ const distance = Math.hypot(sample.worldX - target.x, sample.worldY - target.y);
33230
+ if (distance < bestDistance) {
33231
+ bestDistance = distance;
33232
+ bestT = t;
33233
+ }
33234
+ }
33235
+ return bestT;
33236
+ }
33237
+ evaluateLineAtT(line, t) {
33238
+ const clampedT = Math.max(0, Math.min(1, t));
33239
+ let localX;
33240
+ let localY;
33241
+ if (line.controlX !== undefined && line.controlY !== undefined) {
33242
+ const oneMinusT = 1 - clampedT;
33243
+ localX = oneMinusT * oneMinusT * line.startX + 2 * oneMinusT * clampedT * line.controlX + clampedT * clampedT * line.endX;
33244
+ localY = oneMinusT * oneMinusT * line.startY + 2 * oneMinusT * clampedT * line.controlY + clampedT * clampedT * line.endY;
33245
+ }
33246
+ else {
33247
+ localX = line.startX + (line.endX - line.startX) * clampedT;
33248
+ localY = line.startY + (line.endY - line.startY) * clampedT;
33249
+ }
33250
+ const world = this.lineLocalToWorld(line, localX, localY);
33251
+ return { t: clampedT, localX, localY, worldX: world.x, worldY: world.y };
33252
+ }
33253
+ evaluateDerivativeSpeedAtT(line, t) {
33254
+ const clampedT = Math.max(0, Math.min(1, t));
33255
+ if (line.controlX !== undefined && line.controlY !== undefined) {
33256
+ // Quadratic Bezier derivative: B'(t) = 2(1-t)(P1-P0) + 2t(P2-P1)
33257
+ const p0x = line.startX;
33258
+ const p0y = line.startY;
33259
+ const p1x = line.controlX;
33260
+ const p1y = line.controlY;
33261
+ const p2x = line.endX;
33262
+ const p2y = line.endY;
33263
+ const dx = 2 * (1 - clampedT) * (p1x - p0x) + 2 * clampedT * (p2x - p1x);
33264
+ const dy = 2 * (1 - clampedT) * (p1y - p0y) + 2 * clampedT * (p2y - p1y);
33265
+ return Math.sqrt(dx * dx + dy * dy);
33266
+ }
33267
+ else {
33268
+ // Straight line speed is constant length
33269
+ const dx = line.endX - line.startX;
33270
+ const dy = line.endY - line.startY;
33271
+ return Math.sqrt(dx * dx + dy * dy);
33272
+ }
33273
+ }
33274
+ extractQuadraticSegment(start, control, end, tStart, tEnd) {
33275
+ let normalizedStart = Math.max(0, Math.min(1, tStart));
33276
+ let normalizedEnd = Math.max(0, Math.min(1, tEnd));
33277
+ if (normalizedEnd < normalizedStart) {
33278
+ const swap = normalizedStart;
33279
+ normalizedStart = normalizedEnd;
33280
+ normalizedEnd = swap;
33281
+ }
33282
+ let segment = { start, control, end };
33283
+ if (normalizedStart > 0) {
33284
+ const split = this.splitQuadraticSegment(segment, normalizedStart);
33285
+ segment = split.right;
33286
+ const remainingRange = 1 - normalizedStart;
33287
+ normalizedEnd = remainingRange > 0 ? (normalizedEnd - normalizedStart) / remainingRange : 1;
33288
+ }
33289
+ if (normalizedEnd < 1) {
33290
+ const split = this.splitQuadraticSegment(segment, normalizedEnd);
33291
+ segment = split.left;
33292
+ }
33293
+ return segment;
33294
+ }
33295
+ splitQuadraticSegment(segment, t) {
33296
+ const clampedT = Math.max(0, Math.min(1, t));
33297
+ const p0 = segment.start;
33298
+ const p1 = segment.control;
33299
+ const p2 = segment.end;
33300
+ const p01 = this.lerpPoint(p0, p1, clampedT);
33301
+ const p12 = this.lerpPoint(p1, p2, clampedT);
33302
+ const p012 = this.lerpPoint(p01, p12, clampedT);
33303
+ return {
33304
+ left: { start: p0, control: p01, end: p012 },
33305
+ right: { start: p012, control: p12, end: p2 },
33306
+ };
33307
+ }
33308
+ lerpPoint(a, b, t) {
33309
+ return {
33310
+ x: a.x + (b.x - a.x) * t,
33311
+ y: a.y + (b.y - a.y) * t,
33312
+ };
33313
+ }
33314
+ buildAnchorPath(line, endpoint, clipInfo, targetObject) {
33315
+ if (line.controlX === undefined || line.controlY === undefined || clipInfo.t === undefined) {
33316
+ return `M ${clipInfo.worldX} ${clipInfo.worldY} L ${targetObject.centerX} ${targetObject.centerY}`;
33317
+ }
33318
+ const startT = endpoint === 'start' ? 0 : clipInfo.t;
33319
+ const endT = endpoint === 'start' ? clipInfo.t : 1;
33320
+ if (endT <= startT) {
33321
+ return `M ${clipInfo.worldX} ${clipInfo.worldY} L ${targetObject.centerX} ${targetObject.centerY}`;
33322
+ }
33323
+ const segment = this.extractQuadraticSegment({ x: line.startX, y: line.startY }, { x: line.controlX, y: line.controlY }, { x: line.endX, y: line.endY }, startT, endT);
33324
+ const reverse = endpoint === 'start';
33325
+ return this.buildWorldQuadraticPath(line, segment, reverse);
33326
+ }
33327
+ buildWorldQuadraticPath(line, segment, reverse = false) {
33328
+ const startPoint = reverse ? segment.end : segment.start;
33329
+ const endPoint = reverse ? segment.start : segment.end;
33330
+ const controlPoint = segment.control;
33331
+ const startWorld = this.lineLocalToWorld(line, startPoint.x, startPoint.y);
33332
+ const controlWorld = this.lineLocalToWorld(line, controlPoint.x, controlPoint.y);
33333
+ const endWorld = this.lineLocalToWorld(line, endPoint.x, endPoint.y);
33334
+ return `M ${startWorld.x} ${startWorld.y} Q ${controlWorld.x} ${controlWorld.y} ${endWorld.x} ${endWorld.y}`;
33335
+ }
33336
+ getPolygonPoints(polygon) {
33337
+ return [polygon.topLeft, polygon.topRight, polygon.bottomRight, polygon.bottomLeft];
33338
+ }
33339
+ /**
33340
+ * Converts local line coordinates to world coordinates.
33341
+ */
33342
+ lineLocalToWorld(line, localX, localY) {
33343
+ const px = localX - line.x;
33344
+ const py = localY - line.y;
33345
+ const cx = line.totalWidth / 2;
33346
+ const cy = line.totalHeight / 2;
33347
+ const cos = Math.cos(line.rotation);
33348
+ const sin = Math.sin(line.rotation);
33349
+ const rotatedX = (px - cx) * cos - (py - cy) * sin + cx;
33350
+ const rotatedY = (px - cx) * sin + (py - cy) * cos + cy;
33351
+ return {
33352
+ x: rotatedX / line.scale + line.translateX,
33353
+ y: rotatedY / line.scale + line.translateY,
33354
+ };
33355
+ }
33356
+ /**
33357
+ * Converts world coordinates to line's local SVG coordinates.
33358
+ */
33359
+ lineWorldToLocal(line, worldX, worldY) {
33360
+ const dx = (worldX - line.translateX) * line.scale;
33361
+ const dy = (worldY - line.translateY) * line.scale;
33362
+ const cx = line.totalWidth / 2;
33363
+ const cy = line.totalHeight / 2;
33364
+ const cos = Math.cos(-line.rotation);
33365
+ const sin = Math.sin(-line.rotation);
33366
+ const rotatedX = (dx - cx) * cos - (dy - cy) * sin + cx;
33367
+ const rotatedY = (dx - cx) * sin + (dy - cy) * cos + cy;
33368
+ return {
33369
+ x: rotatedX + line.x,
33370
+ y: rotatedY + line.y,
33371
+ };
33372
+ }
33373
+ /**
33374
+ * Checks if an object can be used as an anchor target.
33375
+ */
33376
+ isAnchorable(object) {
33377
+ // Exclude selection-related objects
33378
+ if (object instanceof KritzelSelectionBox || object instanceof KritzelSelectionGroup) {
33379
+ return false;
33380
+ }
33381
+ // Exclude line objects - lines cannot be anchored to other lines
33382
+ if (object instanceof KritzelLine) {
33383
+ return false;
33384
+ }
33385
+ // All other visible objects can be anchored to
33386
+ return object.isVisible;
33387
+ }
33388
+ }
33389
+
31204
33390
  const DEFAULT_BRUSH_CONFIG = {
31205
33391
  type: 'pen',
31206
33392
  color: '#000000',
@@ -31293,24 +33479,65 @@ const DEFAULT_TEXT_CONFIG = {
31293
33479
  ],
31294
33480
  };
31295
33481
 
33482
+ const DEFAULT_LINE_TOOL_CONFIG = {
33483
+ color: '#000000',
33484
+ size: 4,
33485
+ palette: [
33486
+ '#000000',
33487
+ '#ff5252',
33488
+ '#ffbc00',
33489
+ '#00c853',
33490
+ '#0000FF',
33491
+ '#d500f9',
33492
+ '#fafafa',
33493
+ '#a52714',
33494
+ '#ee8100',
33495
+ '#558b2f',
33496
+ '#01579b',
33497
+ '#8e24aa',
33498
+ '#90a4ae',
33499
+ '#ff4081',
33500
+ '#ff6e40',
33501
+ '#aeea00',
33502
+ '#304ffe',
33503
+ '#7c4dff',
33504
+ '#cfd8dc',
33505
+ '#f8bbd0',
33506
+ '#ffccbc',
33507
+ '#f0f4c3',
33508
+ '#b3e5fc',
33509
+ '#e1bee7',
33510
+ ],
33511
+ arrows: {
33512
+ end: { enabled: true, style: 'triangle' },
33513
+ },
33514
+ };
33515
+
31296
33516
  exports.DEFAULT_BRUSH_CONFIG = DEFAULT_BRUSH_CONFIG;
33517
+ exports.DEFAULT_LINE_TOOL_CONFIG = DEFAULT_LINE_TOOL_CONFIG;
31297
33518
  exports.DEFAULT_SYNC_CONFIG = DEFAULT_SYNC_CONFIG;
31298
33519
  exports.DEFAULT_TEXT_CONFIG = DEFAULT_TEXT_CONFIG;
31299
33520
  exports.Doc = Doc;
31300
33521
  exports.HocuspocusProvider = HocuspocusProvider;
31301
33522
  exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
31302
33523
  exports.IndexedDBSyncProvider = IndexedDBSyncProvider;
33524
+ exports.KritzelAnchorManager = KritzelAnchorManager;
31303
33525
  exports.KritzelAppStateMap = KritzelAppStateMap;
31304
33526
  exports.KritzelBaseHandler = KritzelBaseHandler;
31305
33527
  exports.KritzelBaseObject = KritzelBaseObject;
31306
33528
  exports.KritzelBaseTool = KritzelBaseTool;
31307
33529
  exports.KritzelBrushTool = KritzelBrushTool;
33530
+ exports.KritzelClassHelper = KritzelClassHelper;
33531
+ exports.KritzelCursorHelper = KritzelCursorHelper;
31308
33532
  exports.KritzelDevicesHelper = KritzelDevicesHelper;
31309
33533
  exports.KritzelEraserTool = KritzelEraserTool;
31310
33534
  exports.KritzelEventHelper = KritzelEventHelper;
33535
+ exports.KritzelIconRegistry = KritzelIconRegistry;
31311
33536
  exports.KritzelImage = KritzelImage;
31312
33537
  exports.KritzelImageTool = KritzelImageTool;
31313
33538
  exports.KritzelKeyboardHelper = KritzelKeyboardHelper;
33539
+ exports.KritzelLine = KritzelLine;
33540
+ exports.KritzelLineTool = KritzelLineTool;
31314
33541
  exports.KritzelPath = KritzelPath;
31315
33542
  exports.KritzelSelectionBox = KritzelSelectionBox;
31316
33543
  exports.KritzelSelectionGroup = KritzelSelectionGroup;
@@ -31351,6 +33578,6 @@ exports.varStorage = varStorage;
31351
33578
  exports.writeVarString = writeVarString$2;
31352
33579
  exports.writeVarUint = writeVarUint$2;
31353
33580
  exports.writeVarUint8Array = writeVarUint8Array$2;
31354
- //# sourceMappingURL=default-text-tool.config-D10FksvZ.js.map
33581
+ //# sourceMappingURL=default-line-tool.config-D1Ns0NmM.js.map
31355
33582
 
31356
- //# sourceMappingURL=default-text-tool.config-D10FksvZ.js.map
33583
+ //# sourceMappingURL=default-line-tool.config-D1Ns0NmM.js.map