@elixpo/lixsketch 5.5.19 → 5.5.20

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 (74) hide show
  1. package/dist/react/Circle-6POZCUZD.js +9 -0
  2. package/dist/react/{CopyPaste-JMIXWUUO.js → CopyPaste-KJZ34MIP.js} +6 -5
  3. package/dist/react/{CopyPaste-JMIXWUUO.js.map → CopyPaste-KJZ34MIP.js.map} +1 -1
  4. package/dist/react/{EventDispatcher-H5EA4KZN.js → EventDispatcher-XDRQS7BD.js} +7 -6
  5. package/dist/react/{EventDispatcher-H5EA4KZN.js.map → EventDispatcher-XDRQS7BD.js.map} +1 -1
  6. package/dist/react/{Frame-VADA625S.js → Frame-QLBAVLGW.js} +3 -2
  7. package/dist/react/FreehandStroke-U6GYZOM4.js +9 -0
  8. package/dist/react/Rectangle-S7XUKWYD.js +9 -0
  9. package/dist/react/{SceneSerializer-ARTKPZVR.js → SceneSerializer-VY3HY4PF.js} +6 -5
  10. package/dist/react/{SceneSerializer-ARTKPZVR.js.map → SceneSerializer-VY3HY4PF.js.map} +1 -1
  11. package/dist/react/{Selection-YJNZRBON.js → Selection-U2AR6NQS.js} +3 -2
  12. package/dist/react/{SketchEngine-ZGYBIZG7.js → SketchEngine-ZFYUTKET.js} +14 -14
  13. package/dist/react/{ZoomPan-NYPDJBD5.js → ZoomPan-FAWA5OPS.js} +5 -1
  14. package/dist/react/ZoomPan-FAWA5OPS.js.map +7 -0
  15. package/dist/react/{chunk-73VA5ALZ.js → chunk-2I3GH65Z.js} +5 -1
  16. package/dist/react/chunk-2I3GH65Z.js.map +7 -0
  17. package/dist/react/{chunk-3VSGFP6U.js → chunk-73VLPCBC.js} +6 -2
  18. package/dist/react/chunk-73VLPCBC.js.map +7 -0
  19. package/dist/react/{chunk-CF7AK4ES.js → chunk-75G7XSZU.js} +10 -1
  20. package/dist/react/chunk-75G7XSZU.js.map +7 -0
  21. package/dist/react/{chunk-FV3EPR4L.js → chunk-7VBI7S7T.js} +10 -1
  22. package/dist/react/chunk-7VBI7S7T.js.map +7 -0
  23. package/dist/react/{chunk-XCJOF6BM.js → chunk-FETTJLCV.js} +5 -1
  24. package/dist/react/chunk-FETTJLCV.js.map +7 -0
  25. package/dist/react/chunk-PAKA6HUX.js +59 -0
  26. package/dist/react/chunk-PAKA6HUX.js.map +7 -0
  27. package/dist/react/{chunk-HKYUJUD7.js → chunk-PXFPPO3R.js} +5 -1
  28. package/dist/react/chunk-PXFPPO3R.js.map +7 -0
  29. package/dist/react/{chunk-JJTII2UQ.js → chunk-R5QXEVZ5.js} +5 -1
  30. package/dist/react/chunk-R5QXEVZ5.js.map +7 -0
  31. package/dist/react/{chunk-QYNTPZON.js → chunk-SBSM3GQD.js} +7 -3
  32. package/dist/react/chunk-SBSM3GQD.js.map +7 -0
  33. package/dist/react/{chunk-N7NZLF6P.js → chunk-TKXKJNTX.js} +5 -1
  34. package/dist/react/chunk-TKXKJNTX.js.map +7 -0
  35. package/dist/react/{codeTool-O2Z7ZXGP.js → codeTool-VBDB2ES6.js} +3 -2
  36. package/dist/react/{iconTool-AUHRLOO2.js → iconTool-5WNDHI5T.js} +3 -2
  37. package/dist/react/{imageTool-DRKDCPUV.js → imageTool-ECPMT3BL.js} +3 -2
  38. package/dist/react/index.js +2 -2
  39. package/dist/react/{textTool-UORBAAJE.js → textTool-5IJKTWVQ.js} +4 -3
  40. package/package.json +1 -1
  41. package/src/core/ScreenSpaceControls.js +60 -0
  42. package/src/core/Selection.js +7 -0
  43. package/src/core/ZoomPan.js +3 -0
  44. package/src/shapes/Circle.js +2 -0
  45. package/src/shapes/Frame.js +7 -0
  46. package/src/shapes/FreehandStroke.js +2 -0
  47. package/src/shapes/Rectangle.js +2 -0
  48. package/src/tools/codeTool.js +3 -2
  49. package/src/tools/iconTool.js +3 -1
  50. package/src/tools/imageTool.js +3 -1
  51. package/src/tools/textTool.js +3 -1
  52. package/dist/react/Circle-QQMKCKFR.js +0 -8
  53. package/dist/react/FreehandStroke-BOWDNAVC.js +0 -8
  54. package/dist/react/Rectangle-MZ6ZB6KM.js +0 -8
  55. package/dist/react/ZoomPan-NYPDJBD5.js.map +0 -7
  56. package/dist/react/chunk-3VSGFP6U.js.map +0 -7
  57. package/dist/react/chunk-73VA5ALZ.js.map +0 -7
  58. package/dist/react/chunk-CF7AK4ES.js.map +0 -7
  59. package/dist/react/chunk-FV3EPR4L.js.map +0 -7
  60. package/dist/react/chunk-HKYUJUD7.js.map +0 -7
  61. package/dist/react/chunk-JJTII2UQ.js.map +0 -7
  62. package/dist/react/chunk-N7NZLF6P.js.map +0 -7
  63. package/dist/react/chunk-QYNTPZON.js.map +0 -7
  64. package/dist/react/chunk-XCJOF6BM.js.map +0 -7
  65. /package/dist/react/{Circle-QQMKCKFR.js.map → Circle-6POZCUZD.js.map} +0 -0
  66. /package/dist/react/{Frame-VADA625S.js.map → Frame-QLBAVLGW.js.map} +0 -0
  67. /package/dist/react/{FreehandStroke-BOWDNAVC.js.map → FreehandStroke-U6GYZOM4.js.map} +0 -0
  68. /package/dist/react/{Rectangle-MZ6ZB6KM.js.map → Rectangle-S7XUKWYD.js.map} +0 -0
  69. /package/dist/react/{Selection-YJNZRBON.js.map → Selection-U2AR6NQS.js.map} +0 -0
  70. /package/dist/react/{SketchEngine-ZGYBIZG7.js.map → SketchEngine-ZFYUTKET.js.map} +0 -0
  71. /package/dist/react/{codeTool-O2Z7ZXGP.js.map → codeTool-VBDB2ES6.js.map} +0 -0
  72. /package/dist/react/{iconTool-AUHRLOO2.js.map → iconTool-5WNDHI5T.js.map} +0 -0
  73. /package/dist/react/{imageTool-DRKDCPUV.js.map → imageTool-ECPMT3BL.js.map} +0 -0
  74. /package/dist/react/{textTool-UORBAAJE.js.map → textTool-5IJKTWVQ.js.map} +0 -0
@@ -161,16 +161,16 @@ var SketchEngine = class {
161
161
  { Frame },
162
162
  { FreehandStroke }
163
163
  ] = await Promise.all([
164
- import("./Rectangle-MZ6ZB6KM.js"),
165
- import("./Circle-QQMKCKFR.js"),
164
+ import("./Rectangle-S7XUKWYD.js"),
165
+ import("./Circle-6POZCUZD.js"),
166
166
  import("./Arrow-EF7ALH2Y.js"),
167
167
  import("./Line-FCCF2BOB.js"),
168
168
  import("./TextShape-DY77FFI2.js"),
169
169
  import("./CodeShape-TE74XP5V.js"),
170
170
  import("./ImageShape-MYP2DVVH.js"),
171
171
  import("./IconShape-32EYI2MA.js"),
172
- import("./Frame-VADA625S.js"),
173
- import("./FreehandStroke-BOWDNAVC.js")
172
+ import("./Frame-QLBAVLGW.js"),
173
+ import("./FreehandStroke-U6GYZOM4.js")
174
174
  ]);
175
175
  window.Rectangle = Rectangle;
176
176
  window.Circle = Circle;
@@ -210,10 +210,10 @@ var SketchEngine = class {
210
210
  import("./circleTool-RDOIMKKV.js"),
211
211
  import("./arrowTool-LKFSZKXC.js"),
212
212
  import("./lineTool-XLOOZE3E.js"),
213
- import("./textTool-UORBAAJE.js"),
214
- import("./codeTool-O2Z7ZXGP.js"),
215
- import("./imageTool-DRKDCPUV.js"),
216
- import("./iconTool-AUHRLOO2.js"),
213
+ import("./textTool-5IJKTWVQ.js"),
214
+ import("./codeTool-VBDB2ES6.js"),
215
+ import("./imageTool-ECPMT3BL.js"),
216
+ import("./iconTool-5WNDHI5T.js"),
217
217
  import("./frameTool-Z5ZDCX6H.js"),
218
218
  import("./freehandTool-AAQCTMVM.js")
219
219
  ]);
@@ -239,11 +239,11 @@ var SketchEngine = class {
239
239
  resizeShapes,
240
240
  resizeCode
241
241
  ] = await Promise.all([
242
- import("./EventDispatcher-H5EA4KZN.js"),
242
+ import("./EventDispatcher-XDRQS7BD.js"),
243
243
  import("./UndoRedo-BZ6W4QTV.js"),
244
- import("./Selection-YJNZRBON.js"),
245
- import("./ZoomPan-NYPDJBD5.js"),
246
- import("./CopyPaste-JMIXWUUO.js"),
244
+ import("./Selection-U2AR6NQS.js"),
245
+ import("./ZoomPan-FAWA5OPS.js"),
246
+ import("./CopyPaste-KJZ34MIP.js"),
247
247
  import("./EraserTrail-PA6IQLQN.js"),
248
248
  import("./ResizeShapes-LKEQTITP.js"),
249
249
  import("./ResizeCode-WAMDTKSW.js")
@@ -262,7 +262,7 @@ var SketchEngine = class {
262
262
  eventDispatcher.initEventDispatcher(this.svg);
263
263
  }
264
264
  window.__sketchInteractionReady = true;
265
- const sceneSerializer = await import("./SceneSerializer-ARTKPZVR.js");
265
+ const sceneSerializer = await import("./SceneSerializer-VY3HY4PF.js");
266
266
  if (sceneSerializer.initSceneSerializer) sceneSerializer.initSceneSerializer();
267
267
  await Promise.all([
268
268
  import("./eraserTool-2GS2LQCS.js"),
@@ -414,4 +414,4 @@ export {
414
414
  SketchEngine,
415
415
  SketchEngine_default as default
416
416
  };
417
- //# sourceMappingURL=SketchEngine-ZGYBIZG7.js.map
417
+ //# sourceMappingURL=SketchEngine-ZFYUTKET.js.map
@@ -1,6 +1,10 @@
1
1
  "use client";
2
+ import {
3
+ installScreenSpaceControlSync
4
+ } from "./chunk-PAKA6HUX.js";
2
5
 
3
6
  // src/core/ZoomPan.js
7
+ installScreenSpaceControlSync(freehandCanvas);
4
8
  var scrollRate = 50;
5
9
  function updateZoomDisplay() {
6
10
  zoomPercentSpan.innerText = Math.round(currentZoom * 100) + "%";
@@ -259,4 +263,4 @@ svg.addEventListener("wheel", (e) => {
259
263
  `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`
260
264
  );
261
265
  });
262
- //# sourceMappingURL=ZoomPan-NYPDJBD5.js.map
266
+ //# sourceMappingURL=ZoomPan-FAWA5OPS.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/core/ZoomPan.js"],
4
+ "sourcesContent": ["/* eslint-disable */\nimport { installScreenSpaceControlSync } from './ScreenSpaceControls.js';\n// Zoom and Pan controls - combined from zoomFunction.js + panCanvas.js\n// Depends on globals: svg, freehandCanvas, currentZoom, currentViewBox, isPanningToolActive, isPanning\n// Depends on globals: minScale, maxScale, zoomInBtn, zoomOutBtn, zoomPercentSpan, panStart, startCanvasX, startCanvasY\n\ninstallScreenSpaceControlSync(freehandCanvas);\n\n// === Zoom Functions ===\nlet currentY = 0;\nconst scrollRate = 50;\n\nfunction updateZoomDisplay() {\n zoomPercentSpan.innerText = Math.round(currentZoom * 100) + \"%\";\n}\n\nfunction updateViewBox(anchorX = null, anchorY = null) {\n const width = window.innerWidth;\n const height = window.innerHeight;\n const scaledWidth = width / currentZoom;\n const scaledHeight = height / currentZoom;\n\n let centerX, centerY;\n\n if (anchorX === null || anchorY === null) {\n centerX = currentViewBox.x + (currentViewBox.width / 2);\n centerY = currentViewBox.y + (currentViewBox.height / 2);\n } else {\n\n centerX = anchorX;\n centerY = anchorY;\n }\n\n const viewBoxX = centerX - (scaledWidth / 2);\n const viewBoxY = centerY - (scaledHeight / 2);\n\n freehandCanvas.setAttribute(\n \"viewBox\",\n `${viewBoxX} ${viewBoxY} ${scaledWidth} ${scaledHeight}`\n );\n\n currentViewBox.x = viewBoxX;\n currentViewBox.y = viewBoxY;\n currentViewBox.width = scaledWidth;\n currentViewBox.height = scaledHeight;\n}\n\nzoomInBtn.addEventListener(\"click\", function() {\n currentZoom *= 1.1;\n if (currentZoom > maxScale) currentZoom = maxScale;\n updateViewBox();\n updateZoomDisplay();\n});\n\nzoomOutBtn.addEventListener(\"click\", function() {\n currentZoom /= 1.1;\n if (currentZoom < minScale) currentZoom = minScale;\n updateViewBox();\n updateZoomDisplay();\n});\n\n// Exposed for React Footer buttons \u2014 zoom from center of canvas\nwindow.zoomFromCenter = function(direction) {\n if (direction > 0) {\n currentZoom *= 1.1;\n if (currentZoom > maxScale) currentZoom = maxScale;\n } else {\n currentZoom /= 1.1;\n if (currentZoom < minScale) currentZoom = minScale;\n }\n updateViewBox(); // null anchors = zoom from center\n updateZoomDisplay();\n // Sync React zoom state\n if (window.__sketchStoreApi && window.__sketchStoreApi.setZoom) {\n window.__sketchStoreApi.setZoom(currentZoom);\n }\n};\n\nwindow.zoomReset = function() {\n // Issue #24 bug #5: reset zoom in place around the current viewport\n // centre \u2014 do NOT recenter to (0, 0). Recentering used to make whatever\n // the user had on screen vanish until they panned back to it.\n const cx = currentViewBox.x + currentViewBox.width / 2;\n const cy = currentViewBox.y + currentViewBox.height / 2;\n\n currentZoom = 1;\n const rect = freehandCanvas.getBoundingClientRect();\n const vbW = rect.width || window.innerWidth;\n const vbH = rect.height || window.innerHeight;\n\n currentViewBox.x = cx - vbW / 2;\n currentViewBox.y = cy - vbH / 2;\n currentViewBox.width = vbW;\n currentViewBox.height = vbH;\n freehandCanvas.setAttribute(\n \"viewBox\",\n `${currentViewBox.x} ${currentViewBox.y} ${vbW} ${vbH}`\n );\n updateZoomDisplay();\n if (window.__sketchStoreApi && window.__sketchStoreApi.setZoom) {\n window.__sketchStoreApi.setZoom(1);\n }\n};\n\nfunction getShapeBoundsForFit(shape) {\n if (!shape) return null;\n\n const selection = window.multiSelection;\n if (selection && typeof selection.getShapeBounds === \"function\") {\n try {\n const bounds = selection.getShapeBounds(shape);\n if (bounds && [bounds.x, bounds.y, bounds.width, bounds.height].every(Number.isFinite)) {\n return bounds;\n }\n } catch {}\n }\n\n // Fall back to the common shape interface when selection is unavailable.\n // Accessing x/y instead of boundingBox preserves freehand move offsets.\n const bounds = {\n x: Number(shape.x),\n y: Number(shape.y),\n width: Number(shape.width),\n height: Number(shape.height),\n };\n return Object.values(bounds).every(Number.isFinite) ? bounds : null;\n}\n\nwindow.zoomFit = function() {\n if (!window.shapes || window.shapes.length === 0) {\n if (window.zoomReset) window.zoomReset();\n return;\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n window.shapes.forEach(shape => {\n const bounds = getShapeBoundsForFit(shape);\n if (!bounds) return;\n \n // Normalize coordinates just in case width/height is negative\n const x1 = Math.min(bounds.x, bounds.x + bounds.width);\n const x2 = Math.max(bounds.x, bounds.x + bounds.width);\n const y1 = Math.min(bounds.y, bounds.y + bounds.height);\n const y2 = Math.max(bounds.y, bounds.y + bounds.height);\n\n minX = Math.min(minX, x1);\n minY = Math.min(minY, y1);\n maxX = Math.max(maxX, x2);\n maxY = Math.max(maxY, y2);\n });\n\n if (minX === Infinity) {\n if (window.zoomReset) window.zoomReset();\n return;\n }\n\n const rect = freehandCanvas.getBoundingClientRect();\n const screenW = rect.width || window.innerWidth;\n const screenH = rect.height || window.innerHeight;\n\n const contentW = maxX - minX;\n const contentH = maxY - minY;\n \n // Add 10% padding\n const paddingX = Math.max(50, contentW * 0.1);\n const paddingY = Math.max(50, contentH * 0.1);\n\n const paddedW = contentW + paddingX * 2;\n const paddedH = contentH + paddingY * 2;\n\n // Calculate required zoom to fit the padded content in the screen\n const zoomX = screenW / paddedW;\n const zoomY = screenH / paddedH;\n \n let newZoom = Math.min(zoomX, zoomY);\n newZoom = Math.max(minScale, Math.min(newZoom, maxScale));\n\n // Center coordinate of the content\n const cx = minX + contentW / 2;\n const cy = minY + contentH / 2;\n\n // Set the new viewBox and zoom\n currentZoom = newZoom;\n const vbW = screenW / currentZoom;\n const vbH = screenH / currentZoom;\n\n currentViewBox.x = cx - vbW / 2;\n currentViewBox.y = cy - vbH / 2;\n currentViewBox.width = vbW;\n currentViewBox.height = vbH;\n\n freehandCanvas.setAttribute(\n \"viewBox\",\n `${currentViewBox.x} ${currentViewBox.y} ${vbW} ${vbH}`\n );\n \n updateZoomDisplay();\n if (window.__sketchStoreApi && window.__sketchStoreApi.setZoom) {\n window.__sketchStoreApi.setZoom(currentZoom);\n }\n};\n\nfreehandCanvas.addEventListener(\"wheel\", function(e) {\n if (!e.ctrlKey) return;\n e.preventDefault();\n\n // Determine the zoom delta\n const delta = e.deltaY > 0 ? -0.1 : 0.1;\n let newZoom = currentZoom + delta;\n if (newZoom < minScale) newZoom = minScale;\n if (newZoom > maxScale) newZoom = maxScale;\n\n // Get canvas bounding rect \u2014 this MUST be the SVG element's actual\n // size, not the window's. In split mode the canvas pane is narrower\n // than the viewport; using window.innerWidth here would produce a\n // viewBox aspect that doesn't match the element, and (with\n // preserveAspectRatio=\"none\" on the host) the content gets stretched\n // / squeezed on every zoom step.\n const rect = freehandCanvas.getBoundingClientRect();\n\n // Calculate mouse position relative to the canvas in pixels\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n\n // Determine what fraction of the canvas the mouse is at\n const mouseFracX = mouseX / rect.width;\n const mouseFracY = mouseY / rect.height;\n\n // Compute the current viewBox coordinate under the mouse.\n // currentViewBox.width and .height represent the current viewBox dimensions.\n const anchorViewBoxX = currentViewBox.x + mouseFracX * currentViewBox.width;\n const anchorViewBoxY = currentViewBox.y + mouseFracY * currentViewBox.height;\n\n // New viewBox dimensions sized to the *element*, scaled by the new\n // zoom. This keeps the viewBox aspect ratio matching the element so\n // shapes stay undistorted.\n const newViewBoxWidth = rect.width / newZoom;\n const newViewBoxHeight = rect.height / newZoom;\n \n // Compute the new viewBox's x and y so that the anchor remains at the same screen fraction.\n // That means:\n // newViewBox.x + mouseFracX * newViewBoxWidth === anchorViewBoxX\n // Solve for newViewBox.x:\n const newViewBoxX = anchorViewBoxX - mouseFracX * newViewBoxWidth;\n const newViewBoxY = anchorViewBoxY - mouseFracY * newViewBoxHeight;\n \n // Update the viewBox attribute with the new values.\n freehandCanvas.setAttribute(\n \"viewBox\",\n `${newViewBoxX} ${newViewBoxY} ${newViewBoxWidth} ${newViewBoxHeight}`\n );\n \n // Update our state\n currentZoom = newZoom;\n currentViewBox = {\n x: newViewBoxX,\n y: newViewBoxY,\n width: newViewBoxWidth,\n height: newViewBoxHeight\n };\n\n updateZoomDisplay();\n // Sync React zoom state\n if (window.__sketchStoreApi && window.__sketchStoreApi.setZoom) {\n window.__sketchStoreApi.setZoom(currentZoom);\n }\n});\n\n// Function to resize the canvas to fill the screen (initial setup)\nfunction resizeCanvas() {\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n freehandCanvas.style.width = `${width}px`;\n freehandCanvas.style.height = `${height}px`;\n\n // Set initial viewBox based on initial zoom\n updateViewBox(); // Initial center anchor\n}\n\n\n\nlet isMiddleMousePanning = false;\n\nfreehandCanvas.addEventListener(\"mousedown\", function (e) {\n // Middle mouse button panning\n if (e.button === 1) {\n e.preventDefault();\n isMiddleMousePanning = true;\n isPanning = true;\n startCanvasX = e.clientX;\n startCanvasY = e.clientY;\n panStart = { x: e.clientX, y: e.clientY };\n freehandCanvas.style.cursor = 'grabbing';\n return;\n }\n if (isPanningToolActive) {\n isPanning = true;\n startCanvasX = e.clientX;\n startCanvasY = e.clientY;\n panStart = { x: e.clientX, y: e.clientY };\n freehandCanvas.style.cursor = 'grabbing';\n }\n});\n\nfreehandCanvas.addEventListener(\"mousemove\", (e) => {\n if (!isPanning) return;\n\n const dx = e.clientX - panStart.x;\n const dy = e.clientY - panStart.y;\n const dxViewBox = dx / currentZoom;\n const dyViewBox = dy / currentZoom;\n\n currentViewBox.x -= dxViewBox;\n currentViewBox.y -= dyViewBox;\n\n freehandCanvas.setAttribute(\n \"viewBox\",\n `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`\n );\n\n panStart = { x: e.clientX, y: e.clientY };\n});\n\nfreehandCanvas.addEventListener(\"mouseup\", (e) => {\n if (isMiddleMousePanning) {\n isMiddleMousePanning = false;\n isPanning = false;\n freehandCanvas.style.cursor = '';\n return;\n }\n if(isPanningToolActive)\n {\n isPanning = false;\n freehandCanvas.style.cursor = 'grab';\n }\n});\n\nfreehandCanvas.addEventListener(\"mouseleave\", () => {\n if (isMiddleMousePanning) {\n isMiddleMousePanning = false;\n isPanning = false;\n freehandCanvas.style.cursor = '';\n return;\n }\n if(isPanningToolActive)\n {\n isPanning = false;\n freehandCanvas.style.cursor = 'grab';\n }\n});\n\n// Prevent default middle-click auto-scroll behavior\nfreehandCanvas.addEventListener(\"auxclick\", (e) => {\n if (e.button === 1) e.preventDefault();\n});\n\n\nsvg.addEventListener(\"wheel\", (e) => {\n e.preventDefault();\n if (e.ctrlKey) return; // Ignore zoom gestures\n\n if (e.shiftKey) {\n // Pan sideways when Shift is held\n currentViewBox.x += e.deltaY > 0 ? scrollRate : -scrollRate;\n } else {\n // Pan vertically\n currentViewBox.y += e.deltaY > 0 ? scrollRate : -scrollRate;\n }\n\n svg.setAttribute(\n \"viewBox\",\n `${currentViewBox.x} ${currentViewBox.y} ${currentViewBox.width} ${currentViewBox.height}`\n );\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["bounds"]
7
+ }
@@ -1,4 +1,7 @@
1
1
  "use client";
2
+ import {
3
+ registerRotationAnchor
4
+ } from "./chunk-PAKA6HUX.js";
2
5
  import {
3
6
  cleanupAttachments,
4
7
  updateAttachedArrows
@@ -515,6 +518,7 @@ function addRotationAnchor(x, y, width, height, centerX, centerY, iconWidth, rot
515
518
  rotationAnchor.setAttribute("style", "pointer-events: all; cursor: grab;");
516
519
  rotationAnchor.setAttribute("transform", `rotate(${rotation}, ${centerX}, ${centerY})`);
517
520
  svg.appendChild(rotationAnchor);
521
+ registerRotationAnchor(rotationAnchor, { radius: 5, edgeY: y });
518
522
  rotationAnchor.addEventListener("pointerdown", startRotation);
519
523
  rotationAnchor.addEventListener("pointerup", stopRotation);
520
524
  rotationAnchor.addEventListener("mouseover", function() {
@@ -932,4 +936,4 @@ export {
932
936
  startDrag,
933
937
  stopDrag
934
938
  };
935
- //# sourceMappingURL=chunk-73VA5ALZ.js.map
939
+ //# sourceMappingURL=chunk-2I3GH65Z.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/tools/iconTool.js"],
4
+ "sourcesContent": ["/* eslint-disable */\n// Icon tool event handlers - extracted from icons.js\nimport { pushCreateAction, pushDeleteAction, pushTransformAction, pushFrameAttachmentAction } from '../core/UndoRedo.js';\nimport { updateAttachedArrows as updateArrowsForShape, cleanupAttachments } from './arrowTool.js';\nimport { registerRotationAnchor } from '../core/ScreenSpaceControls.js';\n\n\nlet isDraggingIcon = false;\nlet iconToPlace = null;\nlet iconX = 0;\nlet iconY = 0;\nlet scaleFactor = 0.2;\nlet currentIconElement = null;\n\nlet selectedIcon = null;\nlet originalX, originalY, originalWidth, originalHeight;\nlet currentAnchor = null;\nlet isDragging = false;\nlet isRotatingIcon = false;\nlet dragOffsetX, dragOffsetY;\nlet startRotationMouseAngle = null;\nlet startIconRotation = null;\nlet iconRotation = 0;\nlet aspect_ratio_lock = true;\nconst minIconSize = 25;\nconst miniatureSize = 40;\nconst placedIconSize = 40;\nlet draggedShapeInitialFrameIcon = null;\nlet hoveredFrameIcon = null;\nlet _pendingDragChecker = null; // module-level ref so cancelDragPrep can remove it\n\nconst iconSearchInput = document.getElementById('iconSearchInput') || document.createElement('input');\nlet searchTimeout = null;\n\n\nfunction getSVGElement() {\n return document.getElementById('freehand-canvas');\n}\n\n\nfunction removeSelection() {\n const svg = getSVGElement();\n if (!svg) return;\n\n // Remove ALL selection outlines (prevents orphaned ghost elements)\n svg.querySelectorAll(\".selection-outline\").forEach(el => el.remove());\n\n removeResizeAnchors();\n removeRotationAnchor();\n\n if (selectedIcon) {\n selectedIcon.removeEventListener('pointerdown', startDrag);\n selectedIcon.removeEventListener('pointerup', stopDrag);\n selectedIcon.removeEventListener('pointerleave', stopDrag);\n }\n}\n\nfunction wrapIconElement(element) {\n const iconShape = new IconShape(element);\n return iconShape;\n}\n\ndocument.getElementById(\"importIcon\")?.addEventListener('click', () => {\n \n\n const iconContainer = document.getElementById('iconsToolBar');\n if (iconContainer) {\n if(iconContainer.classList.contains('hidden')) {\n iconContainer.classList.remove('hidden');\n iconSearchInput.focus();\n }\n else \n {\n iconContainer.classList.add('hidden');\n }\n \n }\n});\n\nfunction getSVGCoordsFromMouse(e) {\n const svg = getSVGElement();\n const viewBox = svg.viewBox.baseVal;\n const rect = svg.getBoundingClientRect();\n const mouseX = e.clientX - rect.left;\n const mouseY = e.clientY - rect.top;\n const svgX = viewBox.x + (mouseX / rect.width) * viewBox.width;\n const svgY = viewBox.y + (mouseY / rect.height) * viewBox.height;\n return { x: svgX, y: svgY };\n}\n\nconst handleMouseMoveIcon = (e) => {\n if (!isDraggingIcon || !iconToPlace || !isIconToolActive) return;\n\n const { x, y } = getSVGCoordsFromMouse(e);\n iconX = x;\n iconY = y;\n\n drawMiniatureIcon();\n\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(frame => {\n if (frame.shapeName === 'frame') {\n const tempIconBounds = {\n x: iconX - 50,\n y: iconY - 50,\n width: 100,\n height: 100\n };\n\n if (frame.isShapeInFrame(tempIconBounds)) {\n frame.highlightFrame();\n hoveredFrameIcon = frame;\n if (window.__iconShapeState) window.__iconShapeState.hoveredFrameIcon = frame;\n } else if (hoveredFrameIcon === frame) {\n frame.removeHighlight();\n hoveredFrameIcon = null;\n if (window.__iconShapeState) window.__iconShapeState.hoveredFrameIcon = null;\n }\n }\n });\n }\n};\n\nconst drawMiniatureIcon = () => {\n if (!isDraggingIcon || !iconToPlace || !isIconToolActive) return;\n\n const svg = getSVGElement();\n if (!svg) {\n console.error('SVG element not found for miniature icon');\n return;\n }\n\n if (currentIconElement) {\n svg.removeChild(currentIconElement);\n currentIconElement = null;\n }\n\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = iconToPlace;\n const svgElement = tempDiv.querySelector('svg');\n\n if (svgElement) {\n const iconGroup = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n\n const viewBox = svgElement.getAttribute('viewBox');\n let vbWidth = 24, vbHeight = 24;\n\n if (viewBox) {\n const [, , widthStr, heightStr] = viewBox.split(/\\s+/);\n vbWidth = parseFloat(widthStr) || 24;\n vbHeight = parseFloat(heightStr) || 24;\n } else {\n const width = svgElement.getAttribute('width');\n const height = svgElement.getAttribute('height');\n if (width && height) {\n vbWidth = parseFloat(width) || 24;\n vbHeight = parseFloat(height) || 24;\n }\n }\n\n const scale = miniatureSize / Math.max(vbWidth, vbHeight);\n iconGroup.setAttribute(\"transform\", `translate(${iconX - miniatureSize / 2}, ${iconY - miniatureSize / 2}) scale(${scale})`);\n\n const allChildren = svgElement.children;\n for (let i = 0; i < allChildren.length; i++) {\n const clonedChild = allChildren[i].cloneNode(true);\n \n const applyGrayStyle = (element) => {\n if (element.nodeType === 1) {\n element.setAttribute('fill', '#666');\n element.setAttribute('stroke', '#666');\n \n for (let j = 0; j < element.children.length; j++) {\n applyGrayStyle(element.children[j]);\n }\n }\n };\n \n applyGrayStyle(clonedChild);\n iconGroup.appendChild(clonedChild);\n }\n\n iconGroup.setAttribute(\"style\", \"pointer-events: none; opacity: 0.7;\");\n iconGroup.setAttribute(\"class\", \"miniature-icon\");\n\n currentIconElement = iconGroup;\n svg.appendChild(currentIconElement);\n }\n};\n\nconst handleMouseDownIcon = async (e) => {\n if (!e.target) return;\n if (isSelectionToolActive) {\n const clickedIcon = e.target.closest('[type=\"icon\"]');\n if (clickedIcon) {\n e.preventDefault();\n e.stopPropagation();\n\n if (selectedIcon === clickedIcon) {\n originalX = parseFloat(selectedIcon.getAttribute('x')) || 0;\n originalY = parseFloat(selectedIcon.getAttribute('y')) || 0;\n originalWidth = parseFloat(selectedIcon.getAttribute('width')) || placedIconSize;\n originalHeight = parseFloat(selectedIcon.getAttribute('height')) || placedIconSize;\n\n const { x, y } = getSVGCoordsFromMouse(e);\n dragOffsetX = x - originalX;\n dragOffsetY = y - originalY;\n\n const initialMouseX = x;\n const initialMouseY = y;\n\n // Threshold in screen pixels converted to SVG units, so zoom doesn't affect sensitivity\n const svgEl = getSVGElement();\n const svgRect2 = svgEl ? svgEl.getBoundingClientRect() : { width: 1536 };\n const svgViewW = svgEl ? svgEl.viewBox.baseVal.width : 1536;\n const dragThreshold = 12 * (svgViewW / svgRect2.width); // ~12 screen pixels\n\n function checkDragStartWithThreshold(moveEvent) {\n const { x: currentX, y: currentY } = getSVGCoordsFromMouse(moveEvent);\n const deltaX = Math.abs(currentX - initialMouseX);\n const deltaY = Math.abs(currentY - initialMouseY);\n\n if (deltaX > dragThreshold || deltaY > dragThreshold) {\n document.removeEventListener('pointermove', checkDragStartWithThreshold);\n document.removeEventListener('pointerup', cancelDragPrep);\n window.removeEventListener('pointerup', cancelDragPrep);\n\n const svg = getSVGElement();\n if (svg) svg.removeEventListener('pointerup', cancelDragPrep);\n\n startDrag(moveEvent);\n }\n }\n\n _pendingDragChecker = checkDragStartWithThreshold;\n document.addEventListener('pointermove', checkDragStartWithThreshold);\n document.addEventListener('pointerup', cancelDragPrep);\n window.addEventListener('pointerup', cancelDragPrep);\n\n const svg = getSVGElement();\n if (svg) svg.addEventListener('pointerup', cancelDragPrep);\n\n return;\n } else {\n selectIcon(e);\n return;\n }\n }\n\n // Clicked on empty canvas while an icon is selected \u2014 deselect it\n if (selectedIcon) {\n removeSelection();\n selectedIcon = null;\n if (currentShape && currentShape.shapeName === 'icon') {\n currentShape = null;\n }\n }\n return;\n }\n\n if (!isDraggingIcon || !iconToPlace || !isIconToolActive) {\n return;\n }\n\n let placedIconShape = null;\n\n try {\n const svg = getSVGElement();\n if (!svg) {\n throw new Error('SVG element not found');\n }\n\n if (currentIconElement) {\n svg.removeChild(currentIconElement);\n currentIconElement = null;\n }\n\n const { x: placedX, y: placedY } = getSVGCoordsFromMouse(e);\n\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = iconToPlace;\n const originalSvgElement = tempDiv.querySelector('svg');\n\n if (!originalSvgElement) {\n throw new Error('Invalid SVG content');\n }\n\n const finalIconGroup = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n const finalX = placedX - placedIconSize / 2;\n const finalY = placedY - placedIconSize / 2;\n\n const viewBox = originalSvgElement.getAttribute('viewBox');\n let vbWidth = 24, vbHeight = 24;\n\n if (viewBox) {\n const [, , widthStr, heightStr] = viewBox.split(/\\s+/);\n vbWidth = parseFloat(widthStr) || 24;\n vbHeight = parseFloat(heightStr) || 24;\n } else {\n const width = originalSvgElement.getAttribute('width');\n const height = originalSvgElement.getAttribute('height');\n if (width && height) {\n vbWidth = parseFloat(width) || 24;\n vbHeight = parseFloat(height) || 24;\n }\n }\n\n const scale = placedIconSize / Math.max(vbWidth, vbHeight);\n const localCenterX = placedIconSize / 2 / scale;\n const localCenterY = placedIconSize / 2 / scale;\n finalIconGroup.setAttribute('transform', `translate(${finalX}, ${finalY}) scale(${scale}) rotate(0, ${localCenterX}, ${localCenterY})`);\n finalIconGroup.setAttribute('data-viewbox-width', vbWidth);\n finalIconGroup.setAttribute('data-viewbox-height', vbHeight);\n\n const backgroundRect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n backgroundRect.setAttribute('x', 0);\n backgroundRect.setAttribute('y', 0);\n backgroundRect.setAttribute('width', vbWidth);\n backgroundRect.setAttribute('height', vbHeight);\n backgroundRect.setAttribute('fill', 'transparent');\n backgroundRect.setAttribute('stroke', 'none');\n backgroundRect.setAttribute('style', 'pointer-events: all; cursor: pointer;');\n finalIconGroup.appendChild(backgroundRect);\n\n const allChildren = originalSvgElement.children;\n for (let i = 0; i < allChildren.length; i++) {\n const clonedChild = allChildren[i].cloneNode(true);\n\n // Apply white fill/stroke so icons are visible on dark canvas\n const applyWhiteStyle = (element) => {\n if (element.nodeType === 1) {\n const fill = element.getAttribute('fill');\n const stroke = element.getAttribute('stroke');\n // Replace black/dark fills with white; leave 'none'/'transparent' alone\n if (!fill || fill === '#000' || fill === '#000000' || fill === 'black' || fill === 'currentColor') {\n element.setAttribute('fill', '#ffffff');\n }\n if (stroke === '#000' || stroke === '#000000' || stroke === 'black' || stroke === 'currentColor') {\n element.setAttribute('stroke', '#ffffff');\n }\n for (let j = 0; j < element.children.length; j++) {\n applyWhiteStyle(element.children[j]);\n }\n }\n };\n applyWhiteStyle(clonedChild);\n\n finalIconGroup.appendChild(clonedChild);\n }\n\n finalIconGroup.setAttribute('x', finalX);\n finalIconGroup.setAttribute('y', finalY);\n finalIconGroup.setAttribute('width', placedIconSize);\n finalIconGroup.setAttribute('height', placedIconSize);\n finalIconGroup.setAttribute('type', 'icon');\n finalIconGroup.setAttribute('data-shape-x', finalX);\n finalIconGroup.setAttribute('data-shape-y', finalY);\n finalIconGroup.setAttribute('data-shape-width', placedIconSize);\n finalIconGroup.setAttribute('data-shape-height', placedIconSize);\n finalIconGroup.setAttribute('data-shape-rotation', 0);\n finalIconGroup.shapeID = `icon-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n finalIconGroup.setAttribute('id', finalIconGroup.shapeID);\n finalIconGroup.setAttribute('style', 'cursor: pointer; pointer-events: all;');\n\n svg.appendChild(finalIconGroup);\n\n const iconShape = wrapIconElement(finalIconGroup);\n placedIconShape = iconShape;\n\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n // Prevent duplicates \u2014 check if this element is already tracked\n const alreadyExists = shapes.some(s => s.shapeName === 'icon' && s.element === finalIconGroup);\n if (!alreadyExists) {\n shapes.push(iconShape);\n }\n }\n\n const finalFrame = hoveredFrameIcon;\n if (finalFrame) {\n finalFrame.addShapeToFrame(iconShape);\n pushFrameAttachmentAction(finalFrame, iconShape, 'attach', null);\n }\n\n pushCreateAction(iconShape);\n\n // mousedown handles selection; click listener removed to avoid double-selection conflicts\n\n if (hoveredFrameIcon) {\n hoveredFrameIcon.removeHighlight();\n hoveredFrameIcon = null;\n if (window.__iconShapeState) window.__iconShapeState.hoveredFrameIcon = null;\n }\n\n console.log('Icon placed successfully:', finalIconGroup);\n\n } catch (error) {\n console.error(\"Error placing icon:\", error);\n isDraggingIcon = false;\n iconToPlace = null;\n } finally {\n isDraggingIcon = false;\n iconToPlace = null;\n document.body.style.cursor = 'default';\n }\n\n // Select the placed icon but stay in icon tool so the sidebar remains open\n if (placedIconShape) {\n currentShape = placedIconShape;\n currentShape.isSelected = true;\n requestAnimationFrame(() => {\n placedIconShape.selectShape();\n });\n }\n};\n\nconst handleMouseUpIcon = (e) => {\n if (!e.target) return;\n // Deselection on empty-canvas click is handled by handleMouseDownIcon.\n // We intentionally do NOT deselect on mouseup \u2014 after a drag the cursor\n // often ends up on the background, and that shouldn't kill the selection.\n\n if (hoveredFrameIcon) {\n hoveredFrameIcon.removeHighlight();\n hoveredFrameIcon = null;\n }\n};\n\nfunction addSelectionOutline() {\n if (!selectedIcon) return;\n\n const svg = getSVGElement();\n if (!svg) return;\n\n // Use stored SVG-space data attributes \u2014 reliable even immediately after DOM insertion\n const x = parseFloat(selectedIcon.getAttribute('data-shape-x')) || 0;\n const y = parseFloat(selectedIcon.getAttribute('data-shape-y')) || 0;\n const width = parseFloat(selectedIcon.getAttribute('data-shape-width')) || 40;\n const height = parseFloat(selectedIcon.getAttribute('data-shape-height')) || 40;\n const rotation = parseFloat(selectedIcon.getAttribute('data-shape-rotation')) || 0;\n\n const centerX = x + width / 2;\n const centerY = y + height / 2;\n\n const zoom = window.currentZoom || 1;\n const selectionPadding = 8 / zoom;\n const expandedX = x - selectionPadding;\n const expandedY = y - selectionPadding;\n const expandedWidth = width + 2 * selectionPadding;\n const expandedHeight = height + 2 * selectionPadding;\n\n removeSelection();\n\n const outline = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n outline.setAttribute(\"x\", expandedX);\n outline.setAttribute(\"y\", expandedY);\n outline.setAttribute(\"width\", expandedWidth);\n outline.setAttribute(\"height\", expandedHeight);\n outline.setAttribute(\"fill\", \"none\");\n outline.setAttribute(\"stroke\", \"#5B57D1\");\n outline.setAttribute(\"stroke-width\", 1.5);\n outline.setAttribute(\"stroke-dasharray\", \"4 3\");\n outline.setAttribute(\"style\", \"pointer-events: none;\");\n outline.setAttribute(\"class\", \"selection-outline\");\n if (rotation !== 0) {\n outline.setAttribute(\"transform\", `rotate(${rotation}, ${centerX}, ${centerY})`);\n }\n\n svg.appendChild(outline);\n\n addResizeAnchors(expandedX, expandedY, expandedWidth, expandedHeight, centerX, centerY, width, rotation);\n addRotationAnchor(expandedX, expandedY, expandedWidth, expandedHeight, centerX, centerY, width, rotation);\n}\n\nfunction checkDragStart(event) {\n document.removeEventListener('pointermove', checkDragStart);\n document.removeEventListener('pointerup', cancelDragPrep);\n startDrag(event);\n}\nfunction cancelDragPrep(event) {\n if (_pendingDragChecker) {\n document.removeEventListener('pointermove', _pendingDragChecker);\n _pendingDragChecker = null;\n }\n document.removeEventListener('pointerup', cancelDragPrep);\n window.removeEventListener('pointerup', cancelDragPrep);\n\n const svg = getSVGElement();\n if (svg) {\n svg.removeEventListener('pointerup', cancelDragPrep);\n }\n}\n\nfunction startDrag(event) {\n console.log(\"start dragging icon\");\n if (!isSelectionToolActive || !selectedIcon) return;\n \n isDragging = true;\n if (window.__iconShapeState) window.__iconShapeState.isDragging = true;\n\n let iconShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n iconShape = shapes.find(shape => shape.shapeName === 'icon' && shape.element === selectedIcon);\n }\n\n if (iconShape) {\n draggedShapeInitialFrameIcon = iconShape.parentFrame || null;\n\n if (iconShape.parentFrame) {\n iconShape.parentFrame.temporarilyRemoveFromFrame(iconShape);\n }\n }\n\n document.addEventListener('pointermove', dragIcon);\n document.addEventListener('pointerup', stopDrag);\n \n window.addEventListener('pointerup', stopDrag);\n \n const svg = getSVGElement();\n if (svg) {\n svg.addEventListener('pointerup', stopDrag);\n }\n \n document.addEventListener('dragstart', preventDefaultDrag);\n document.addEventListener('selectstart', preventDefaultDrag);\n}\n\nfunction preventDefaultDrag(e) {\n e.preventDefault();\n return false;\n}\n\nfunction selectIcon(event) {\n if (!isSelectionToolActive) return;\n\n event.stopPropagation();\n\n let targetIcon = event.target.closest ? event.target.closest('[type=\"icon\"]') : null;\n if (!targetIcon) {\n let current = event.target;\n while (current && current !== document) {\n if (current.getAttribute && current.getAttribute('type') === 'icon') {\n targetIcon = current;\n break;\n }\n current = current.parentElement;\n }\n }\n\n // Always clean up previous selection before applying new one\n if (selectedIcon) {\n removeSelection();\n }\n\n selectedIcon = targetIcon;\n\n if (!selectedIcon) {\n console.warn('Could not find icon to select');\n return;\n }\n\n const transform = selectedIcon.getAttribute('transform');\n if (transform) {\n const rotateMatch = transform.match(/rotate\\(([^,\\s]+)/);\n if (rotateMatch) {\n iconRotation = parseFloat(rotateMatch[1]);\n }\n } else {\n iconRotation = 0;\n }\n\n addSelectionOutline();\n\n originalX = parseFloat(selectedIcon.getAttribute('x')) || 0;\n originalY = parseFloat(selectedIcon.getAttribute('y')) || 0;\n originalWidth = parseFloat(selectedIcon.getAttribute('width')) || placedIconSize;\n originalHeight = parseFloat(selectedIcon.getAttribute('height')) || placedIconSize;\n\n // Set currentShape so EventDispatcher routes subsequent events to icon handler\n const iconShape = (typeof shapes !== 'undefined' && Array.isArray(shapes))\n ? shapes.find(s => s.shapeName === 'icon' && s.element === selectedIcon)\n : null;\n if (iconShape) {\n currentShape = iconShape;\n currentShape.isSelected = true;\n if (window.__showSidebarForShape) window.__showSidebarForShape('icon');\n }\n}\n\nfunction addResizeAnchors(x, y, width, height, centerX, centerY, iconWidth, rotation) {\n const svg = getSVGElement();\n if (!svg) return;\n\n const zoom = window.currentZoom || 1;\n const anchorSize = 10 / zoom;\n const anchorStrokeWidth = 2 / zoom;\n\n const positions = [\n { x: x, y: y, cursor: \"nw-resize\" },\n { x: x + width, y: y, cursor: \"ne-resize\" },\n { x: x, y: y + height, cursor: \"sw-resize\" },\n { x: x + width, y: y + height, cursor: \"se-resize\" }\n ];\n\n positions.forEach((pos) => {\n const anchor = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n anchor.setAttribute(\"x\", pos.x - anchorSize / 2);\n anchor.setAttribute(\"y\", pos.y - anchorSize / 2);\n anchor.setAttribute(\"width\", anchorSize);\n anchor.setAttribute(\"height\", anchorSize);\n anchor.setAttribute(\"fill\", \"#121212\");\n anchor.setAttribute(\"stroke\", \"#5B57D1\");\n anchor.setAttribute(\"stroke-width\", anchorStrokeWidth);\n anchor.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n anchor.setAttribute(\"class\", \"resize-anchor\");\n anchor.setAttribute(\"transform\", `rotate(${rotation}, ${centerX}, ${centerY})`);\n anchor.style.cursor = pos.cursor;\n\n svg.appendChild(anchor);\n\n anchor.addEventListener('pointerdown', startResize);\n anchor.addEventListener('pointerup', stopResize);\n });\n}\n\nfunction addRotationAnchor(x, y, width, height, centerX, centerY, iconWidth, rotation) {\n const svg = getSVGElement();\n if (!svg) return;\n\n const zoom = window.currentZoom || 1;\n const anchorRadius = 5 / zoom;\n const anchorStrokeWidth = 2 / zoom;\n const rotationDistance = 30 / zoom;\n\n const rotationAnchorX = x + width / 2;\n const rotationAnchorY = y - rotationDistance;\n\n const rotationAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n rotationAnchor.setAttribute('cx', rotationAnchorX);\n rotationAnchor.setAttribute('cy', rotationAnchorY);\n rotationAnchor.setAttribute('r', anchorRadius);\n rotationAnchor.setAttribute('class', 'rotation-anchor');\n rotationAnchor.setAttribute('fill', '#121212');\n rotationAnchor.setAttribute('stroke', '#5B57D1');\n rotationAnchor.setAttribute('stroke-width', anchorStrokeWidth);\n rotationAnchor.setAttribute('vector-effect', 'non-scaling-stroke');\n rotationAnchor.setAttribute('style', 'pointer-events: all; cursor: grab;');\n rotationAnchor.setAttribute('transform', `rotate(${rotation}, ${centerX}, ${centerY})`);\n\n svg.appendChild(rotationAnchor);\n registerRotationAnchor(rotationAnchor, { radius: 5, edgeY: y });\n\n rotationAnchor.addEventListener('pointerdown', startRotation);\n rotationAnchor.addEventListener('pointerup', stopRotation);\n\n rotationAnchor.addEventListener('mouseover', function() {\n if (!isRotatingIcon && !isDragging) {\n rotationAnchor.style.cursor = 'grab';\n }\n });\n\n rotationAnchor.addEventListener('mouseout', function() {\n if (!isRotatingIcon && !isDragging) {\n rotationAnchor.style.cursor = 'default';\n }\n });\n}\n\nfunction removeRotationAnchor() {\n const svg = getSVGElement();\n if (!svg) return;\n\n svg.querySelectorAll(\".rotation-anchor\").forEach(el => el.remove());\n}\n\nfunction removeResizeAnchors() {\n const svg = getSVGElement();\n if (!svg) return;\n\n const anchors = svg.querySelectorAll(\".resize-anchor\");\n anchors.forEach(anchor => svg.removeChild(anchor));\n}\n\nfunction startResize(event) {\n event.preventDefault();\n event.stopPropagation();\n\n currentAnchor = event.target;\n\n originalX = parseFloat(selectedIcon.getAttribute('x')) || 0;\n originalY = parseFloat(selectedIcon.getAttribute('y')) || 0;\n originalWidth = parseFloat(selectedIcon.getAttribute('width')) || 100;\n originalHeight = parseFloat(selectedIcon.getAttribute('height')) || 100;\n\n const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(event);\n currentAnchor.startMouseX = mouseX;\n currentAnchor.startMouseY = mouseY;\n\n currentAnchor.iconRotation = iconRotation;\n\n const svg = getSVGElement();\n if (svg) {\n svg.addEventListener('pointermove', resizeIcon);\n }\n document.addEventListener('pointerup', stopResize);\n}\n\nfunction stopResize(event) {\n stopInteracting();\n document.removeEventListener('pointerup', stopResize);\n}\n\nfunction resizeIcon(event) {\n if (!selectedIcon || !currentAnchor) return;\n\n const { x: currentMouseX, y: currentMouseY } = getSVGCoordsFromMouse(event);\n\n const rotation = currentAnchor.iconRotation || 0;\n const rotationRad = (rotation * Math.PI) / 180;\n\n const rawDeltaX = currentMouseX - currentAnchor.startMouseX;\n const rawDeltaY = currentMouseY - currentAnchor.startMouseY;\n\n // Rotate delta to local (unrotated) space\n const deltaX = rawDeltaX * Math.cos(-rotationRad) - rawDeltaY * Math.sin(-rotationRad);\n const deltaY = rawDeltaX * Math.sin(-rotationRad) + rawDeltaY * Math.cos(-rotationRad);\n\n let newWidth = originalWidth;\n let newHeight = originalHeight;\n let newX = originalX;\n let newY = originalY;\n\n // Track which corner is fixed (relative to original rect)\n let fixedRelX, fixedRelY;\n\n switch (currentAnchor.style.cursor) {\n case \"nw-resize\":\n newWidth = Math.max(minIconSize, originalWidth - deltaX);\n newHeight = Math.max(minIconSize, originalHeight - deltaY);\n if (aspect_ratio_lock) {\n const scale = Math.min(newWidth / originalWidth, newHeight / originalHeight);\n newWidth = originalWidth * scale;\n newHeight = originalHeight * scale;\n }\n newX = originalX + (originalWidth - newWidth);\n newY = originalY + (originalHeight - newHeight);\n fixedRelX = originalWidth; fixedRelY = originalHeight;\n break;\n case \"ne-resize\":\n newWidth = Math.max(minIconSize, originalWidth + deltaX);\n newHeight = Math.max(minIconSize, originalHeight - deltaY);\n if (aspect_ratio_lock) {\n const scale = Math.max(newWidth / originalWidth, newHeight / originalHeight);\n newWidth = originalWidth * scale;\n newHeight = originalHeight * scale;\n }\n newX = originalX;\n newY = originalY + (originalHeight - newHeight);\n fixedRelX = 0; fixedRelY = originalHeight;\n break;\n case \"sw-resize\":\n newWidth = Math.max(minIconSize, originalWidth - deltaX);\n newHeight = Math.max(minIconSize, originalHeight + deltaY);\n if (aspect_ratio_lock) {\n const scale = Math.max(newWidth / originalWidth, newHeight / originalHeight);\n newWidth = originalWidth * scale;\n newHeight = originalHeight * scale;\n }\n newX = originalX + (originalWidth - newWidth);\n newY = originalY;\n fixedRelX = originalWidth; fixedRelY = 0;\n break;\n case \"se-resize\":\n newWidth = Math.max(minIconSize, originalWidth + deltaX);\n newHeight = Math.max(minIconSize, originalHeight + deltaY);\n if (aspect_ratio_lock) {\n const scale = Math.max(newWidth / originalWidth, newHeight / originalHeight);\n newWidth = originalWidth * scale;\n newHeight = originalHeight * scale;\n }\n newX = originalX;\n newY = originalY;\n fixedRelX = 0; fixedRelY = 0;\n break;\n }\n\n // Compensate for rotation center shift when rotated\n if (rotation !== 0) {\n const cosR = Math.cos(rotationRad);\n const sinR = Math.sin(rotationRad);\n\n // Compute rotated world position of the fixed corner in the original rect\n const oldCX = originalX + originalWidth / 2;\n const oldCY = originalY + originalHeight / 2;\n const odx = (originalX + fixedRelX) - oldCX;\n const ody = (originalY + fixedRelY) - oldCY;\n const fixedWorldX = oldCX + odx * cosR - ody * sinR;\n const fixedWorldY = oldCY + odx * sinR + ody * cosR;\n\n // Determine where the fixed corner sits in the new rect\n const fixedNewRelX = fixedRelX === 0 ? 0 : newWidth;\n const fixedNewRelY = fixedRelY === 0 ? 0 : newHeight;\n\n // Solve for new origin so the fixed corner stays in place\n const ncx = newWidth / 2;\n const ncy = newHeight / 2;\n const ndx = fixedNewRelX - ncx;\n const ndy = fixedNewRelY - ncy;\n const rotX = ncx + ndx * cosR - ndy * sinR;\n const rotY = ncy + ndx * sinR + ndy * cosR;\n\n newX = fixedWorldX - rotX;\n newY = fixedWorldY - rotY;\n }\n\n selectedIcon.setAttribute('width', newWidth);\n selectedIcon.setAttribute('height', newHeight);\n selectedIcon.setAttribute('x', newX);\n selectedIcon.setAttribute('y', newY);\n\n selectedIcon.setAttribute('data-shape-x', newX);\n selectedIcon.setAttribute('data-shape-y', newY);\n selectedIcon.setAttribute('data-shape-width', newWidth);\n selectedIcon.setAttribute('data-shape-height', newHeight);\n\n const vbWidth = parseFloat(selectedIcon.getAttribute('data-viewbox-width')) || 24;\n const vbHeight = parseFloat(selectedIcon.getAttribute('data-viewbox-height')) || 24;\n const iconScale = newWidth / Math.max(vbWidth, vbHeight);\n const localCenterX = newWidth / 2 / iconScale;\n const localCenterY = newHeight / 2 / iconScale;\n selectedIcon.setAttribute('transform', `translate(${newX}, ${newY}) scale(${iconScale}) rotate(${iconRotation}, ${localCenterX}, ${localCenterY})`);\n\n updateArrowsForShape(selectedIcon);\n\n addSelectionOutline();\n}\n\nfunction dragIcon(event) {\n if (!isDragging || !selectedIcon) return;\n\n const { x, y } = getSVGCoordsFromMouse(event);\n let newX = x - dragOffsetX;\n let newY = y - dragOffsetY;\n\n selectedIcon.setAttribute('x', newX);\n selectedIcon.setAttribute('y', newY);\n\n selectedIcon.setAttribute('data-shape-x', newX);\n selectedIcon.setAttribute('data-shape-y', newY);\n\n const width = parseFloat(selectedIcon.getAttribute('width')) || 100;\n const height = parseFloat(selectedIcon.getAttribute('height')) || 100;\n const vbWidth = parseFloat(selectedIcon.getAttribute('data-viewbox-width')) || 24;\n const vbHeight = parseFloat(selectedIcon.getAttribute('data-viewbox-height')) || 24;\n const scale = width / Math.max(vbWidth, vbHeight);\n const localCenterX = width / 2 / scale;\n const localCenterY = height / 2 / scale;\n \n selectedIcon.setAttribute('transform', `translate(${newX}, ${newY}) scale(${scale}) rotate(${iconRotation}, ${localCenterX}, ${localCenterY})`);\n\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n const iconShape = shapes.find(shape => shape.shapeName === 'icon' && shape.element === selectedIcon);\n if (iconShape) {\n iconShape.x = newX;\n iconShape.y = newY;\n iconShape.updateFrameContainment();\n }\n }\n\n updateArrowsForShape(selectedIcon);\n\n removeSelection();\n addSelectionOutline();\n}\n\nfunction stopDrag(event) {\n if (!isDragging) return;\n \n console.log(\"stop dragging icon\");\n isDragging = false;\n if (window.__iconShapeState) window.__iconShapeState.isDragging = false;\n\n document.removeEventListener('pointermove', dragIcon);\n document.removeEventListener('pointerup', stopDrag);\n window.removeEventListener('pointerup', stopDrag);\n \n const svg = getSVGElement();\n if (svg) {\n svg.removeEventListener('pointerup', stopDrag);\n }\n \n document.removeEventListener('dragstart', preventDefaultDrag);\n document.removeEventListener('selectstart', preventDefaultDrag);\n \n stopInteracting();\n}\n\nfunction startRotation(event) {\n event.preventDefault();\n event.stopPropagation();\n\n if (!selectedIcon) return;\n\n isRotatingIcon = true;\n console.log(selectedIcon)\n const iconX = parseFloat(selectedIcon.getAttribute('x'));\n const iconY = parseFloat(selectedIcon.getAttribute('y'));\n const iconWidth = parseFloat(selectedIcon.getAttribute('width'));\n const iconHeight = parseFloat(selectedIcon.getAttribute('height'));\n\n const centerX = iconX + iconWidth / 2;\n const centerY = iconY + iconHeight / 2;\n\n const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(event);\n\n startRotationMouseAngle = Math.atan2(mouseY - centerY, mouseX - centerX) * 180 / Math.PI;\n console.log('Start rotation mouse angle:', startRotationMouseAngle);\n startIconRotation = iconRotation;\n\n const svg = getSVGElement();\n if (svg) {\n svg.addEventListener('pointermove', rotateIcon);\n svg.style.cursor = 'grabbing';\n }\n document.addEventListener('pointerup', stopRotation);\n}\n\nfunction rotateIcon(event) {\n if (!isRotatingIcon || !selectedIcon) return;\n\n const iconX = parseFloat(selectedIcon.getAttribute('x')) || 0;\n const iconY = parseFloat(selectedIcon.getAttribute('y')) || 0;\n const iconWidth = parseFloat(selectedIcon.getAttribute('width')) || 100;\n const iconHeight = parseFloat(selectedIcon.getAttribute('height')) || 100;\n\n const centerX = iconX + iconWidth / 2;\n const centerY = iconY + iconHeight / 2;\n\n const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(event);\n\n const currentMouseAngle = Math.atan2(mouseY - centerY, mouseX - centerX) * 180 / Math.PI;\n const angleDiff = currentMouseAngle - startRotationMouseAngle;\n\n iconRotation = startIconRotation + angleDiff;\n iconRotation = iconRotation % 360;\n if (iconRotation < 0) iconRotation += 360;\n\n const vbWidth = parseFloat(selectedIcon.getAttribute('data-viewbox-width')) || 24;\n const vbHeight = parseFloat(selectedIcon.getAttribute('data-viewbox-height')) || 24;\n const scale = iconWidth / Math.max(vbWidth, vbHeight);\n const localCenterX = iconWidth / 2 / scale;\n const localCenterY = iconHeight / 2 / scale;\n\n selectedIcon.setAttribute('transform', `translate(${iconX}, ${iconY}) scale(${scale}) rotate(${iconRotation}, ${localCenterX}, ${localCenterY})`);\n\n selectedIcon.setAttribute('data-shape-rotation', iconRotation);\n\n updateArrowsForShape(selectedIcon);\n\n removeSelection();\n addSelectionOutline();\n}\n\nfunction stopRotation(event) {\n if (!isRotatingIcon) return;\n stopInteracting();\n isRotatingIcon = false;\n startRotationMouseAngle = null;\n startIconRotation = null;\n\n const svg = getSVGElement();\n if (svg) {\n svg.removeEventListener('pointermove', rotateIcon);\n svg.style.cursor = 'default';\n }\n document.removeEventListener('pointerup', stopRotation);\n}\n\nfunction stopInteracting() {\n if (selectedIcon && (isDragging || isRotatingIcon || currentAnchor)) {\n const newPos = {\n x: parseFloat(selectedIcon.getAttribute('x')) || 0,\n y: parseFloat(selectedIcon.getAttribute('y')) || 0,\n width: parseFloat(selectedIcon.getAttribute('width')) || 100,\n height: parseFloat(selectedIcon.getAttribute('height')) || 100,\n rotation: iconRotation\n };\n\n let originalRotation = iconRotation;\n if (isRotatingIcon && startIconRotation !== null) {\n originalRotation = startIconRotation;\n }\n\n const oldPos = {\n x: originalX,\n y: originalY,\n width: originalWidth,\n height: originalHeight,\n rotation: originalRotation\n };\n\n let iconShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n iconShape = shapes.find(shape => shape.shapeName === 'icon' && shape.element === selectedIcon);\n if (iconShape) {\n iconShape.x = newPos.x;\n iconShape.y = newPos.y;\n iconShape.width = newPos.width;\n iconShape.height = newPos.height;\n iconShape.rotation = newPos.rotation;\n }\n }\n\n const oldPosWithFrame = {\n ...oldPos,\n parentFrame: draggedShapeInitialFrameIcon\n };\n // Issue #34 bug #2: for drag, hoveredFrameIcon tracks the actual\n // destination \u2014 currentShape.parentFrame is still the OLD frame.\n // Resize / rotate don't track hover, so fall back to whatever the\n // shape itself reports (parent doesn't change for those gestures).\n const newPosWithFrame = {\n ...newPos,\n parentFrame: isDragging\n ? (hoveredFrameIcon || null)\n : (iconShape ? iconShape.parentFrame : null),\n };\n\n const stateChanged = newPos.x !== oldPos.x || newPos.y !== oldPos.y ||\n newPos.width !== oldPos.width || newPos.height !== oldPos.height ||\n newPos.rotation !== oldPos.rotation;\n const frameChanged = oldPosWithFrame.parentFrame !== newPosWithFrame.parentFrame;\n\n if ((stateChanged || frameChanged) && iconShape) {\n pushTransformAction(iconShape, oldPosWithFrame, newPosWithFrame);\n }\n\n if (isDragging && iconShape) {\n const finalFrame = hoveredFrameIcon;\n\n if (draggedShapeInitialFrameIcon !== finalFrame) {\n if (draggedShapeInitialFrameIcon) {\n draggedShapeInitialFrameIcon.removeShapeFromFrame(iconShape);\n }\n\n if (finalFrame) {\n finalFrame.addShapeToFrame(iconShape);\n }\n\n if (frameChanged) {\n pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameIcon, iconShape,\n finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameIcon);\n }\n } else if (draggedShapeInitialFrameIcon) {\n draggedShapeInitialFrameIcon.restoreToFrame(iconShape);\n }\n }\n\n draggedShapeInitialFrameIcon = null;\n }\n\n if (hoveredFrameIcon) {\n hoveredFrameIcon.removeHighlight();\n hoveredFrameIcon = null;\n if (window.__iconShapeState) window.__iconShapeState.hoveredFrameIcon = null;\n }\n\n isDragging = false;\n if (window.__iconShapeState) window.__iconShapeState.isDragging = false;\n isRotatingIcon = false;\n\n const svg = getSVGElement();\n if (svg) {\n svg.removeEventListener('pointermove', dragIcon);\n svg.removeEventListener('pointermove', resizeIcon);\n svg.removeEventListener('pointermove', rotateIcon);\n }\n \n document.removeEventListener('pointermove', dragIcon);\n document.removeEventListener('pointermove', resizeIcon);\n document.removeEventListener('pointermove', rotateIcon);\n \n currentAnchor = null;\n startRotationMouseAngle = null;\n startIconRotation = null;\n\n if (selectedIcon) {\n originalX = parseFloat(selectedIcon.getAttribute('x')) || 0;\n originalY = parseFloat(selectedIcon.getAttribute('y')) || 0;\n originalWidth = parseFloat(selectedIcon.getAttribute('width')) || 100;\n originalHeight = parseFloat(selectedIcon.getAttribute('height')) || 100;\n\n selectedIcon.setAttribute('data-shape-x', originalX);\n selectedIcon.setAttribute('data-shape-y', originalY);\n selectedIcon.setAttribute('data-shape-width', originalWidth);\n selectedIcon.setAttribute('data-shape-height', originalHeight);\n selectedIcon.setAttribute('data-shape-rotation', iconRotation);\n\n updateArrowsForShape(selectedIcon);\n }\n}\n\nfunction deleteCurrentIcon() {\n if (selectedIcon) {\n let iconShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n iconShape = shapes.find(shape => shape.shapeName === 'icon' && shape.element === selectedIcon);\n if (iconShape) {\n const idx = shapes.indexOf(iconShape);\n if (idx !== -1) shapes.splice(idx, 1);\n\n if (iconShape.group && iconShape.group.parentNode) {\n iconShape.group.parentNode.removeChild(iconShape.group);\n }\n }\n }\n\n if (!iconShape && selectedIcon.parentNode) {\n selectedIcon.parentNode.removeChild(selectedIcon);\n }\n\n if (typeof cleanupAttachments === 'function') {\n cleanupAttachments(selectedIcon);\n }\n\n if (iconShape) {\n pushDeleteAction(iconShape);\n }\n\n removeSelection();\n selectedIcon = null;\n }\n}\n\ndocument.addEventListener('keydown', (e) => {\n if (e.key === 'Delete' && selectedIcon) {\n deleteCurrentIcon();\n }\n});\n\nasync function fetchIconsFromServer() {\n try {\n const apiUrl = \"/api/icons/feed?offset=0&limit=20\";\n \n const response = await fetch(apiUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const data = await response.json();\n return data.results;\n } catch (error) {\n console.error('Failed to fetch icons from server:', error);\n return null;\n }\n}\n\nasync function renderIconsFromServer() {\n const icons = await fetchIconsFromServer();\n if (icons) {\n document.getElementById(\"iconsContainer\").innerHTML = '';\n\n for (const icon of icons) {\n try {\n const response = await fetch(`/api/icons/serve?name=${encodeURIComponent(icon.filename)}`);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const svgContent = await response.text();\n\n const normalizedSVG = normalizeSVGSize(svgContent);\n\n // Use normalizedSVG for display, not original svgContent\n let svgIcon = `<div class=\"icons\" data-url=\"${icon.filename}\" data-svg=\"${encodeURIComponent(normalizedSVG)}\">\n ${normalizedSVG}\n </div>`;\n document.getElementById(\"iconsContainer\").innerHTML += svgIcon;\n } catch (error) {\n console.error('Failed to render icon:', icon.filename, error);\n }\n }\n\n addIconClickListeners();\n }\n}\n\nasync function searchAndRenderIcons(query) {\n try {\n const apiUrl = `/api/icons/search?q=${encodeURIComponent(query)}`;\n \n const response = await fetch(apiUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const searchResults = await response.json();\n\n document.getElementById(\"iconsContainer\").innerHTML = '';\n\n for (const icon of searchResults) {\n try {\n const svgResponse = await fetch(`/api/icons/serve?name=${encodeURIComponent(icon.filename)}`);\n if (!svgResponse.ok) {\n throw new Error(`HTTP error! status: ${svgResponse.status}`);\n }\n const svgContent = await svgResponse.text();\n\n const normalizedSVG = normalizeSVGSize(svgContent);\n\n // Use normalizedSVG for display, not original svgContent\n let svgIcon = `<div class=\"icons\" data-url=\"${icon.filename}\">\n ${normalizedSVG}\n </div>`;\n document.getElementById(\"iconsContainer\").innerHTML += svgIcon;\n } catch (error) {\n console.error('Failed to render search result:', icon.filename, error);\n }\n }\n\n addIconClickListeners();\n\n } catch (error) {\n console.error('Failed to search icons:', error);\n }\n}\n\nfunction normalizeSVGSize(svgContent, fillColor = '#fff', strokeColor = null) {\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = svgContent;\n const svgElement = tempDiv.querySelector('svg');\n \n if (svgElement) {\n const originalViewBox = svgElement.getAttribute('viewBox');\n \n svgElement.setAttribute('width', '35');\n svgElement.setAttribute('height', '35');\n \n if (!originalViewBox) {\n const width = svgElement.getAttribute('width') || '24';\n const height = svgElement.getAttribute('height') || '24';\n const numWidth = parseFloat(width);\n const numHeight = parseFloat(height);\n \n if (!isNaN(numWidth) && !isNaN(numHeight)) {\n svgElement.setAttribute('viewBox', `0 0 ${numWidth} ${numHeight}`);\n }\n }\n \n // Apply custom styling with more selective logic\n const applyCustomStyling = (element) => {\n if (element.nodeType === 1) {\n const tagName = element.tagName.toLowerCase();\n \n // Only apply to shape elements, not container elements like 'g'\n if (['path', 'circle', 'rect', 'polygon', 'ellipse', 'polyline', 'line'].includes(tagName)) {\n const currentFill = element.getAttribute('fill');\n const currentStroke = element.getAttribute('stroke');\n \n // Check if parent <g> has explicit colors - if so, don't override\n let parentG = element.parentElement;\n let hasParentColor = false;\n \n while (parentG && parentG.tagName.toLowerCase() === 'g') {\n if (parentG.getAttribute('fill') || parentG.getAttribute('stroke')) {\n hasParentColor = true;\n break;\n }\n parentG = parentG.parentElement;\n }\n \n // Only apply white fill if:\n // 1. Element doesn't have explicit 'none' fill\n // 2. Element doesn't already have a specific color (other than black/default)\n // 3. Parent <g> doesn't have explicit colors\n if (!hasParentColor && currentFill !== 'none') {\n // Only override if it's black, default, or unset\n if (!currentFill || currentFill === '#000' || currentFill === '#000000' || currentFill === 'black' || currentFill === 'currentColor') {\n element.setAttribute('fill', fillColor);\n }\n }\n \n // Handle stroke similarly\n if (strokeColor && !hasParentColor) {\n if (currentStroke && currentStroke !== 'none') {\n if (!currentStroke || currentStroke === '#000' || currentStroke === '#000000' || currentStroke === 'black' || currentStroke === 'currentColor') {\n element.setAttribute('stroke', strokeColor);\n }\n }\n }\n }\n \n // Recursively process children\n for (let i = 0; i < element.children.length; i++) {\n applyCustomStyling(element.children[i]);\n }\n }\n };\n \n applyCustomStyling(svgElement);\n \n return svgElement.outerHTML;\n }\n\n return svgContent;\n}\n\nfunction addIconClickListeners() {\n const iconElements = document.querySelectorAll('#iconsContainer .icons');\n iconElements.forEach(iconElement => {\n iconElement.addEventListener('click', (e) => {\n const filename = iconElement.getAttribute('data-url') || 'unknown';\n handleIconClick(e, filename);\n });\n });\n}\n\n// Legacy category/search listeners disabled \u2014 React IconSidebar handles all icon UI\n\nfunction handleIconClick(event, filename) {\n event.stopPropagation();\n\n const iconElement = event.currentTarget;\n const svgElement = iconElement.querySelector('svg');\n\n if (svgElement) {\n iconToPlace = svgElement.outerHTML;\n isDraggingIcon = true;\n isIconToolActive = true;\n\n console.log('Icon selected and ready to place:', filename);\n\n const iconContainer = document.getElementById('iconsToolBar');\n if (iconContainer) {\n iconContainer.classList.add('hidden');\n }\n\n document.body.style.cursor = 'crosshair';\n }\n}\n\n\n\n// Clean up any lingering miniature icon or drag state (called on tool switch)\nfunction cleanupIconTool() {\n if (currentIconElement) {\n const svg = getSVGElement();\n if (svg && currentIconElement.parentNode === svg) {\n svg.removeChild(currentIconElement);\n }\n currentIconElement = null;\n }\n isDraggingIcon = false;\n iconToPlace = null;\n document.body.style.cursor = 'default';\n}\nwindow.__cleanupIconTool = cleanupIconTool;\n\n// Bridge for React sidebar to trigger icon placement\nwindow.prepareIconPlacement = function(svgContent) {\n iconToPlace = svgContent;\n isDraggingIcon = true;\n window.isIconToolActive = true;\n document.body.style.cursor = 'crosshair';\n};\n\n// Bridge for IconShape to call selectIcon and removeSelection\nwindow.__iconToolSelectIcon = selectIcon;\nwindow.__iconToolRemoveSelection = removeSelection;\n\n// Legacy icon rendering disabled \u2014 React IconSidebar handles icon UI\n\nexport { handleMouseDownIcon, handleMouseMoveIcon, handleMouseUpIcon, startDrag, stopDrag}\n"],
5
+ "mappings": "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",
6
+ "names": ["svg", "iconX", "iconY"]
7
+ }
@@ -1,4 +1,7 @@
1
1
  "use client";
2
+ import {
3
+ registerRotationAnchor
4
+ } from "./chunk-PAKA6HUX.js";
2
5
  import {
3
6
  cleanupAttachments,
4
7
  updateAttachedArrows
@@ -756,6 +759,7 @@ function createCodeSelectionFeedback(groupElement) {
756
759
  rotationAnchor.style.cursor = "grab";
757
760
  rotationAnchor.setAttribute("pointer-events", "all");
758
761
  groupElement.appendChild(rotationAnchor);
762
+ registerRotationAnchor(rotationAnchor, { radius: 8, edgeY: selY });
759
763
  codeResizeHandles.rotate = rotationAnchor;
760
764
  rotationAnchor.addEventListener("pointerdown", (e) => {
761
765
  if (window.isSelectionToolActive) {
@@ -816,7 +820,7 @@ function updateCodeSelectionFeedback() {
816
820
  });
817
821
  const rotationAnchor = codeResizeHandles.rotate;
818
822
  if (rotationAnchor) {
819
- const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };
823
+ const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 / zoom };
820
824
  rotationAnchor.setAttribute("cx", rotationAnchorPos.x);
821
825
  rotationAnchor.setAttribute("cy", rotationAnchorPos.y);
822
826
  }
@@ -1597,4 +1601,4 @@ export {
1597
1601
  getCodeLanguage,
1598
1602
  getSelectedCodeBlock
1599
1603
  };
1600
- //# sourceMappingURL=chunk-3VSGFP6U.js.map
1604
+ //# sourceMappingURL=chunk-73VLPCBC.js.map