@elixpo/lixsketch 5.5.3 → 5.5.5

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.
@@ -4,7 +4,7 @@ import {
4
4
  handleTextMouseDown,
5
5
  handleTextMouseMove,
6
6
  handleTextMouseUp
7
- } from "./chunk-VRRJEOCH.js";
7
+ } from "./chunk-TUMWA4YF.js";
8
8
  import {
9
9
  handleCodeMouseDown,
10
10
  handleCodeMouseMove,
@@ -460,4 +460,4 @@ export {
460
460
  handleMainMouseUp,
461
461
  initEventDispatcher
462
462
  };
463
- //# sourceMappingURL=EventDispatcher-5FYHE47T.js.map
463
+ //# sourceMappingURL=EventDispatcher-RTXLMHPL.js.map
@@ -209,7 +209,7 @@ var SketchEngine = class {
209
209
  import("./circleTool-NUATSEI2.js"),
210
210
  import("./arrowTool-CWEMIRXK.js"),
211
211
  import("./lineTool-WNI5EMZ5.js"),
212
- import("./textTool-7LMEIBF5.js"),
212
+ import("./textTool-QVKV45VD.js"),
213
213
  import("./codeTool-MHCB2FNB.js"),
214
214
  import("./imageTool-D5R4XGEU.js"),
215
215
  import("./iconTool-GPZ7O7NH.js"),
@@ -238,7 +238,7 @@ var SketchEngine = class {
238
238
  resizeShapes,
239
239
  resizeCode
240
240
  ] = await Promise.all([
241
- import("./EventDispatcher-5FYHE47T.js"),
241
+ import("./EventDispatcher-RTXLMHPL.js"),
242
242
  import("./UndoRedo-M2JZUBKN.js"),
243
243
  import("./Selection-VVUHY5YJ.js"),
244
244
  import("./ZoomPan-U3TXC5AJ.js"),
@@ -410,4 +410,4 @@ export {
410
410
  SketchEngine,
411
411
  SketchEngine_default as default
412
412
  };
413
- //# sourceMappingURL=SketchEngine-R5RCQF3L.js.map
413
+ //# sourceMappingURL=SketchEngine-DWDJP2BB.js.map
@@ -157,16 +157,19 @@ function makeTextEditable(textElement, groupElement) {
157
157
  let input = document.createElement("textarea");
158
158
  input.className = "svg-text-editor";
159
159
  let textContent = "";
160
- const tspans = textElement.querySelectorAll("tspan");
161
- if (tspans.length > 0) {
162
- tspans.forEach((tspan, index) => {
163
- textContent += tspan.textContent.replace(/ /g, "\xA0");
164
- if (index < tspans.length - 1) {
165
- textContent += "\n";
166
- }
167
- });
160
+ const storedSource = textElement.getAttribute("data-wrap-source");
161
+ if (storedSource !== null && storedSource !== "") {
162
+ textContent = storedSource.replace(/ /g, "\xA0");
168
163
  } else {
169
- textContent = textElement.textContent.replace(/ /g, "\xA0");
164
+ const tspans = textElement.querySelectorAll("tspan");
165
+ if (tspans.length > 0) {
166
+ tspans.forEach((tspan, index) => {
167
+ textContent += tspan.textContent.replace(/ /g, "\xA0");
168
+ if (index < tspans.length - 1) textContent += "\n";
169
+ });
170
+ } else {
171
+ textContent = textElement.textContent.replace(/ /g, "\xA0");
172
+ }
170
173
  }
171
174
  input.value = textContent;
172
175
  input.style.position = "absolute";
@@ -270,6 +273,64 @@ function makeTextEditable(textElement, groupElement) {
270
273
  if (textEl) textEl.setAttribute("cursor", "text");
271
274
  groupElement.style.display = "none";
272
275
  }
276
+ function measureSegment(text, refTextElement) {
277
+ if (!text) return 0;
278
+ const NS = "http://www.w3.org/2000/svg";
279
+ const probe = document.createElementNS(NS, "tspan");
280
+ probe.textContent = text;
281
+ refTextElement.appendChild(probe);
282
+ let width = 0;
283
+ try {
284
+ width = probe.getComputedTextLength();
285
+ } catch {
286
+ }
287
+ refTextElement.removeChild(probe);
288
+ return width;
289
+ }
290
+ function paintTextContent(textElement, source, wrapWidth) {
291
+ while (textElement.firstChild) textElement.removeChild(textElement.firstChild);
292
+ const x = textElement.getAttribute("x") || 0;
293
+ const NS = "http://www.w3.org/2000/svg";
294
+ const paragraphs = (source || "").split("\n");
295
+ const finalLines = [];
296
+ for (const paragraph of paragraphs) {
297
+ const para = paragraph.replace(/ /g, " ");
298
+ if (!wrapWidth || wrapWidth <= 0) {
299
+ finalLines.push(para || " ");
300
+ continue;
301
+ }
302
+ const words = para.split(/(\s+)/);
303
+ let cur = "";
304
+ for (const token of words) {
305
+ const candidate = cur + token;
306
+ if (measureSegment(candidate, textElement) > wrapWidth && cur.trim().length > 0) {
307
+ finalLines.push(cur);
308
+ cur = token.replace(/^\s+/, "");
309
+ } else {
310
+ cur = candidate;
311
+ }
312
+ }
313
+ finalLines.push(cur.length ? cur : " ");
314
+ }
315
+ finalLines.forEach((line, i) => {
316
+ const tspan = document.createElementNS(NS, "tspan");
317
+ tspan.setAttribute("x", x);
318
+ tspan.setAttribute("dy", i === 0 ? "0" : "1.2em");
319
+ tspan.textContent = line.length ? line : " ";
320
+ textElement.appendChild(tspan);
321
+ });
322
+ }
323
+ function getWrapSource(textElement) {
324
+ const stored = textElement.getAttribute("data-wrap-source");
325
+ if (stored !== null && stored !== "") return stored;
326
+ const tspans = textElement.querySelectorAll("tspan");
327
+ if (tspans.length === 0) return textElement.textContent || "";
328
+ return Array.from(tspans).map((t) => (t.textContent || "").replace(/ /g, " ")).join("\n");
329
+ }
330
+ function getWrapWidth(textElement) {
331
+ const v = parseFloat(textElement.getAttribute("data-wrap-width") || "");
332
+ return Number.isFinite(v) && v > 0 ? v : 0;
333
+ }
273
334
  function renderText(input, textElement, deleteIfEmpty = false) {
274
335
  if (!input || !document.body.contains(input)) {
275
336
  return;
@@ -313,21 +374,9 @@ function renderText(input, textElement, deleteIfEmpty = false) {
313
374
  removeSelectionFeedback();
314
375
  }
315
376
  } else {
316
- while (textElement.firstChild) {
317
- textElement.removeChild(textElement.firstChild);
318
- }
319
- const lines = text.split("\n");
320
- const x = textElement.getAttribute("x") || 0;
321
- lines.forEach((line, index) => {
322
- let tspan = document.createElementNS(
323
- "http://www.w3.org/2000/svg",
324
- "tspan"
325
- );
326
- tspan.setAttribute("x", x);
327
- tspan.setAttribute("dy", index === 0 ? "0" : "1.2em");
328
- tspan.textContent = line.replace(/\u00A0/g, " ") || " ";
329
- textElement.appendChild(tspan);
330
- });
377
+ const cleanSource = text.replace(/\u00A0/g, " ");
378
+ textElement.setAttribute("data-wrap-source", cleanSource);
379
+ paintTextContent(textElement, cleanSource, getWrapWidth(textElement));
331
380
  gElement.style.display = "block";
332
381
  updateAttachedArrows(gElement);
333
382
  if (selectedElement === gElement) {
@@ -378,7 +427,9 @@ function createSelectionFeedback(groupElement) {
378
427
  { name: "nw", x: selX, y: selY, cursor: "nwse-resize" },
379
428
  { name: "ne", x: selX + selWidth, y: selY, cursor: "nesw-resize" },
380
429
  { name: "sw", x: selX, y: selY + selHeight, cursor: "nesw-resize" },
381
- { name: "se", x: selX + selWidth, y: selY + selHeight, cursor: "nwse-resize" }
430
+ { name: "se", x: selX + selWidth, y: selY + selHeight, cursor: "nwse-resize" },
431
+ { name: "e", x: selX + selWidth, y: selY + selHeight / 2, cursor: "ew-resize" },
432
+ { name: "w", x: selX, y: selY + selHeight / 2, cursor: "ew-resize" }
382
433
  ];
383
434
  resizeHandles = {};
384
435
  handlesData.forEach((handle) => {
@@ -465,7 +516,11 @@ function updateSelectionFeedback() {
465
516
  { name: "nw", x: selX, y: selY },
466
517
  { name: "ne", x: selX + selWidth, y: selY },
467
518
  { name: "sw", x: selX, y: selY + selHeight },
468
- { name: "se", x: selX + selWidth, y: selY + selHeight }
519
+ { name: "se", x: selX + selWidth, y: selY + selHeight },
520
+ // Issue #48 bug #5: keep E/W midpoint handles aligned with the
521
+ // resize so they follow the box as it scales.
522
+ { name: "e", x: selX + selWidth, y: selY + selHeight / 2 },
523
+ { name: "w", x: selX, y: selY + selHeight / 2 }
469
524
  ];
470
525
  handlesData.forEach((handle) => {
471
526
  const handleRect = resizeHandles[handle.name];
@@ -687,6 +742,7 @@ var handleMouseMove = (event) => {
687
742
  const startWidth = startBBox.width;
688
743
  const startHeight = startBBox.height;
689
744
  let anchorX, anchorY;
745
+ const isEdgeWidth = currentResizeHandle === "e" || currentResizeHandle === "w";
690
746
  switch (currentResizeHandle) {
691
747
  case "nw":
692
748
  anchorX = startX + startWidth;
@@ -704,6 +760,26 @@ var handleMouseMove = (event) => {
704
760
  anchorX = startX;
705
761
  anchorY = startY;
706
762
  break;
763
+ case "e":
764
+ anchorX = startX;
765
+ anchorY = startY + startHeight / 2;
766
+ break;
767
+ case "w":
768
+ anchorX = startX + startWidth;
769
+ anchorY = startY + startHeight / 2;
770
+ break;
771
+ }
772
+ if (isEdgeWidth) {
773
+ const newWrap = Math.abs(currentPoint.x - anchorX);
774
+ const minWrap = startFontSize * 2;
775
+ const clampedWrap = Math.max(minWrap, newWrap);
776
+ textElement.setAttribute("data-wrap-width", String(clampedWrap));
777
+ const source = getWrapSource(textElement);
778
+ paintTextContent(textElement, source, clampedWrap);
779
+ if (typeof updateSelectionFeedback === "function") {
780
+ setTimeout(updateSelectionFeedback, 0);
781
+ }
782
+ return;
707
783
  }
708
784
  const newWidth = Math.abs(currentPoint.x - anchorX);
709
785
  const newHeight = Math.abs(currentPoint.y - anchorY);
@@ -734,6 +810,14 @@ var handleMouseMove = (event) => {
734
810
  newAnchorX = currentBBox.x;
735
811
  newAnchorY = currentBBox.y;
736
812
  break;
813
+ case "e":
814
+ newAnchorX = currentBBox.x;
815
+ newAnchorY = currentBBox.y + currentBBox.height / 2;
816
+ break;
817
+ case "w":
818
+ newAnchorX = currentBBox.x + currentBBox.width;
819
+ newAnchorY = currentBBox.y + currentBBox.height / 2;
820
+ break;
737
821
  }
738
822
  const deltaX = anchorX - newAnchorX;
739
823
  const deltaY = anchorY - newAnchorY;
@@ -1488,4 +1572,4 @@ export {
1488
1572
  handleTextMouseUp,
1489
1573
  updateCodeToggleForShape
1490
1574
  };
1491
- //# sourceMappingURL=chunk-VRRJEOCH.js.map
1575
+ //# sourceMappingURL=chunk-TUMWA4YF.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/tools/textTool.js"],
4
+ "sourcesContent": ["/* eslint-disable */\n// Text tool event handlers - extracted from writeText.js\nimport {\n pushCreateAction,\n pushDeleteAction,\n pushDeleteActionWithAttachments,\n pushTransformAction,\n pushOptionsChangeAction,\n pushFrameAttachmentAction,\n setTextReferences,\n updateSelectedElement\n} from '../core/UndoRedo.js';\nimport { cleanupAttachments, updateAttachedArrows } from './arrowTool.js';\n\nfunction getThemeStroke() { if (typeof document === \"undefined\") return \"#fff\"; return document.body && document.body.classList.contains(\"theme-dark\") ? \"#fff\" : \"#1a1a2e\"; }\nimport {\n addCodeBlock,\n wrapCodeElement,\n selectCodeBlock,\n deselectCodeBlock,\n applySyntaxHighlightingToSVG,\n createHighlightedSVGText,\n updateCodeBackground,\n extractTextFromCodeElement,\n setCodeLanguage,\n getCodeLanguage,\n getSelectedCodeBlock\n} from './codeTool.js';\n\nlet textSize = \"30px\";\nlet textFont = \"lixFont\";\nlet textColor = null;\nlet textAlign = \"left\";\n\nlet textColorOptions = document.querySelectorAll(\".textColorSpan\");\nlet textFontOptions = document.querySelectorAll(\".textFontSpan\");\nlet textSizeOptions = document.querySelectorAll(\".textSizeSpan\");\nlet textAlignOptions = document.querySelectorAll(\".textAlignSpan\");\n\nlet selectedElement = null;\nlet selectionBox = null;\nlet resizeHandles = {};\nlet dragOffsetX, dragOffsetY;\nlet isDragging = false;\nlet isResizing = false;\nlet currentResizeHandle = null;\nlet startBBox = null;\nlet startFontSize = null;\nlet startPoint = null;\nlet isRotating = false;\nlet rotationStartAngle = 0;\nlet rotationStartTransform = null;\nlet initialHandlePosRelGroup = null;\nlet initialGroupTx = 0;\nlet initialGroupTy = 0;\nlet initialInverseScreenCTM = null;\n\n// Frame attachment variables\nlet draggedShapeInitialFrameText = null;\nlet hoveredFrameText = null;\n\nsetTextReferences(selectedElement, updateSelectionFeedback, svg);\n\n\nfunction switchToSelectionTool() {\n if (window.__sketchStoreApi) {\n window.__sketchStoreApi.setActiveTool('select', { afterDraw: true });\n } else {\n window.isSelectionToolActive = true;\n }\n}\n\n\n// Convert group element to our TextShape class\nfunction wrapTextElement(groupElement) {\n const textShape = new TextShape(groupElement);\n return textShape;\n}\n\nfunction getSVGCoordinates(event, element = svg) {\n if (!svg || !svg.createSVGPoint) {\n console.error(\"SVG element or createSVGPoint method not available.\");\n return { x: 0, y: 0 };\n }\n let pt = svg.createSVGPoint();\n pt.x = event.clientX;\n pt.y = event.clientY;\n\n try {\n let screenCTM = (element && typeof element.getScreenCTM === 'function' && element.getScreenCTM()) || svg.getScreenCTM();\n if (!screenCTM) {\n console.error(\"Could not get Screen CTM.\");\n return { x: event.clientX, y: event.clientY };\n }\n let svgPoint = pt.matrixTransform(screenCTM.inverse());\n return {\n x: svgPoint.x,\n y: svgPoint.y,\n };\n } catch (error) {\n console.error(\"Error getting SVG coordinates:\", error);\n return { x: event.clientX, y: event.clientY };\n }\n}\n\nfunction addText(event) {\n let { x, y } = getSVGCoordinates(event);\n\n let gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"text-group\");\n gElement.setAttribute(\"transform\", `translate(${x}, ${y})`);\n\n let textElement = document.createElementNS(\n \"http://www.w3.org/2000/svg\",\n \"text\"\n );\n let textAlignElement = \"start\";\n if (textAlign === \"center\") textAlignElement = \"middle\";\n else if (textAlign === \"right\") textAlignElement = \"end\";\n\n textElement.setAttribute(\"x\", 0);\n textElement.setAttribute(\"y\", 0);\n textElement.setAttribute(\"fill\", textColor ?? getThemeStroke());\n textElement.setAttribute(\"font-size\", textSize);\n textElement.setAttribute(\"font-family\", textFont);\n textElement.setAttribute(\"text-anchor\", textAlignElement);\n textElement.setAttribute(\"cursor\", \"default\");\n textElement.setAttribute(\"white-space\", \"pre\");\n textElement.setAttribute(\"dominant-baseline\", \"hanging\");\n // Issue #48 bugs #1 / #3: reverted the previous `bounding-box` hit\n // area (introduced for #34 #5b) \u2014 extending the text's hit zone to\n // its full bbox meant the negative space between glyphs and around\n // the text was stealing clicks from shapes drawn ON TOP of the text\n // (drag a rect over text \u2192 text intercepts; select text then click a\n // different shape elsewhere \u2192 click lands in text's invisible bbox\n // and the text stays selected). Default `painted` only fires on\n // glyph pixels, which restores the expected pass-through. Trade-off:\n // empty-padding clicks no longer select the text \u2014 the user has to\n // click on the letters.\n textElement.setAttribute(\"pointer-events\", \"painted\");\n textElement.textContent = \"\";\n\n gElement.setAttribute(\"data-x\", x);\n gElement.setAttribute(\"data-y\", y);\n textElement.setAttribute(\"data-initial-size\", textSize);\n textElement.setAttribute(\"data-initial-font\", textFont);\n textElement.setAttribute(\"data-initial-color\", textColor ?? getThemeStroke());\n textElement.setAttribute(\"data-initial-align\", textAlign);\n textElement.setAttribute(\"data-type\", \"text\");\n gElement.appendChild(textElement);\n svg.appendChild(gElement);\n \n // Attach ID to both group and text element\n const shapeID = `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute('id', shapeID);\n textElement.setAttribute('id', `${shapeID}-text`);\n \n // Create TextShape wrapper for frame functionality\n const textShape = wrapTextElement(gElement);\n \n // Add to shapes array for arrow attachment and frame functionality\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(textShape);\n }\n \n pushCreateAction({\n type: 'text',\n element: textShape,\n shapeName: 'text'\n });\n\n // Check if text was created inside a frame and add it\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(shape => {\n if (shape.shapeName === 'frame' && shape.isShapeInFrame(textShape)) {\n shape.addShapeToFrame(textShape);\n }\n });\n }\n\n makeTextEditable(textElement, gElement);\n}\n\nfunction makeTextEditable(textElement, groupElement) {\n\n if (document.querySelector(\"textarea.svg-text-editor\")) {\n return;\n }\n\n if (selectedElement) {\n deselectElement();\n }\n\n let input = document.createElement(\"textarea\");\n input.className = \"svg-text-editor\";\n\n // Issue #48 phase D follow-up: prefer the persisted `data-wrap-\n // source` when present so re-opening the editor shows the user's\n // original line breaks instead of the auto-wrap output. Falls back\n // to tspan-concatenation for legacy text shapes.\n let textContent = '';\n const storedSource = textElement.getAttribute('data-wrap-source');\n if (storedSource !== null && storedSource !== '') {\n textContent = storedSource.replace(/ /g, '\\u00A0');\n } else {\n const tspans = textElement.querySelectorAll('tspan');\n if (tspans.length > 0) {\n tspans.forEach((tspan, index) => {\n textContent += tspan.textContent.replace(/ /g, '\\u00A0');\n if (index < tspans.length - 1) textContent += '\\n';\n });\n } else {\n textContent = textElement.textContent.replace(/ /g, '\\u00A0');\n }\n }\n\n input.value = textContent;\n input.style.position = \"absolute\";\n input.style.outline = \"none\";\n input.style.padding = \"1px\";\n input.style.margin = \"0\";\n input.style.boxSizing = \"border-box\";\n input.style.overflow = \"hidden\";\n input.style.resize = \"none\";\n input.style.whiteSpace = \"pre-wrap\";\n input.style.minHeight = \"1.2em\";\n input.style.zIndex = \"10000\";\n\n const svgRect = svg.getBoundingClientRect();\n\n // Use the group element's own screenCTM which includes group transform + SVG viewBox transform\n const textBBox = textElement.getBBox();\n let pt = svg.createSVGPoint();\n pt.x = textBBox.x;\n pt.y = textBBox.y;\n\n const groupCTM = groupElement.getScreenCTM() || svg.getScreenCTM();\n let screenPt = pt.matrixTransform(groupCTM);\n\n input.style.left = `${screenPt.x}px`;\n input.style.top = `${screenPt.y}px`;\n\n const svgZoomFactor = svg.getScreenCTM() ? svg.getScreenCTM().a : 1;\n const screenWidth = textBBox.width * svgZoomFactor;\n\n input.style.width = \"auto\";\n input.style.height = \"auto\";\n\n const currentFontSize = textElement.getAttribute(\"font-size\") || \"30px\";\n const currentFontFamily = textElement.getAttribute(\"font-family\") || \"lixFont\";\n const currentFill = textElement.getAttribute(\"fill\") || \"#fff\";\n const currentAnchor = textElement.getAttribute(\"text-anchor\") || \"start\";\n // Scale font-size by zoom so the textarea matches what the user sees on canvas\n const rawSize = parseFloat(currentFontSize) || 30;\n const scaledFontSize = `${rawSize * svgZoomFactor}px`;\n\n input.style.minWidth = \"150px\";\n input.style.minHeight = \"1.5em\";\n input.style.width = \"auto\";\n input.style.height = \"auto\";\n input.style.overflow = \"visible\";\n input.style.whiteSpace = \"pre-wrap\";\n input.style.wordBreak = \"break-word\";\n input.style.fontSize = scaledFontSize;\n input.style.fontFamily = currentFontFamily;\n input.style.color = currentFill;\n input.style.lineHeight = \"1.2em\";\n input.style.textAlign = currentAnchor === \"middle\" ? \"center\" : currentAnchor === \"end\" ? \"right\" : \"left\";\n input.style.backgroundColor = \"transparent\";\n // Issue #34 bug #5a + #48 bug #2: dashed creation outline that grows\n // with the text. Originally `rgba(255,255,255,0.55)` \u2014 invisible on\n // the new light canvas. Read the active theme so the dashes stay\n // visible in both modes.\n const _isDark = typeof document !== 'undefined'\n && document.body\n && document.body.classList.contains('theme-dark');\n input.style.border = _isDark\n ? \"1px dashed rgba(255,255,255,0.55)\"\n : \"1px dashed rgba(40,40,60,0.45)\";\n input.style.borderRadius = \"3px\";\n input.style.outline = \"none\";\n document.body.appendChild(input);\n\n const adjustHeight = () => {\n input.style.height = 'auto';\n input.style.height = input.scrollHeight + 'px';\n const maxHeight = svgRect.height - (screenPt.y);\n if (input.scrollHeight > maxHeight) {\n input.style.height = maxHeight + 'px';\n input.style.overflowY = 'auto';\n } else {\n input.style.overflowY = 'hidden';\n }\n };\n\n const adjustWidth = () => {\n input.style.width = 'auto';\n const maxWidth = svgRect.width - (screenPt.x);\n const contentWidth = Math.max(input.scrollWidth, 150);\n if (contentWidth > maxWidth) {\n input.style.width = maxWidth + 'px';\n input.style.overflowX = 'auto';\n } else {\n input.style.width = contentWidth + 'px';\n input.style.overflowX = 'hidden';\n }\n };\n adjustHeight();\n adjustWidth();\n\n setTimeout(() => {\n input.focus();\n input.select();\n }, 50);\n\n input.addEventListener('input', adjustHeight);\n input.addEventListener('input', adjustWidth);\n\n input.addEventListener(\"keydown\", function (e) {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n renderText(input, textElement, true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n renderText(input, textElement, true);\n }\n });\n\n input.originalTextElement = textElement;\n input.textGroup = groupElement;\n\n const handleClickOutside = (event) => {\n if (!input.contains(event.target)) {\n renderText(input, textElement, true);\n document.removeEventListener('pointerdown', handleClickOutside, true);\n }\n };\n document.addEventListener('pointerdown', handleClickOutside, true);\n input.handleClickOutside = handleClickOutside;\n\n // Set text cursor on the element during edit mode\n const textEl = groupElement.querySelector('text');\n if (textEl) textEl.setAttribute(\"cursor\", \"text\");\n\n groupElement.style.display = \"none\";\n}\n\n/**\n * Issue #48 phase D follow-up: true text wrapping driven by E/W width.\n *\n * `paint(textElement, source, wrapWidth)` renders the user-typed `source`\n * string into tspans, soft-wrapping any line that exceeds `wrapWidth`\n * pixels (in SVG units). `wrapWidth = null | 0` falls back to the old\n * \"explicit newlines only\" behaviour so existing text shapes that have\n * never been width-resized look identical.\n *\n * The raw `source` is persisted on the element as `data-wrap-source`\n * so subsequent edits (textarea re-open) and serialisation can rebuild\n * the original content even after auto-wrap inserted line breaks.\n */\nfunction measureSegment(text, refTextElement) {\n if (!text) return 0;\n const NS = 'http://www.w3.org/2000/svg';\n const probe = document.createElementNS(NS, 'tspan');\n probe.textContent = text;\n refTextElement.appendChild(probe);\n let width = 0;\n try { width = probe.getComputedTextLength(); } catch {}\n refTextElement.removeChild(probe);\n return width;\n}\n\nfunction paintTextContent(textElement, source, wrapWidth) {\n while (textElement.firstChild) textElement.removeChild(textElement.firstChild);\n const x = textElement.getAttribute('x') || 0;\n const NS = 'http://www.w3.org/2000/svg';\n const paragraphs = (source || '').split('\\n');\n const finalLines = [];\n\n for (const paragraph of paragraphs) {\n const para = paragraph.replace(/\u00A0/g, ' ');\n if (!wrapWidth || wrapWidth <= 0) {\n finalLines.push(para || ' ');\n continue;\n }\n // Word-wrap the paragraph against wrapWidth.\n const words = para.split(/(\\s+)/); // keep separators so spacing survives\n let cur = '';\n for (const token of words) {\n const candidate = cur + token;\n if (measureSegment(candidate, textElement) > wrapWidth && cur.trim().length > 0) {\n finalLines.push(cur);\n cur = token.replace(/^\\s+/, ''); // drop the leading break-space\n } else {\n cur = candidate;\n }\n }\n finalLines.push(cur.length ? cur : ' ');\n }\n\n finalLines.forEach((line, i) => {\n const tspan = document.createElementNS(NS, 'tspan');\n tspan.setAttribute('x', x);\n tspan.setAttribute('dy', i === 0 ? '0' : '1.2em');\n tspan.textContent = line.length ? line : ' ';\n textElement.appendChild(tspan);\n });\n}\n\nfunction getWrapSource(textElement) {\n const stored = textElement.getAttribute('data-wrap-source');\n if (stored !== null && stored !== '') return stored;\n // Fallback: rebuild from existing tspans (legacy text shapes without\n // the attribute). Join each tspan as a paragraph.\n const tspans = textElement.querySelectorAll('tspan');\n if (tspans.length === 0) return textElement.textContent || '';\n return Array.from(tspans).map((t) => (t.textContent || '').replace(/\u00A0/g, ' ')).join('\\n');\n}\n\nfunction getWrapWidth(textElement) {\n const v = parseFloat(textElement.getAttribute('data-wrap-width') || '');\n return Number.isFinite(v) && v > 0 ? v : 0;\n}\n\nfunction renderText(input, textElement, deleteIfEmpty = false) {\n if (!input || !document.body.contains(input)) {\n return;\n }\n\n const text = input.value || \"\";\n const gElement = input.textGroup;\n\n if (input.handleClickOutside) {\n document.removeEventListener('pointerdown', input.handleClickOutside, true);\n }\n\n document.body.removeChild(input);\n\n // Reset cursor back to default after edit mode ends\n if (textElement) textElement.setAttribute(\"cursor\", \"default\");\n\n if (!gElement || !textElement) {\n return;\n }\n\n if (!gElement.parentNode) {\n if (selectedElement === gElement) {\n deselectElement();\n }\n return;\n }\n\n if (deleteIfEmpty && text.trim() === \"\") {\n // Find the TextShape wrapper\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === gElement);\n if (textShape) {\n const idx = shapes.indexOf(textShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Use enhanced delete action for text with arrow attachments\n pushDeleteActionWithAttachments({\n type: 'text',\n element: textShape || gElement,\n shapeName: 'text'\n });\n\n // Clean up any arrow attachments before deleting\n if (typeof cleanupAttachments === 'function') {\n cleanupAttachments(gElement);\n }\n\n svg.removeChild(gElement);\n if (selectedElement === gElement) {\n selectedElement = null;\n removeSelectionFeedback();\n }\n } else {\n // Persist the raw editor content so future E/W resizes can re-wrap\n // from the user's original line breaks instead of the auto-inserted\n // ones. Then call the shared paint helper \\u2014 it falls back to the\n // legacy \"explicit newlines only\" path when no wrap-width is set.\n const cleanSource = text.replace(/\\u00A0/g, ' ');\n textElement.setAttribute('data-wrap-source', cleanSource);\n paintTextContent(textElement, cleanSource, getWrapWidth(textElement));\n\n gElement.style.display = 'block';\n\n // Update attached arrows after text content change\n updateAttachedArrows(gElement);\n\n if (selectedElement === gElement) {\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n\n // After rendering text, switch to selection tool and auto-select\n if (gElement.parentNode) {\n switchToSelectionTool();\n // Defer selection so the tool switch (async React state) completes first\n requestAnimationFrame(() => selectElement(gElement));\n }\n}\n\nfunction createSelectionFeedback(groupElement) {\n if (!groupElement) return;\n removeSelectionFeedback();\n\n const textElement = groupElement.querySelector('text');\n if (!textElement) {\n return;\n }\n\n const bbox = textElement.getBBox();\n\n const zoom = window.currentZoom || 1;\n const padding = 8 / zoom;\n const handleSize = 10 / zoom;\n const handleOffset = handleSize / 2;\n const anchorStrokeWidth = 2;\n\n const selX = bbox.x - padding;\n const selY = bbox.y - padding;\n const selWidth = bbox.width + 2 * padding;\n const selHeight = bbox.height + 2 * padding;\n\n const outlinePoints = [\n [selX, selY],\n [selX + selWidth, selY],\n [selX + selWidth, selY + selHeight],\n [selX, selY + selHeight],\n [selX, selY]\n ];\n\n const pointsAttr = outlinePoints.map(p => p.join(',')).join(' ');\n selectionBox = document.createElementNS(\"http://www.w3.org/2000/svg\", \"polyline\");\n selectionBox.setAttribute(\"class\", \"selection-box\");\n selectionBox.setAttribute(\"points\", pointsAttr);\n selectionBox.setAttribute(\"fill\", \"none\");\n selectionBox.setAttribute(\"stroke\", \"#5B57D1\");\n selectionBox.setAttribute(\"stroke-width\", \"1.5\");\n selectionBox.setAttribute(\"stroke-dasharray\", `${4 / zoom} ${2 / zoom}`);\n selectionBox.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n selectionBox.setAttribute(\"pointer-events\", \"none\");\n groupElement.appendChild(selectionBox);\n\n // Issue #48 bug #5 + phase D follow-up: E/W midpoint anchors drive\n // TRUE word-wrap. Dragging E/W sets `data-wrap-width` on the text\n // element; the paint helper re-flows tspans so the text wraps into\n // multiple lines. Corner anchors keep their font-size scaling\n // behaviour.\n const handlesData = [\n { name: 'nw', x: selX, y: selY, cursor: 'nwse-resize' },\n { name: 'ne', x: selX + selWidth, y: selY, cursor: 'nesw-resize' },\n { name: 'sw', x: selX, y: selY + selHeight, cursor: 'nesw-resize' },\n { name: 'se', x: selX + selWidth, y: selY + selHeight, cursor: 'nwse-resize' },\n { name: 'e', x: selX + selWidth, y: selY + selHeight / 2, cursor: 'ew-resize' },\n { name: 'w', x: selX, y: selY + selHeight / 2, cursor: 'ew-resize' },\n ];\n\n resizeHandles = {};\n handlesData.forEach(handle => {\n const handleRect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n handleRect.setAttribute(\"class\", `resize-handle resize-handle-${handle.name}`);\n handleRect.setAttribute(\"x\", handle.x - handleOffset);\n handleRect.setAttribute(\"y\", handle.y - handleOffset);\n handleRect.setAttribute(\"width\", handleSize);\n handleRect.setAttribute(\"height\", handleSize);\n handleRect.setAttribute(\"fill\", \"#121212\");\n handleRect.setAttribute(\"stroke\", \"#5B57D1\");\n handleRect.setAttribute(\"stroke-width\", anchorStrokeWidth);\n handleRect.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n handleRect.style.cursor = handle.cursor;\n handleRect.setAttribute(\"data-anchor\", handle.name);\n groupElement.appendChild(handleRect);\n resizeHandles[handle.name] = handleRect;\n\n handleRect.addEventListener('pointerdown', (e) => {\n if (window.isSelectionToolActive) {\n e.stopPropagation();\n startResize(e, handle.name);\n }\n });\n });\n\n const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };\n const rotationAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n rotationAnchor.setAttribute('cx', rotationAnchorPos.x);\n rotationAnchor.setAttribute('cy', rotationAnchorPos.y);\n rotationAnchor.setAttribute('r', 8);\n rotationAnchor.setAttribute('class', 'rotate-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.style.cursor = 'grab';\n rotationAnchor.setAttribute('pointer-events', 'all');\n groupElement.appendChild(rotationAnchor);\n\n resizeHandles.rotate = rotationAnchor;\n\n rotationAnchor.addEventListener('pointerdown', (e) => {\n if (window.isSelectionToolActive) {\n e.stopPropagation();\n startRotation(e);\n }\n });\n\n rotationAnchor.addEventListener('mouseover', function() {\n if (!isResizing && !isDragging) {\n this.style.cursor = 'grab';\n }\n });\n\n rotationAnchor.addEventListener('mouseout', function() {\n if (!isResizing && !isDragging) {\n this.style.cursor = 'default';\n }\n });\n}\n\nfunction updateSelectionFeedback() {\n if (!selectedElement || !selectionBox) return;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const wasHidden = selectedElement.style.display === 'none';\n if (wasHidden) selectedElement.style.display = 'block';\n\n const bbox = textElement.getBBox();\n\n if (wasHidden) selectedElement.style.display = 'none';\n\n if (bbox.width === 0 && bbox.height === 0 && textElement.textContent.trim() !== \"\") {\n }\n\n const zoom2 = window.currentZoom || 1;\n const padding = 8 / zoom2;\n const handleSize = 10 / zoom2;\n const handleOffset = handleSize / 2;\n\n const selX = bbox.x - padding;\n const selY = bbox.y - padding;\n const selWidth = Math.max(bbox.width + 2 * padding, handleSize);\n const selHeight = Math.max(bbox.height + 2 * padding, handleSize);\n\n const outlinePoints = [\n [selX, selY],\n [selX + selWidth, selY],\n [selX + selWidth, selY + selHeight],\n [selX, selY + selHeight],\n [selX, selY]\n ];\n\n const pointsAttr = outlinePoints.map(p => p.join(',')).join(' ');\n selectionBox.setAttribute(\"points\", pointsAttr);\n\n const handlesData = [\n { name: 'nw', x: selX, y: selY },\n { name: 'ne', x: selX + selWidth, y: selY },\n { name: 'sw', x: selX, y: selY + selHeight },\n { name: 'se', x: selX + selWidth, y: selY + selHeight },\n // Issue #48 bug #5: keep E/W midpoint handles aligned with the\n // resize so they follow the box as it scales.\n { name: 'e', x: selX + selWidth, y: selY + selHeight / 2 },\n { name: 'w', x: selX, y: selY + selHeight / 2 },\n ];\n\n handlesData.forEach(handle => {\n const handleRect = resizeHandles[handle.name];\n if (handleRect) {\n handleRect.setAttribute(\"x\", handle.x - handleOffset);\n handleRect.setAttribute(\"y\", handle.y - handleOffset);\n }\n });\n\n const rotationAnchor = resizeHandles.rotate;\n if (rotationAnchor) {\n const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };\n rotationAnchor.setAttribute('cx', rotationAnchorPos.x);\n rotationAnchor.setAttribute('cy', rotationAnchorPos.y);\n }\n}\n\nfunction startRotation(event) {\n if (!selectedElement || event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n isRotating = true;\n isDragging = false;\n isResizing = false;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n const mousePos = getSVGCoordinates(event);\n\n let centerPoint = svg.createSVGPoint();\n centerPoint.x = centerX;\n centerPoint.y = centerY;\n\n const groupTransform = selectedElement.transform.baseVal.consolidate();\n if (groupTransform) {\n centerPoint = centerPoint.matrixTransform(groupTransform.matrix);\n }\n\n rotationStartAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n rotationStartTransform = currentTransform ? currentTransform.matrix : svg.createSVGMatrix();\n\n svg.style.cursor = 'grabbing';\n\n window.addEventListener('pointermove', handleMouseMove);\n window.addEventListener('pointerup', handleMouseUp);\n}\n\nfunction removeSelectionFeedback(element) {\n const target = element || selectedElement;\n if (target) {\n target.querySelectorAll(\".selection-box, .resize-handle, .rotate-anchor\").forEach(el => el.remove());\n }\n // Also clean up any orphaned selection elements in all text groups\n svg.querySelectorAll('g[data-type=\"text-group\"] .selection-box, g[data-type=\"text-group\"] .resize-handle, g[data-type=\"text-group\"] .rotate-anchor').forEach(el => el.remove());\n\n selectionBox = null;\n resizeHandles = {};\n}\n\nfunction selectElement(groupElement) {\n if (!groupElement || !groupElement.parentNode) return;\n if (groupElement === selectedElement) return;\n\n deselectElement();\n selectedElement = groupElement;\n selectedElement.classList.add(\"selected\");\n createSelectionFeedback(selectedElement);\n\n updateSelectedElement(selectedElement);\n updateCodeToggleForShape('text');\n\n // Update global currentShape so EventDispatcher can route to other tools later\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n const wrapper = shapes.find(s => s.element === groupElement || s.group === groupElement);\n if (wrapper) {\n currentShape = wrapper;\n }\n }\n\n // Show text property panel when text is selected\n if (window.__showSidebarForShape) window.__showSidebarForShape('text');\n}\n\nfunction deselectElement() {\n const activeEditor = document.querySelector(\"textarea.svg-text-editor\");\n if (activeEditor) {\n let textElement = activeEditor.originalTextElement;\n if (textElement) {\n renderText(activeEditor, textElement, true);\n } else if (document.body.contains(activeEditor)) {\n document.body.removeChild(activeEditor);\n }\n }\n\n if (selectedElement) {\n removeSelectionFeedback();\n selectedElement.classList.remove(\"selected\");\n selectedElement = null;\n\n updateSelectedElement(null);\n\n // Clear global currentShape if it was a text shape\n if (currentShape && (currentShape.shapeName === 'text' || currentShape.shapeName === 'code')) {\n currentShape = null;\n }\n\n // Hide text property panel if we're in selection mode (not text tool)\n if (isSelectionToolActive) {\n textSideBar.classList.add(\"hidden\");\n }\n }\n\n if (isRotating) {\n isRotating = false;\n rotationStartAngle = 0;\n rotationStartTransform = null;\n svg.style.cursor = 'default';\n\n window.removeEventListener('pointermove', handleMouseMove);\n window.removeEventListener('pointerup', handleMouseUp);\n }\n}\n\nfunction startDrag(event) {\n if (!selectedElement || event.button !== 0) return;\n\n if (event.target.closest('.resize-handle')) {\n return;\n }\n\n isDragging = true;\n isResizing = false;\n event.preventDefault();\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n const initialTranslateX = currentTransform ? currentTransform.matrix.e : 0;\n const initialTranslateY = currentTransform ? currentTransform.matrix.f : 0;\n\n startPoint = getSVGCoordinates(event);\n\n dragOffsetX = startPoint.x - initialTranslateX;\n dragOffsetY = startPoint.y - initialTranslateY;\n\n // Find the TextShape wrapper for frame functionality\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n }\n\n if (textShape) {\n // Store initial frame state\n draggedShapeInitialFrameText = textShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (textShape.parentFrame) {\n textShape.parentFrame.temporarilyRemoveFromFrame(textShape);\n }\n }\n\n svg.style.cursor = 'grabbing';\n\n svg.addEventListener('pointermove', handleMouseMove);\n svg.addEventListener('pointerup', handleMouseUp);\n}\n\nfunction startResize(event, anchor) {\n if (!selectedElement || event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n isResizing = true;\n isDragging = false;\n currentResizeHandle = anchor;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) {\n isResizing = false;\n return;\n }\n\n startBBox = textElement.getBBox();\n startFontSize = parseFloat(textElement.getAttribute(\"font-size\") || 30);\n if (isNaN(startFontSize)) startFontSize = 30;\n\n startPoint = getSVGCoordinates(event, selectedElement);\n\n // Freeze the group's screen CTM at resize start so mouse\u2192local mapping stays stable\n const groupScreenCTM = selectedElement.getScreenCTM();\n initialInverseScreenCTM = groupScreenCTM ? groupScreenCTM.inverse() : null;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n initialGroupTx = currentTransform ? currentTransform.matrix.e : 0;\n initialGroupTy = currentTransform ? currentTransform.matrix.f : 0;\n\n const padding = 3;\n const startX = startBBox.x - padding;\n const startY = startBBox.y - padding;\n const startWidth = startBBox.width + 2 * padding;\n const startHeight = startBBox.height + 2 * padding;\n\n let hx = startX;\n let hy = startY;\n if (anchor.includes('e')) { hx = startX + startWidth; }\n if (anchor.includes('s')) { hy = startY + startHeight; }\n initialHandlePosRelGroup = { x: hx, y: hy };\n\n svg.style.cursor = resizeHandles[anchor]?.style.cursor || 'default';\n\n svg.addEventListener('pointermove', handleMouseMove);\n svg.addEventListener('pointerup', handleMouseUp);\n}\n\n\nconst handleMouseMove = (event) => {\n if (!selectedElement) return;\n event.preventDefault();\n\n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: event.clientX - svgRect.left, \n y: event.clientY - svgRect.top\n };\n\n if (isDragging) {\n const currentPoint = getSVGCoordinates(event);\n const newTranslateX = currentPoint.x - dragOffsetX;\n const newTranslateY = currentPoint.y - dragOffsetY;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n selectedElement.setAttribute('transform',\n `translate(${newTranslateX}, ${newTranslateY}) rotate(${angle}, ${centerX}, ${centerY})`\n );\n } else {\n selectedElement.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY})`);\n }\n } else {\n selectedElement.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY})`);\n }\n\n // Update frame containment for TextShape wrapper\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n const textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n if (textShape) {\n textShape.updateFrameContainment();\n }\n }\n\n // Update attached arrows during dragging\n updateAttachedArrows(selectedElement);\n\n } else if (isResizing) {\n const textElement = selectedElement.querySelector('text');\n if (!textElement || !startBBox || startFontSize === null || !startPoint || !initialHandlePosRelGroup) return;\n\n // Use the frozen initial CTM so the mapping doesn't shift as we change the group transform\n let currentPoint;\n if (initialInverseScreenCTM) {\n const pt = svg.createSVGPoint();\n pt.x = event.clientX;\n pt.y = event.clientY;\n currentPoint = pt.matrixTransform(initialInverseScreenCTM);\n } else {\n currentPoint = getSVGCoordinates(event, selectedElement);\n }\n\n const startX = startBBox.x;\n const startY = startBBox.y;\n const startWidth = startBBox.width;\n const startHeight = startBBox.height;\n\n let anchorX, anchorY;\n const isEdgeWidth = currentResizeHandle === 'e' || currentResizeHandle === 'w';\n\n switch (currentResizeHandle) {\n case 'nw':\n anchorX = startX + startWidth;\n anchorY = startY + startHeight;\n break;\n case 'ne':\n anchorX = startX;\n anchorY = startY + startHeight;\n break;\n case 'sw':\n anchorX = startX + startWidth;\n anchorY = startY;\n break;\n case 'se':\n anchorX = startX;\n anchorY = startY;\n break;\n case 'e':\n anchorX = startX;\n anchorY = startY + startHeight / 2;\n break;\n case 'w':\n anchorX = startX + startWidth;\n anchorY = startY + startHeight / 2;\n break;\n }\n\n // Issue #48 phase D follow-up: E/W now drives TRUE word-wrap.\n // The drag distance becomes the wrap-width target; we re-paint\n // the text with the new width and short-circuit the font-size\n // scaling path that the corners use.\n if (isEdgeWidth) {\n const newWrap = Math.abs(currentPoint.x - anchorX);\n const minWrap = startFontSize * 2; // at least ~one short word wide\n const clampedWrap = Math.max(minWrap, newWrap);\n textElement.setAttribute('data-wrap-width', String(clampedWrap));\n const source = getWrapSource(textElement);\n paintTextContent(textElement, source, clampedWrap);\n if (typeof updateSelectionFeedback === 'function') {\n setTimeout(updateSelectionFeedback, 0);\n }\n return;\n }\n\n const newWidth = Math.abs(currentPoint.x - anchorX);\n const newHeight = Math.abs(currentPoint.y - anchorY);\n const chosenScale = newHeight / startHeight;\n\n const minScale = 0.1;\n const maxScale = 10.0;\n const clampedScale = Math.max(minScale, Math.min(chosenScale, maxScale));\n\n const newFontSize = startFontSize * clampedScale;\n const minFontSize = 5;\n const finalFontSize = Math.max(newFontSize, minFontSize);\n\n textElement.setAttribute(\"font-size\", `${finalFontSize}px`);\n\n const currentBBox = textElement.getBBox();\n\n let newAnchorX, newAnchorY;\n\n switch (currentResizeHandle) {\n case 'nw':\n newAnchorX = currentBBox.x + currentBBox.width;\n newAnchorY = currentBBox.y + currentBBox.height;\n break;\n case 'ne':\n newAnchorX = currentBBox.x;\n newAnchorY = currentBBox.y + currentBBox.height;\n break;\n case 'sw':\n newAnchorX = currentBBox.x + currentBBox.width;\n newAnchorY = currentBBox.y;\n break;\n case 'se':\n newAnchorX = currentBBox.x;\n newAnchorY = currentBBox.y;\n break;\n case 'e':\n newAnchorX = currentBBox.x;\n newAnchorY = currentBBox.y + currentBBox.height / 2;\n break;\n case 'w':\n newAnchorX = currentBBox.x + currentBBox.width;\n newAnchorY = currentBBox.y + currentBBox.height / 2;\n break;\n }\n\n const deltaX = anchorX - newAnchorX;\n const deltaY = anchorY - newAnchorY;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n\n const newGroupTx = initialGroupTx + deltaX;\n const newGroupTy = initialGroupTy + deltaY;\n\n const centerX = currentBBox.x + currentBBox.width / 2;\n const centerY = currentBBox.y + currentBBox.height / 2;\n\n selectedElement.setAttribute('transform',\n `translate(${newGroupTx}, ${newGroupTy}) rotate(${angle}, ${centerX}, ${centerY})`\n );\n } else {\n const newGroupTx = initialGroupTx + deltaX;\n const newGroupTy = initialGroupTy + deltaY;\n selectedElement.setAttribute('transform', `translate(${newGroupTx}, ${newGroupTy})`);\n }\n\n // Update attached arrows during resizing\n updateAttachedArrows(selectedElement);\n\n clearTimeout(selectedElement.updateFeedbackTimeout);\n selectedElement.updateFeedbackTimeout = setTimeout(() => {\n updateSelectionFeedback();\n delete selectedElement.updateFeedbackTimeout;\n }, 0);\n\n } else if (isRotating) {\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n const mousePos = getSVGCoordinates(event);\n\n let centerPoint = svg.createSVGPoint();\n centerPoint.x = centerX;\n centerPoint.y = centerY;\n\n const groupTransform = selectedElement.transform.baseVal.consolidate();\n if (groupTransform) {\n centerPoint = centerPoint.matrixTransform(groupTransform.matrix);\n }\n\n const currentAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;\n\n const rotationDiff = currentAngle - rotationStartAngle;\n\n const newTransform = `translate(${rotationStartTransform.e}, ${rotationStartTransform.f}) rotate(${rotationDiff}, ${centerX}, ${centerY})`;\n selectedElement.setAttribute('transform', newTransform);\n\n // Update attached arrows during rotation\n updateAttachedArrows(selectedElement);\n\n updateSelectionFeedback();\n }\n};\n\n\n\nconst handleMouseUp = (event) => {\n if (event.button !== 0) return;\n\n if (isDragging && selectedElement) {\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const finalTranslateX = currentTransform.matrix.e;\n const finalTranslateY = currentTransform.matrix.f;\n\n const initialX = parseFloat(selectedElement.getAttribute(\"data-x\")) || 0;\n const initialY = parseFloat(selectedElement.getAttribute(\"data-y\")) || 0;\n\n // Find the TextShape wrapper for frame tracking\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n }\n\n // Add frame information for undo tracking\n const oldPosWithFrame = {\n x: initialX,\n y: initialY,\n rotation: extractRotationFromTransform(selectedElement) || 0,\n parentFrame: draggedShapeInitialFrameText\n };\n // Issue #34 bug #2: hoveredFrameText is the actual destination\n // tracked during drag \u2014 textShape.parentFrame is still the OLD\n // frame at this point.\n const newPosWithFrame = {\n x: finalTranslateX,\n y: finalTranslateY,\n rotation: extractRotationFromTransform(selectedElement) || 0,\n parentFrame: hoveredFrameText || null,\n };\n\n const stateChanged = initialX !== finalTranslateX || initialY !== finalTranslateY;\n const frameChanged = oldPosWithFrame.parentFrame !== newPosWithFrame.parentFrame;\n\n if (stateChanged || frameChanged) {\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n oldPosWithFrame,\n newPosWithFrame\n );\n }\n\n // Handle frame containment changes after drag\n if (textShape) {\n const finalFrame = hoveredFrameText;\n \n // If shape moved to a different frame\n if (draggedShapeInitialFrameText !== finalFrame) {\n // Remove from initial frame\n if (draggedShapeInitialFrameText) {\n draggedShapeInitialFrameText.removeShapeFromFrame(textShape);\n }\n \n // Add to new frame\n if (finalFrame) {\n finalFrame.addShapeToFrame(textShape);\n }\n \n // Track the frame change for undo\n if (frameChanged) {\n pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameText, textShape, \n finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameText);\n }\n } else if (draggedShapeInitialFrameText) {\n // Shape stayed in same frame, restore clipping\n draggedShapeInitialFrameText.restoreToFrame(textShape);\n }\n }\n\n selectedElement.setAttribute(\"data-x\", finalTranslateX);\n selectedElement.setAttribute(\"data-y\", finalTranslateY);\n }\n\n draggedShapeInitialFrameText = null;\n\n } else if (isResizing && selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const finalFontSize = textElement.getAttribute(\"font-size\");\n const initialFontSize = startFontSize;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform && initialFontSize !== parseFloat(finalFontSize)) {\n const finalTranslateX = currentTransform.matrix.e;\n const finalTranslateY = currentTransform.matrix.f;\n\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n x: initialGroupTx,\n y: initialGroupTy,\n fontSize: initialFontSize,\n rotation: extractRotationFromTransform(selectedElement) || 0\n },\n {\n x: finalTranslateX,\n y: finalTranslateY,\n fontSize: parseFloat(finalFontSize),\n rotation: extractRotationFromTransform(selectedElement) || 0\n }\n );\n\n selectedElement.setAttribute(\"data-x\", finalTranslateX);\n selectedElement.setAttribute(\"data-y\", finalTranslateY);\n }\n\n clearTimeout(selectedElement.updateFeedbackTimeout);\n updateSelectionFeedback();\n }\n } else if (isRotating && selectedElement) {\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform && rotationStartTransform) {\n const initialRotation = Math.atan2(rotationStartTransform.b, rotationStartTransform.a) * 180 / Math.PI;\n const finalRotation = extractRotationFromTransform(selectedElement) || 0;\n\n if (Math.abs(initialRotation - finalRotation) > 1) {\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n x: rotationStartTransform.e,\n y: rotationStartTransform.f,\n rotation: initialRotation\n },\n {\n x: currentTransform.matrix.e,\n y: currentTransform.matrix.f,\n rotation: finalRotation\n }\n );\n }\n\n }\n updateSelectionFeedback();\n }\n\n // Clear frame highlighting\n if (hoveredFrameText) {\n hoveredFrameText.removeHighlight();\n hoveredFrameText = null;\n }\n\n isDragging = false;\n isResizing = false;\n isRotating = false;\n currentResizeHandle = null;\n startPoint = null;\n startBBox = null;\n startFontSize = null;\n dragOffsetX = undefined;\n dragOffsetY = undefined;\n initialHandlePosRelGroup = null;\n initialGroupTx = 0;\n initialGroupTy = 0;\n rotationStartAngle = 0;\n rotationStartTransform = null;\n\n // Restore cursor based on context - keep pointer if over selected text\n svg.style.cursor = isSelectionToolActive ? 'default' : (isTextToolActive ? 'text' : 'default');\n\n // Ensure selection feedback is refreshed after transforms\n if (selectedElement) {\n setTimeout(updateSelectionFeedback, 0);\n }\n\n svg.removeEventListener('pointermove', handleMouseMove);\n svg.removeEventListener('pointerup', handleMouseUp);\n window.removeEventListener('pointermove', handleMouseMove);\n window.removeEventListener('pointerup', handleMouseUp);\n};\n\nfunction extractRotationFromTransform(element) {\n const currentTransform = element.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n return Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n }\n return 0;\n}\n\n// EXPORTED EVENT HANDLERS\nconst handleTextMouseDown = function (e) {\n if (!e.target) return;\n const activeEditor = document.querySelector(\"textarea.svg-text-editor\");\n if (activeEditor && activeEditor.contains(e.target)) {\n return;\n }\n if (activeEditor && !activeEditor.contains(e.target)) {\n let textElement = activeEditor.originalTextElement;\n if (textElement) {\n // renderText switches to selection tool and auto-selects the text as a side effect.\n // Return early so we don't continue with stale tool state.\n renderText(activeEditor, textElement, true);\n return;\n } else if (document.body.contains(activeEditor)){\n document.body.removeChild(activeEditor);\n }\n }\n\n const targetGroup = e.target.closest('g[data-type=\"text-group\"]');\n\n if (isSelectionToolActive && e.button === 0) {\n if (targetGroup) {\n if (e.target.closest('.resize-handle')) {\n return;\n }\n\n // Double-click on selected text: enter edit mode\n if (e.detail >= 2 && targetGroup === selectedElement) {\n enterEditMode(targetGroup);\n e.stopPropagation();\n return;\n }\n\n if (targetGroup === selectedElement) {\n startDrag(e);\n } else {\n selectElement(targetGroup);\n startDrag(e);\n }\n } else {\n deselectElement();\n }\n\n } else if (isTextToolActive && e.button === 0) {\n if (targetGroup) {\n // Double-click: enter edit mode\n if (e.detail >= 2) {\n let textEl = targetGroup.querySelector('text');\n if (textEl) {\n makeTextEditable(textEl, targetGroup);\n e.stopPropagation();\n return;\n }\n }\n // Single-click: select and drag (same as selection tool behavior)\n if (targetGroup === selectedElement) {\n startDrag(e);\n } else {\n selectElement(targetGroup);\n startDrag(e);\n }\n } else {\n deselectElement();\n addText(e);\n }\n }\n};\n\nfunction enterEditMode(groupElement) {\n const textEl = groupElement.querySelector('text');\n if (!textEl) return;\n\n // Switch to text tool with property panel (go through store to clear other flags)\n if (window.__sketchStoreApi) {\n window.__sketchStoreApi.setActiveTool('text');\n } else {\n window.isTextToolActive = true;\n window.isSelectionToolActive = false;\n }\n toolExtraPopup();\n\n // Deselect (removes selection feedback) then open editor\n deselectElement();\n makeTextEditable(textEl, groupElement);\n}\n\nconst handleTextMouseMove = function (e) {\n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: e.clientX - svgRect.left, \n y: e.clientY - svgRect.top\n };\n\n // Handle cursor changes for text tool\n if (isTextToolActive) {\n const targetGroup = e.target?.closest?.('g[data-type=\"text-group\"]');\n if (targetGroup) {\n svg.style.cursor = 'pointer';\n } else {\n svg.style.cursor = 'crosshair';\n }\n } else if (isSelectionToolActive) {\n const targetGroup = e.target?.closest?.('g[data-type=\"text-group\"]');\n if (targetGroup) {\n svg.style.cursor = 'default';\n }\n }\n\n // Check for frame containment while creating text\n if (isTextToolActive && !isDragging && !isResizing && !isRotating) {\n // Get current mouse position for frame highlighting preview\n const { x, y } = getSVGCoordinates(e);\n \n // Create temporary text bounds for frame checking\n const tempTextBounds = {\n x: x - 50,\n y: y - 20,\n width: 100,\n height: 40\n };\n \n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(frame => {\n if (frame.shapeName === 'frame') {\n if (frame.isShapeInFrame(tempTextBounds)) {\n frame.highlightFrame();\n hoveredFrameText = frame;\n } else if (hoveredFrameText === frame) {\n frame.removeHighlight();\n hoveredFrameText = null;\n }\n }\n });\n }\n }\n};\n\nconst handleTextMouseUp = function (e) {\n // Clear frame highlighting when done with text tool operations\n if (hoveredFrameText) {\n hoveredFrameText.removeHighlight();\n hoveredFrameText = null;\n }\n};\n\n// updateAttachedArrows is imported from drawArrow.js\n\n\ntextColorOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textColorOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newColor = span.getAttribute(\"data-id\");\n const oldColor = textColor;\n textColor = newColor;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentColor = textElement.getAttribute('fill');\n\n if (currentColor !== newColor) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: currentColor,\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: newColor,\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('fill', newColor);\n }\n }\n });\n});\n\ntextFontOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textFontOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newFont = span.getAttribute(\"data-id\");\n const oldFont = textFont;\n textFont = newFont;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentFont = textElement.getAttribute('font-family');\n\n if (currentFont !== newFont) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: currentFont,\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: textElement.getAttribute('fill'),\n font: newFont,\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('font-family', newFont);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\ntextSizeOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textSizeOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newSize = span.getAttribute(\"data-id\") + \"px\";\n const oldSize = textSize;\n textSize = newSize;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentSize = textElement.getAttribute('font-size');\n\n if (currentSize !== newSize) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: currentSize,\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: newSize,\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('font-size', newSize);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\ntextAlignOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textAlignOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newAlign = span.getAttribute(\"data-id\");\n const oldAlign = textAlign;\n textAlign = newAlign;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentAnchor = textElement.getAttribute('text-anchor');\n let newAnchor = 'start';\n if (newAlign === 'center') newAnchor = 'middle';\n else if (newAlign === 'right') newAnchor = 'end';\n\n if (currentAnchor !== newAnchor) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: currentAnchor\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: newAnchor\n }\n );\n }\n\n textElement.setAttribute('text-anchor', newAnchor);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\n\n// --- Code/Text Toggle Handler ---\nconst textCodeOptions = document.querySelectorAll(\".textCodeSpan\");\nconst languageSelector = document.getElementById(\"textLanguageSelector\");\nconst codeLanguageSelect = document.getElementById(\"codeLanguageSelect\");\n\ntextCodeOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textCodeOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const isCodeMode = span.getAttribute(\"data-id\") === \"true\";\n isTextInCodeMode = isCodeMode;\n\n // Show/hide language selector\n if (languageSelector) {\n languageSelector.classList.toggle(\"hidden\", !isCodeMode);\n }\n\n // Update tool flags\n if (isTextToolActive) {\n isCodeToolActive = isCodeMode;\n }\n\n // If a shape is selected, convert it\n if (isCodeMode && selectedElement) {\n // Convert text \u2192 code\n convertTextToCode(selectedElement);\n } else if (!isCodeMode && getSelectedCodeBlock()) {\n // Convert code \u2192 text\n convertCodeToText(getSelectedCodeBlock());\n }\n });\n});\n\n// Language selector handler\nif (codeLanguageSelect) {\n codeLanguageSelect.addEventListener(\"change\", (event) => {\n const lang = event.target.value;\n setCodeLanguage(lang);\n\n // If a code block is selected, re-highlight it with the new language\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeElement = selectedCode.querySelector('text');\n if (codeElement) {\n codeElement.setAttribute(\"data-language\", lang);\n // Re-render with new language highlighting\n const content = extractTextFromCodeElement(codeElement);\n while (codeElement.firstChild) {\n codeElement.removeChild(codeElement.firstChild);\n }\n const highlighted = applySyntaxHighlightingToSVG(content, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(selectedCode, codeElement);\n }\n }\n });\n}\n\nfunction convertTextToCode(textGroupElement) {\n const textElement = textGroupElement.querySelector('text');\n if (!textElement) return;\n\n // Get text content\n let textContent = \"\";\n const tspans = textElement.querySelectorAll('tspan');\n if (tspans.length > 0) {\n tspans.forEach((tspan, index) => {\n textContent += tspan.textContent;\n if (index < tspans.length - 1) textContent += \"\\n\";\n });\n } else {\n textContent = textElement.textContent || \"\";\n }\n\n // Get position from transform\n const currentTransform = textGroupElement.transform.baseVal.consolidate();\n const tx = currentTransform ? currentTransform.matrix.e : 0;\n const ty = currentTransform ? currentTransform.matrix.f : 0;\n const fontSize = textElement.getAttribute('font-size') || \"25px\";\n const color = textElement.getAttribute('fill') || \"#fff\";\n\n // Find and remove old TextShape from shapes array\n let oldTextShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n oldTextShape = shapes.find(s => s.shapeName === 'text' && s.group === textGroupElement);\n if (oldTextShape) {\n const idx = shapes.indexOf(oldTextShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Deselect current text\n deselectElement();\n\n // Remove old text group from SVG\n if (textGroupElement.parentNode) {\n textGroupElement.parentNode.removeChild(textGroupElement);\n }\n\n // Create new code block\n const gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"code-group\");\n gElement.setAttribute(\"transform\", `translate(${tx}, ${ty})`);\n\n const backgroundRect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n backgroundRect.setAttribute(\"class\", \"code-background\");\n backgroundRect.setAttribute(\"x\", -10);\n backgroundRect.setAttribute(\"y\", -10);\n backgroundRect.setAttribute(\"width\", 300);\n backgroundRect.setAttribute(\"height\", 60);\n backgroundRect.setAttribute(\"fill\", \"#161b22\");\n backgroundRect.setAttribute(\"stroke\", \"#30363d\");\n backgroundRect.setAttribute(\"stroke-width\", \"1\");\n backgroundRect.setAttribute(\"rx\", \"6\");\n backgroundRect.setAttribute(\"ry\", \"6\");\n gElement.appendChild(backgroundRect);\n\n const codeElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n codeElement.setAttribute(\"x\", 0);\n codeElement.setAttribute(\"y\", 0);\n codeElement.setAttribute(\"fill\", color);\n codeElement.setAttribute(\"font-size\", fontSize);\n codeElement.setAttribute(\"font-family\", \"lixCode\");\n codeElement.setAttribute(\"text-anchor\", \"start\");\n codeElement.setAttribute(\"cursor\", \"default\");\n codeElement.setAttribute(\"white-space\", \"pre\");\n codeElement.setAttribute(\"dominant-baseline\", \"hanging\");\n codeElement.setAttribute(\"data-language\", getCodeLanguage());\n codeElement.setAttribute(\"data-type\", \"code\");\n\n const shapeID = `code-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute(\"id\", shapeID);\n gElement.setAttribute(\"data-x\", tx);\n gElement.setAttribute(\"data-y\", ty);\n codeElement.setAttribute(\"id\", `${shapeID}-code`);\n gElement.appendChild(codeElement);\n svg.appendChild(gElement);\n\n // Apply syntax highlighting and add content\n if (textContent.trim()) {\n const lang = getCodeLanguage();\n const highlighted = applySyntaxHighlightingToSVG(textContent, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(gElement, codeElement);\n }\n\n // Create CodeShape wrapper\n const codeShape = wrapCodeElement(gElement);\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(codeShape);\n }\n\n // Push undo action for mode conversion\n pushModeConvertAction(oldTextShape, textGroupElement, 'text', codeShape, gElement, 'code');\n\n // Select the new code block\n selectCodeBlock(gElement);\n}\n\nfunction convertCodeToText(codeGroupElement) {\n const codeElement = codeGroupElement.querySelector('text');\n if (!codeElement) return;\n\n // Get text content\n const textContent = extractTextFromCodeElement(codeElement);\n\n // Get position from transform\n const currentTransform = codeGroupElement.transform.baseVal.consolidate();\n const tx = currentTransform ? currentTransform.matrix.e : 0;\n const ty = currentTransform ? currentTransform.matrix.f : 0;\n const fontSize = codeElement.getAttribute('font-size') || \"30px\";\n const color = codeElement.getAttribute('fill') || \"#fff\";\n\n // Find and remove old CodeShape from shapes array\n let oldCodeShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n oldCodeShape = shapes.find(s => s.shapeName === 'code' && s.group === codeGroupElement);\n if (oldCodeShape) {\n const idx = shapes.indexOf(oldCodeShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Deselect current code block\n deselectCodeBlock();\n\n // Remove old code group from SVG\n if (codeGroupElement.parentNode) {\n codeGroupElement.parentNode.removeChild(codeGroupElement);\n }\n\n // Create new text shape\n const gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"text-group\");\n gElement.setAttribute(\"transform\", `translate(${tx}, ${ty})`);\n\n const textElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n textElement.setAttribute(\"x\", 0);\n textElement.setAttribute(\"y\", 0);\n textElement.setAttribute(\"fill\", color);\n textElement.setAttribute(\"font-size\", fontSize);\n textElement.setAttribute(\"font-family\", textFont);\n textElement.setAttribute(\"text-anchor\", \"start\");\n textElement.setAttribute(\"cursor\", \"default\");\n textElement.setAttribute(\"white-space\", \"pre\");\n textElement.setAttribute(\"dominant-baseline\", \"hanging\");\n textElement.setAttribute(\"data-type\", \"text\");\n\n const shapeID = `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute(\"id\", shapeID);\n gElement.setAttribute(\"data-x\", tx);\n gElement.setAttribute(\"data-y\", ty);\n textElement.setAttribute(\"id\", `${shapeID}-text`);\n gElement.appendChild(textElement);\n svg.appendChild(gElement);\n\n // Add text content as tspans\n if (textContent.trim()) {\n const lines = textContent.split(\"\\n\");\n const x = textElement.getAttribute(\"x\") || 0;\n lines.forEach((line, index) => {\n const tspan = document.createElementNS(\"http://www.w3.org/2000/svg\", \"tspan\");\n tspan.setAttribute(\"x\", x);\n tspan.setAttribute(\"dy\", index === 0 ? \"0\" : \"1.2em\");\n tspan.textContent = line || \" \";\n textElement.appendChild(tspan);\n });\n }\n\n // Create TextShape wrapper\n const textShape = wrapTextElement(gElement);\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(textShape);\n }\n\n // Push undo action for mode conversion\n pushModeConvertAction(oldCodeShape, codeGroupElement, 'code', textShape, gElement, 'text');\n\n // Select the new text shape\n selectElement(gElement);\n}\n\nfunction pushModeConvertAction(oldShape, oldElement, oldType, newShape, newElement, newType) {\n pushCreateAction({\n type: 'modeConvert',\n oldShape: oldShape,\n oldElement: oldElement,\n oldType: oldType,\n newShape: newShape,\n newElement: newElement,\n newType: newType,\n element: newShape,\n shapeName: newType\n });\n}\n\n// Update toggle state when a shape is selected\nfunction updateCodeToggleForShape(shapeName) {\n const isCode = shapeName === 'code';\n textCodeOptions.forEach(el => el.classList.remove(\"selected\"));\n textCodeOptions.forEach(el => {\n if ((el.getAttribute(\"data-id\") === \"true\") === isCode) {\n el.classList.add(\"selected\");\n }\n });\n if (languageSelector) {\n languageSelector.classList.toggle(\"hidden\", !isCode);\n }\n // Update the language dropdown to match the selected code block\n if (isCode && codeLanguageSelect) {\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeEl = selectedCode.querySelector('text');\n const lang = codeEl?.getAttribute(\"data-language\") || \"auto\";\n codeLanguageSelect.value = lang;\n }\n }\n}\n\n// React sidebar bridge \u2014 update currently selected text/code shape\nwindow.updateSelectedTextStyle = function(changes) {\n const el = selectedElement || (window.currentShape && window.currentShape.shapeName === 'text' ? window.currentShape.group : null);\n if (!el) return;\n const textElement = el.querySelector('text');\n if (!textElement) return;\n\n if (changes.color) {\n textElement.setAttribute('fill', changes.color);\n textColor = changes.color;\n }\n if (changes.font) {\n textElement.setAttribute('font-family', changes.font);\n textFont = changes.font;\n }\n if (changes.fontSize) {\n textElement.setAttribute('font-size', changes.fontSize);\n textSize = changes.fontSize;\n }\n};\n\n// Expose select/deselect for external callers (Selection.js, TextShape.selectShape)\nwindow.__deselectTextElement = deselectElement;\nwindow.__selectTextElement = selectElement;\n\n// React sidebar bridge \u2014 text \u2194 code conversion\nwindow.__convertTextToCode = function() {\n if (selectedElement && selectedElement.getAttribute('data-type') === 'text-group') {\n convertTextToCode(selectedElement);\n }\n};\nwindow.__convertCodeToText = function() {\n // Try codeTool's selectedCodeBlock first, then check textTool's selectedElement\n let codeBlock = getSelectedCodeBlock();\n if (!codeBlock && selectedElement && selectedElement.getAttribute('data-type') === 'code-group') {\n codeBlock = selectedElement;\n }\n if (codeBlock) {\n convertCodeToText(codeBlock);\n }\n};\nwindow.__setCodeLanguage = function(lang) {\n setCodeLanguage(lang);\n // Re-highlight selected code block with new language\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeElement = selectedCode.querySelector('text');\n if (codeElement) {\n codeElement.setAttribute('data-language', lang);\n const textContent = extractTextFromCodeElement(codeElement);\n // Clear and re-highlight\n while (codeElement.firstChild) codeElement.removeChild(codeElement.firstChild);\n const highlighted = applySyntaxHighlightingToSVG(textContent, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(selectedCode, codeElement);\n }\n }\n};\n\nexport { handleTextMouseDown, handleTextMouseMove, handleTextMouseUp, updateCodeToggleForShape, deselectElement as deselectTextElement, enterEditMode };\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -427,7 +427,7 @@ function useSketchEngine(svgRef, ready = true) {
427
427
  setZoom: (zoom) => useSketchStore_default.setState({ zoom }),
428
428
  getState: () => useSketchStore_default.getState()
429
429
  };
430
- const { SketchEngine } = await import("./SketchEngine-R5RCQF3L.js");
430
+ const { SketchEngine } = await import("./SketchEngine-DWDJP2BB.js");
431
431
  if (cancelled) return;
432
432
  const engine = new SketchEngine(svgRef.current);
433
433
  await engine.init();
@@ -6,7 +6,7 @@ import {
6
6
  handleTextMouseMove,
7
7
  handleTextMouseUp,
8
8
  updateCodeToggleForShape
9
- } from "./chunk-VRRJEOCH.js";
9
+ } from "./chunk-TUMWA4YF.js";
10
10
  import "./chunk-LHRTXNVE.js";
11
11
  import "./chunk-BZE4CEBG.js";
12
12
  import "./chunk-L5OARZJ7.js";
@@ -18,4 +18,4 @@ export {
18
18
  handleTextMouseUp,
19
19
  updateCodeToggleForShape
20
20
  };
21
- //# sourceMappingURL=textTool-7LMEIBF5.js.map
21
+ //# sourceMappingURL=textTool-QVKV45VD.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elixpo/lixsketch",
3
- "version": "5.5.3",
3
+ "version": "5.5.5",
4
4
  "description": "Open-source SVG whiteboard engine + React canvas component with hand-drawn aesthetics — the core of LixSketch",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -194,17 +194,24 @@ function makeTextEditable(textElement, groupElement) {
194
194
  let input = document.createElement("textarea");
195
195
  input.className = "svg-text-editor";
196
196
 
197
- let textContent = "";
198
- const tspans = textElement.querySelectorAll('tspan');
199
- if (tspans.length > 0) {
200
- tspans.forEach((tspan, index) => {
201
- textContent += tspan.textContent.replace(/ /g, '\u00A0');
202
- if (index < tspans.length - 1) {
203
- textContent += "\n";
204
- }
205
- });
197
+ // Issue #48 phase D follow-up: prefer the persisted `data-wrap-
198
+ // source` when present so re-opening the editor shows the user's
199
+ // original line breaks instead of the auto-wrap output. Falls back
200
+ // to tspan-concatenation for legacy text shapes.
201
+ let textContent = '';
202
+ const storedSource = textElement.getAttribute('data-wrap-source');
203
+ if (storedSource !== null && storedSource !== '') {
204
+ textContent = storedSource.replace(/ /g, '\u00A0');
206
205
  } else {
207
- textContent = textElement.textContent.replace(/ /g, '\u00A0');
206
+ const tspans = textElement.querySelectorAll('tspan');
207
+ if (tspans.length > 0) {
208
+ tspans.forEach((tspan, index) => {
209
+ textContent += tspan.textContent.replace(/ /g, '\u00A0');
210
+ if (index < tspans.length - 1) textContent += '\n';
211
+ });
212
+ } else {
213
+ textContent = textElement.textContent.replace(/ /g, '\u00A0');
214
+ }
208
215
  }
209
216
 
210
217
  input.value = textContent;
@@ -338,6 +345,83 @@ function makeTextEditable(textElement, groupElement) {
338
345
  groupElement.style.display = "none";
339
346
  }
340
347
 
348
+ /**
349
+ * Issue #48 phase D follow-up: true text wrapping driven by E/W width.
350
+ *
351
+ * `paint(textElement, source, wrapWidth)` renders the user-typed `source`
352
+ * string into tspans, soft-wrapping any line that exceeds `wrapWidth`
353
+ * pixels (in SVG units). `wrapWidth = null | 0` falls back to the old
354
+ * "explicit newlines only" behaviour so existing text shapes that have
355
+ * never been width-resized look identical.
356
+ *
357
+ * The raw `source` is persisted on the element as `data-wrap-source`
358
+ * so subsequent edits (textarea re-open) and serialisation can rebuild
359
+ * the original content even after auto-wrap inserted line breaks.
360
+ */
361
+ function measureSegment(text, refTextElement) {
362
+ if (!text) return 0;
363
+ const NS = 'http://www.w3.org/2000/svg';
364
+ const probe = document.createElementNS(NS, 'tspan');
365
+ probe.textContent = text;
366
+ refTextElement.appendChild(probe);
367
+ let width = 0;
368
+ try { width = probe.getComputedTextLength(); } catch {}
369
+ refTextElement.removeChild(probe);
370
+ return width;
371
+ }
372
+
373
+ function paintTextContent(textElement, source, wrapWidth) {
374
+ while (textElement.firstChild) textElement.removeChild(textElement.firstChild);
375
+ const x = textElement.getAttribute('x') || 0;
376
+ const NS = 'http://www.w3.org/2000/svg';
377
+ const paragraphs = (source || '').split('\n');
378
+ const finalLines = [];
379
+
380
+ for (const paragraph of paragraphs) {
381
+ const para = paragraph.replace(/ /g, ' ');
382
+ if (!wrapWidth || wrapWidth <= 0) {
383
+ finalLines.push(para || ' ');
384
+ continue;
385
+ }
386
+ // Word-wrap the paragraph against wrapWidth.
387
+ const words = para.split(/(\s+)/); // keep separators so spacing survives
388
+ let cur = '';
389
+ for (const token of words) {
390
+ const candidate = cur + token;
391
+ if (measureSegment(candidate, textElement) > wrapWidth && cur.trim().length > 0) {
392
+ finalLines.push(cur);
393
+ cur = token.replace(/^\s+/, ''); // drop the leading break-space
394
+ } else {
395
+ cur = candidate;
396
+ }
397
+ }
398
+ finalLines.push(cur.length ? cur : ' ');
399
+ }
400
+
401
+ finalLines.forEach((line, i) => {
402
+ const tspan = document.createElementNS(NS, 'tspan');
403
+ tspan.setAttribute('x', x);
404
+ tspan.setAttribute('dy', i === 0 ? '0' : '1.2em');
405
+ tspan.textContent = line.length ? line : ' ';
406
+ textElement.appendChild(tspan);
407
+ });
408
+ }
409
+
410
+ function getWrapSource(textElement) {
411
+ const stored = textElement.getAttribute('data-wrap-source');
412
+ if (stored !== null && stored !== '') return stored;
413
+ // Fallback: rebuild from existing tspans (legacy text shapes without
414
+ // the attribute). Join each tspan as a paragraph.
415
+ const tspans = textElement.querySelectorAll('tspan');
416
+ if (tspans.length === 0) return textElement.textContent || '';
417
+ return Array.from(tspans).map((t) => (t.textContent || '').replace(/ /g, ' ')).join('\n');
418
+ }
419
+
420
+ function getWrapWidth(textElement) {
421
+ const v = parseFloat(textElement.getAttribute('data-wrap-width') || '');
422
+ return Number.isFinite(v) && v > 0 ? v : 0;
423
+ }
424
+
341
425
  function renderText(input, textElement, deleteIfEmpty = false) {
342
426
  if (!input || !document.body.contains(input)) {
343
427
  return;
@@ -395,23 +479,13 @@ function renderText(input, textElement, deleteIfEmpty = false) {
395
479
  removeSelectionFeedback();
396
480
  }
397
481
  } else {
398
- while (textElement.firstChild) {
399
- textElement.removeChild(textElement.firstChild);
400
- }
401
-
402
- const lines = text.split("\n");
403
- const x = textElement.getAttribute("x") || 0;
404
-
405
- lines.forEach((line, index) => {
406
- let tspan = document.createElementNS(
407
- "http://www.w3.org/2000/svg",
408
- "tspan"
409
- );
410
- tspan.setAttribute("x", x);
411
- tspan.setAttribute("dy", index === 0 ? "0" : "1.2em");
412
- tspan.textContent = line.replace(/\u00A0/g, ' ') || " ";
413
- textElement.appendChild(tspan);
414
- });
482
+ // Persist the raw editor content so future E/W resizes can re-wrap
483
+ // from the user's original line breaks instead of the auto-inserted
484
+ // ones. Then call the shared paint helper \u2014 it falls back to the
485
+ // legacy "explicit newlines only" path when no wrap-width is set.
486
+ const cleanSource = text.replace(/\u00A0/g, ' ');
487
+ textElement.setAttribute('data-wrap-source', cleanSource);
488
+ paintTextContent(textElement, cleanSource, getWrapWidth(textElement));
415
489
 
416
490
  gElement.style.display = 'block';
417
491
 
@@ -473,11 +547,18 @@ function createSelectionFeedback(groupElement) {
473
547
  selectionBox.setAttribute("pointer-events", "none");
474
548
  groupElement.appendChild(selectionBox);
475
549
 
550
+ // Issue #48 bug #5 + phase D follow-up: E/W midpoint anchors drive
551
+ // TRUE word-wrap. Dragging E/W sets `data-wrap-width` on the text
552
+ // element; the paint helper re-flows tspans so the text wraps into
553
+ // multiple lines. Corner anchors keep their font-size scaling
554
+ // behaviour.
476
555
  const handlesData = [
477
556
  { name: 'nw', x: selX, y: selY, cursor: 'nwse-resize' },
478
557
  { name: 'ne', x: selX + selWidth, y: selY, cursor: 'nesw-resize' },
479
558
  { name: 'sw', x: selX, y: selY + selHeight, cursor: 'nesw-resize' },
480
- { name: 'se', x: selX + selWidth, y: selY + selHeight, cursor: 'nwse-resize' }
559
+ { name: 'se', x: selX + selWidth, y: selY + selHeight, cursor: 'nwse-resize' },
560
+ { name: 'e', x: selX + selWidth, y: selY + selHeight / 2, cursor: 'ew-resize' },
561
+ { name: 'w', x: selX, y: selY + selHeight / 2, cursor: 'ew-resize' },
481
562
  ];
482
563
 
483
564
  resizeHandles = {};
@@ -582,7 +663,11 @@ function updateSelectionFeedback() {
582
663
  { name: 'nw', x: selX, y: selY },
583
664
  { name: 'ne', x: selX + selWidth, y: selY },
584
665
  { name: 'sw', x: selX, y: selY + selHeight },
585
- { name: 'se', x: selX + selWidth, y: selY + selHeight }
666
+ { name: 'se', x: selX + selWidth, y: selY + selHeight },
667
+ // Issue #48 bug #5: keep E/W midpoint handles aligned with the
668
+ // resize so they follow the box as it scales.
669
+ { name: 'e', x: selX + selWidth, y: selY + selHeight / 2 },
670
+ { name: 'w', x: selX, y: selY + selHeight / 2 },
586
671
  ];
587
672
 
588
673
  handlesData.forEach(handle => {
@@ -874,6 +959,7 @@ const handleMouseMove = (event) => {
874
959
  const startHeight = startBBox.height;
875
960
 
876
961
  let anchorX, anchorY;
962
+ const isEdgeWidth = currentResizeHandle === 'e' || currentResizeHandle === 'w';
877
963
 
878
964
  switch (currentResizeHandle) {
879
965
  case 'nw':
@@ -892,11 +978,35 @@ const handleMouseMove = (event) => {
892
978
  anchorX = startX;
893
979
  anchorY = startY;
894
980
  break;
981
+ case 'e':
982
+ anchorX = startX;
983
+ anchorY = startY + startHeight / 2;
984
+ break;
985
+ case 'w':
986
+ anchorX = startX + startWidth;
987
+ anchorY = startY + startHeight / 2;
988
+ break;
989
+ }
990
+
991
+ // Issue #48 phase D follow-up: E/W now drives TRUE word-wrap.
992
+ // The drag distance becomes the wrap-width target; we re-paint
993
+ // the text with the new width and short-circuit the font-size
994
+ // scaling path that the corners use.
995
+ if (isEdgeWidth) {
996
+ const newWrap = Math.abs(currentPoint.x - anchorX);
997
+ const minWrap = startFontSize * 2; // at least ~one short word wide
998
+ const clampedWrap = Math.max(minWrap, newWrap);
999
+ textElement.setAttribute('data-wrap-width', String(clampedWrap));
1000
+ const source = getWrapSource(textElement);
1001
+ paintTextContent(textElement, source, clampedWrap);
1002
+ if (typeof updateSelectionFeedback === 'function') {
1003
+ setTimeout(updateSelectionFeedback, 0);
1004
+ }
1005
+ return;
895
1006
  }
896
1007
 
897
1008
  const newWidth = Math.abs(currentPoint.x - anchorX);
898
1009
  const newHeight = Math.abs(currentPoint.y - anchorY);
899
-
900
1010
  const chosenScale = newHeight / startHeight;
901
1011
 
902
1012
  const minScale = 0.1;
@@ -930,6 +1040,14 @@ const handleMouseMove = (event) => {
930
1040
  newAnchorX = currentBBox.x;
931
1041
  newAnchorY = currentBBox.y;
932
1042
  break;
1043
+ case 'e':
1044
+ newAnchorX = currentBBox.x;
1045
+ newAnchorY = currentBBox.y + currentBBox.height / 2;
1046
+ break;
1047
+ case 'w':
1048
+ newAnchorX = currentBBox.x + currentBBox.width;
1049
+ newAnchorY = currentBBox.y + currentBBox.height / 2;
1050
+ break;
933
1051
  }
934
1052
 
935
1053
  const deltaX = anchorX - newAnchorX;
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/tools/textTool.js"],
4
- "sourcesContent": ["/* eslint-disable */\n// Text tool event handlers - extracted from writeText.js\nimport {\n pushCreateAction,\n pushDeleteAction,\n pushDeleteActionWithAttachments,\n pushTransformAction,\n pushOptionsChangeAction,\n pushFrameAttachmentAction,\n setTextReferences,\n updateSelectedElement\n} from '../core/UndoRedo.js';\nimport { cleanupAttachments, updateAttachedArrows } from './arrowTool.js';\n\nfunction getThemeStroke() { if (typeof document === \"undefined\") return \"#fff\"; return document.body && document.body.classList.contains(\"theme-dark\") ? \"#fff\" : \"#1a1a2e\"; }\nimport {\n addCodeBlock,\n wrapCodeElement,\n selectCodeBlock,\n deselectCodeBlock,\n applySyntaxHighlightingToSVG,\n createHighlightedSVGText,\n updateCodeBackground,\n extractTextFromCodeElement,\n setCodeLanguage,\n getCodeLanguage,\n getSelectedCodeBlock\n} from './codeTool.js';\n\nlet textSize = \"30px\";\nlet textFont = \"lixFont\";\nlet textColor = null;\nlet textAlign = \"left\";\n\nlet textColorOptions = document.querySelectorAll(\".textColorSpan\");\nlet textFontOptions = document.querySelectorAll(\".textFontSpan\");\nlet textSizeOptions = document.querySelectorAll(\".textSizeSpan\");\nlet textAlignOptions = document.querySelectorAll(\".textAlignSpan\");\n\nlet selectedElement = null;\nlet selectionBox = null;\nlet resizeHandles = {};\nlet dragOffsetX, dragOffsetY;\nlet isDragging = false;\nlet isResizing = false;\nlet currentResizeHandle = null;\nlet startBBox = null;\nlet startFontSize = null;\nlet startPoint = null;\nlet isRotating = false;\nlet rotationStartAngle = 0;\nlet rotationStartTransform = null;\nlet initialHandlePosRelGroup = null;\nlet initialGroupTx = 0;\nlet initialGroupTy = 0;\nlet initialInverseScreenCTM = null;\n\n// Frame attachment variables\nlet draggedShapeInitialFrameText = null;\nlet hoveredFrameText = null;\n\nsetTextReferences(selectedElement, updateSelectionFeedback, svg);\n\n\nfunction switchToSelectionTool() {\n if (window.__sketchStoreApi) {\n window.__sketchStoreApi.setActiveTool('select', { afterDraw: true });\n } else {\n window.isSelectionToolActive = true;\n }\n}\n\n\n// Convert group element to our TextShape class\nfunction wrapTextElement(groupElement) {\n const textShape = new TextShape(groupElement);\n return textShape;\n}\n\nfunction getSVGCoordinates(event, element = svg) {\n if (!svg || !svg.createSVGPoint) {\n console.error(\"SVG element or createSVGPoint method not available.\");\n return { x: 0, y: 0 };\n }\n let pt = svg.createSVGPoint();\n pt.x = event.clientX;\n pt.y = event.clientY;\n\n try {\n let screenCTM = (element && typeof element.getScreenCTM === 'function' && element.getScreenCTM()) || svg.getScreenCTM();\n if (!screenCTM) {\n console.error(\"Could not get Screen CTM.\");\n return { x: event.clientX, y: event.clientY };\n }\n let svgPoint = pt.matrixTransform(screenCTM.inverse());\n return {\n x: svgPoint.x,\n y: svgPoint.y,\n };\n } catch (error) {\n console.error(\"Error getting SVG coordinates:\", error);\n return { x: event.clientX, y: event.clientY };\n }\n}\n\nfunction addText(event) {\n let { x, y } = getSVGCoordinates(event);\n\n let gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"text-group\");\n gElement.setAttribute(\"transform\", `translate(${x}, ${y})`);\n\n let textElement = document.createElementNS(\n \"http://www.w3.org/2000/svg\",\n \"text\"\n );\n let textAlignElement = \"start\";\n if (textAlign === \"center\") textAlignElement = \"middle\";\n else if (textAlign === \"right\") textAlignElement = \"end\";\n\n textElement.setAttribute(\"x\", 0);\n textElement.setAttribute(\"y\", 0);\n textElement.setAttribute(\"fill\", textColor ?? getThemeStroke());\n textElement.setAttribute(\"font-size\", textSize);\n textElement.setAttribute(\"font-family\", textFont);\n textElement.setAttribute(\"text-anchor\", textAlignElement);\n textElement.setAttribute(\"cursor\", \"default\");\n textElement.setAttribute(\"white-space\", \"pre\");\n textElement.setAttribute(\"dominant-baseline\", \"hanging\");\n // Issue #48 bugs #1 / #3: reverted the previous `bounding-box` hit\n // area (introduced for #34 #5b) \u2014 extending the text's hit zone to\n // its full bbox meant the negative space between glyphs and around\n // the text was stealing clicks from shapes drawn ON TOP of the text\n // (drag a rect over text \u2192 text intercepts; select text then click a\n // different shape elsewhere \u2192 click lands in text's invisible bbox\n // and the text stays selected). Default `painted` only fires on\n // glyph pixels, which restores the expected pass-through. Trade-off:\n // empty-padding clicks no longer select the text \u2014 the user has to\n // click on the letters.\n textElement.setAttribute(\"pointer-events\", \"painted\");\n textElement.textContent = \"\";\n\n gElement.setAttribute(\"data-x\", x);\n gElement.setAttribute(\"data-y\", y);\n textElement.setAttribute(\"data-initial-size\", textSize);\n textElement.setAttribute(\"data-initial-font\", textFont);\n textElement.setAttribute(\"data-initial-color\", textColor ?? getThemeStroke());\n textElement.setAttribute(\"data-initial-align\", textAlign);\n textElement.setAttribute(\"data-type\", \"text\");\n gElement.appendChild(textElement);\n svg.appendChild(gElement);\n \n // Attach ID to both group and text element\n const shapeID = `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute('id', shapeID);\n textElement.setAttribute('id', `${shapeID}-text`);\n \n // Create TextShape wrapper for frame functionality\n const textShape = wrapTextElement(gElement);\n \n // Add to shapes array for arrow attachment and frame functionality\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(textShape);\n }\n \n pushCreateAction({\n type: 'text',\n element: textShape,\n shapeName: 'text'\n });\n\n // Check if text was created inside a frame and add it\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(shape => {\n if (shape.shapeName === 'frame' && shape.isShapeInFrame(textShape)) {\n shape.addShapeToFrame(textShape);\n }\n });\n }\n\n makeTextEditable(textElement, gElement);\n}\n\nfunction makeTextEditable(textElement, groupElement) {\n\n if (document.querySelector(\"textarea.svg-text-editor\")) {\n return;\n }\n\n if (selectedElement) {\n deselectElement();\n }\n\n let input = document.createElement(\"textarea\");\n input.className = \"svg-text-editor\";\n\n let textContent = \"\";\n const tspans = textElement.querySelectorAll('tspan');\n if (tspans.length > 0) {\n tspans.forEach((tspan, index) => {\n textContent += tspan.textContent.replace(/ /g, '\\u00A0');\n if (index < tspans.length - 1) {\n textContent += \"\\n\";\n }\n });\n } else {\n textContent = textElement.textContent.replace(/ /g, '\\u00A0');\n }\n\n input.value = textContent;\n input.style.position = \"absolute\";\n input.style.outline = \"none\";\n input.style.padding = \"1px\";\n input.style.margin = \"0\";\n input.style.boxSizing = \"border-box\";\n input.style.overflow = \"hidden\";\n input.style.resize = \"none\";\n input.style.whiteSpace = \"pre-wrap\";\n input.style.minHeight = \"1.2em\";\n input.style.zIndex = \"10000\";\n\n const svgRect = svg.getBoundingClientRect();\n\n // Use the group element's own screenCTM which includes group transform + SVG viewBox transform\n const textBBox = textElement.getBBox();\n let pt = svg.createSVGPoint();\n pt.x = textBBox.x;\n pt.y = textBBox.y;\n\n const groupCTM = groupElement.getScreenCTM() || svg.getScreenCTM();\n let screenPt = pt.matrixTransform(groupCTM);\n\n input.style.left = `${screenPt.x}px`;\n input.style.top = `${screenPt.y}px`;\n\n const svgZoomFactor = svg.getScreenCTM() ? svg.getScreenCTM().a : 1;\n const screenWidth = textBBox.width * svgZoomFactor;\n\n input.style.width = \"auto\";\n input.style.height = \"auto\";\n\n const currentFontSize = textElement.getAttribute(\"font-size\") || \"30px\";\n const currentFontFamily = textElement.getAttribute(\"font-family\") || \"lixFont\";\n const currentFill = textElement.getAttribute(\"fill\") || \"#fff\";\n const currentAnchor = textElement.getAttribute(\"text-anchor\") || \"start\";\n // Scale font-size by zoom so the textarea matches what the user sees on canvas\n const rawSize = parseFloat(currentFontSize) || 30;\n const scaledFontSize = `${rawSize * svgZoomFactor}px`;\n\n input.style.minWidth = \"150px\";\n input.style.minHeight = \"1.5em\";\n input.style.width = \"auto\";\n input.style.height = \"auto\";\n input.style.overflow = \"visible\";\n input.style.whiteSpace = \"pre-wrap\";\n input.style.wordBreak = \"break-word\";\n input.style.fontSize = scaledFontSize;\n input.style.fontFamily = currentFontFamily;\n input.style.color = currentFill;\n input.style.lineHeight = \"1.2em\";\n input.style.textAlign = currentAnchor === \"middle\" ? \"center\" : currentAnchor === \"end\" ? \"right\" : \"left\";\n input.style.backgroundColor = \"transparent\";\n // Issue #34 bug #5a + #48 bug #2: dashed creation outline that grows\n // with the text. Originally `rgba(255,255,255,0.55)` \u2014 invisible on\n // the new light canvas. Read the active theme so the dashes stay\n // visible in both modes.\n const _isDark = typeof document !== 'undefined'\n && document.body\n && document.body.classList.contains('theme-dark');\n input.style.border = _isDark\n ? \"1px dashed rgba(255,255,255,0.55)\"\n : \"1px dashed rgba(40,40,60,0.45)\";\n input.style.borderRadius = \"3px\";\n input.style.outline = \"none\";\n document.body.appendChild(input);\n\n const adjustHeight = () => {\n input.style.height = 'auto';\n input.style.height = input.scrollHeight + 'px';\n const maxHeight = svgRect.height - (screenPt.y);\n if (input.scrollHeight > maxHeight) {\n input.style.height = maxHeight + 'px';\n input.style.overflowY = 'auto';\n } else {\n input.style.overflowY = 'hidden';\n }\n };\n\n const adjustWidth = () => {\n input.style.width = 'auto';\n const maxWidth = svgRect.width - (screenPt.x);\n const contentWidth = Math.max(input.scrollWidth, 150);\n if (contentWidth > maxWidth) {\n input.style.width = maxWidth + 'px';\n input.style.overflowX = 'auto';\n } else {\n input.style.width = contentWidth + 'px';\n input.style.overflowX = 'hidden';\n }\n };\n adjustHeight();\n adjustWidth();\n\n setTimeout(() => {\n input.focus();\n input.select();\n }, 50);\n\n input.addEventListener('input', adjustHeight);\n input.addEventListener('input', adjustWidth);\n\n input.addEventListener(\"keydown\", function (e) {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n renderText(input, textElement, true);\n } else if (e.key === \"Escape\") {\n e.preventDefault();\n renderText(input, textElement, true);\n }\n });\n\n input.originalTextElement = textElement;\n input.textGroup = groupElement;\n\n const handleClickOutside = (event) => {\n if (!input.contains(event.target)) {\n renderText(input, textElement, true);\n document.removeEventListener('pointerdown', handleClickOutside, true);\n }\n };\n document.addEventListener('pointerdown', handleClickOutside, true);\n input.handleClickOutside = handleClickOutside;\n\n // Set text cursor on the element during edit mode\n const textEl = groupElement.querySelector('text');\n if (textEl) textEl.setAttribute(\"cursor\", \"text\");\n\n groupElement.style.display = \"none\";\n}\n\nfunction renderText(input, textElement, deleteIfEmpty = false) {\n if (!input || !document.body.contains(input)) {\n return;\n }\n\n const text = input.value || \"\";\n const gElement = input.textGroup;\n\n if (input.handleClickOutside) {\n document.removeEventListener('pointerdown', input.handleClickOutside, true);\n }\n\n document.body.removeChild(input);\n\n // Reset cursor back to default after edit mode ends\n if (textElement) textElement.setAttribute(\"cursor\", \"default\");\n\n if (!gElement || !textElement) {\n return;\n }\n\n if (!gElement.parentNode) {\n if (selectedElement === gElement) {\n deselectElement();\n }\n return;\n }\n\n if (deleteIfEmpty && text.trim() === \"\") {\n // Find the TextShape wrapper\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === gElement);\n if (textShape) {\n const idx = shapes.indexOf(textShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Use enhanced delete action for text with arrow attachments\n pushDeleteActionWithAttachments({\n type: 'text',\n element: textShape || gElement,\n shapeName: 'text'\n });\n\n // Clean up any arrow attachments before deleting\n if (typeof cleanupAttachments === 'function') {\n cleanupAttachments(gElement);\n }\n\n svg.removeChild(gElement);\n if (selectedElement === gElement) {\n selectedElement = null;\n removeSelectionFeedback();\n }\n } else {\n while (textElement.firstChild) {\n textElement.removeChild(textElement.firstChild);\n }\n\n const lines = text.split(\"\\n\");\n const x = textElement.getAttribute(\"x\") || 0;\n\n lines.forEach((line, index) => {\n let tspan = document.createElementNS(\n \"http://www.w3.org/2000/svg\",\n \"tspan\"\n );\n tspan.setAttribute(\"x\", x);\n tspan.setAttribute(\"dy\", index === 0 ? \"0\" : \"1.2em\");\n tspan.textContent = line.replace(/\\u00A0/g, ' ') || \" \";\n textElement.appendChild(tspan);\n });\n\n gElement.style.display = 'block';\n\n // Update attached arrows after text content change\n updateAttachedArrows(gElement);\n\n if (selectedElement === gElement) {\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n\n // After rendering text, switch to selection tool and auto-select\n if (gElement.parentNode) {\n switchToSelectionTool();\n // Defer selection so the tool switch (async React state) completes first\n requestAnimationFrame(() => selectElement(gElement));\n }\n}\n\nfunction createSelectionFeedback(groupElement) {\n if (!groupElement) return;\n removeSelectionFeedback();\n\n const textElement = groupElement.querySelector('text');\n if (!textElement) {\n return;\n }\n\n const bbox = textElement.getBBox();\n\n const zoom = window.currentZoom || 1;\n const padding = 8 / zoom;\n const handleSize = 10 / zoom;\n const handleOffset = handleSize / 2;\n const anchorStrokeWidth = 2;\n\n const selX = bbox.x - padding;\n const selY = bbox.y - padding;\n const selWidth = bbox.width + 2 * padding;\n const selHeight = bbox.height + 2 * padding;\n\n const outlinePoints = [\n [selX, selY],\n [selX + selWidth, selY],\n [selX + selWidth, selY + selHeight],\n [selX, selY + selHeight],\n [selX, selY]\n ];\n\n const pointsAttr = outlinePoints.map(p => p.join(',')).join(' ');\n selectionBox = document.createElementNS(\"http://www.w3.org/2000/svg\", \"polyline\");\n selectionBox.setAttribute(\"class\", \"selection-box\");\n selectionBox.setAttribute(\"points\", pointsAttr);\n selectionBox.setAttribute(\"fill\", \"none\");\n selectionBox.setAttribute(\"stroke\", \"#5B57D1\");\n selectionBox.setAttribute(\"stroke-width\", \"1.5\");\n selectionBox.setAttribute(\"stroke-dasharray\", `${4 / zoom} ${2 / zoom}`);\n selectionBox.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n selectionBox.setAttribute(\"pointer-events\", \"none\");\n groupElement.appendChild(selectionBox);\n\n const handlesData = [\n { name: 'nw', x: selX, y: selY, cursor: 'nwse-resize' },\n { name: 'ne', x: selX + selWidth, y: selY, cursor: 'nesw-resize' },\n { name: 'sw', x: selX, y: selY + selHeight, cursor: 'nesw-resize' },\n { name: 'se', x: selX + selWidth, y: selY + selHeight, cursor: 'nwse-resize' }\n ];\n\n resizeHandles = {};\n handlesData.forEach(handle => {\n const handleRect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n handleRect.setAttribute(\"class\", `resize-handle resize-handle-${handle.name}`);\n handleRect.setAttribute(\"x\", handle.x - handleOffset);\n handleRect.setAttribute(\"y\", handle.y - handleOffset);\n handleRect.setAttribute(\"width\", handleSize);\n handleRect.setAttribute(\"height\", handleSize);\n handleRect.setAttribute(\"fill\", \"#121212\");\n handleRect.setAttribute(\"stroke\", \"#5B57D1\");\n handleRect.setAttribute(\"stroke-width\", anchorStrokeWidth);\n handleRect.setAttribute(\"vector-effect\", \"non-scaling-stroke\");\n handleRect.style.cursor = handle.cursor;\n handleRect.setAttribute(\"data-anchor\", handle.name);\n groupElement.appendChild(handleRect);\n resizeHandles[handle.name] = handleRect;\n\n handleRect.addEventListener('pointerdown', (e) => {\n if (window.isSelectionToolActive) {\n e.stopPropagation();\n startResize(e, handle.name);\n }\n });\n });\n\n const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };\n const rotationAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n rotationAnchor.setAttribute('cx', rotationAnchorPos.x);\n rotationAnchor.setAttribute('cy', rotationAnchorPos.y);\n rotationAnchor.setAttribute('r', 8);\n rotationAnchor.setAttribute('class', 'rotate-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.style.cursor = 'grab';\n rotationAnchor.setAttribute('pointer-events', 'all');\n groupElement.appendChild(rotationAnchor);\n\n resizeHandles.rotate = rotationAnchor;\n\n rotationAnchor.addEventListener('pointerdown', (e) => {\n if (window.isSelectionToolActive) {\n e.stopPropagation();\n startRotation(e);\n }\n });\n\n rotationAnchor.addEventListener('mouseover', function() {\n if (!isResizing && !isDragging) {\n this.style.cursor = 'grab';\n }\n });\n\n rotationAnchor.addEventListener('mouseout', function() {\n if (!isResizing && !isDragging) {\n this.style.cursor = 'default';\n }\n });\n}\n\nfunction updateSelectionFeedback() {\n if (!selectedElement || !selectionBox) return;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const wasHidden = selectedElement.style.display === 'none';\n if (wasHidden) selectedElement.style.display = 'block';\n\n const bbox = textElement.getBBox();\n\n if (wasHidden) selectedElement.style.display = 'none';\n\n if (bbox.width === 0 && bbox.height === 0 && textElement.textContent.trim() !== \"\") {\n }\n\n const zoom2 = window.currentZoom || 1;\n const padding = 8 / zoom2;\n const handleSize = 10 / zoom2;\n const handleOffset = handleSize / 2;\n\n const selX = bbox.x - padding;\n const selY = bbox.y - padding;\n const selWidth = Math.max(bbox.width + 2 * padding, handleSize);\n const selHeight = Math.max(bbox.height + 2 * padding, handleSize);\n\n const outlinePoints = [\n [selX, selY],\n [selX + selWidth, selY],\n [selX + selWidth, selY + selHeight],\n [selX, selY + selHeight],\n [selX, selY]\n ];\n\n const pointsAttr = outlinePoints.map(p => p.join(',')).join(' ');\n selectionBox.setAttribute(\"points\", pointsAttr);\n\n const handlesData = [\n { name: 'nw', x: selX, y: selY },\n { name: 'ne', x: selX + selWidth, y: selY },\n { name: 'sw', x: selX, y: selY + selHeight },\n { name: 'se', x: selX + selWidth, y: selY + selHeight }\n ];\n\n handlesData.forEach(handle => {\n const handleRect = resizeHandles[handle.name];\n if (handleRect) {\n handleRect.setAttribute(\"x\", handle.x - handleOffset);\n handleRect.setAttribute(\"y\", handle.y - handleOffset);\n }\n });\n\n const rotationAnchor = resizeHandles.rotate;\n if (rotationAnchor) {\n const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };\n rotationAnchor.setAttribute('cx', rotationAnchorPos.x);\n rotationAnchor.setAttribute('cy', rotationAnchorPos.y);\n }\n}\n\nfunction startRotation(event) {\n if (!selectedElement || event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n isRotating = true;\n isDragging = false;\n isResizing = false;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n const mousePos = getSVGCoordinates(event);\n\n let centerPoint = svg.createSVGPoint();\n centerPoint.x = centerX;\n centerPoint.y = centerY;\n\n const groupTransform = selectedElement.transform.baseVal.consolidate();\n if (groupTransform) {\n centerPoint = centerPoint.matrixTransform(groupTransform.matrix);\n }\n\n rotationStartAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n rotationStartTransform = currentTransform ? currentTransform.matrix : svg.createSVGMatrix();\n\n svg.style.cursor = 'grabbing';\n\n window.addEventListener('pointermove', handleMouseMove);\n window.addEventListener('pointerup', handleMouseUp);\n}\n\nfunction removeSelectionFeedback(element) {\n const target = element || selectedElement;\n if (target) {\n target.querySelectorAll(\".selection-box, .resize-handle, .rotate-anchor\").forEach(el => el.remove());\n }\n // Also clean up any orphaned selection elements in all text groups\n svg.querySelectorAll('g[data-type=\"text-group\"] .selection-box, g[data-type=\"text-group\"] .resize-handle, g[data-type=\"text-group\"] .rotate-anchor').forEach(el => el.remove());\n\n selectionBox = null;\n resizeHandles = {};\n}\n\nfunction selectElement(groupElement) {\n if (!groupElement || !groupElement.parentNode) return;\n if (groupElement === selectedElement) return;\n\n deselectElement();\n selectedElement = groupElement;\n selectedElement.classList.add(\"selected\");\n createSelectionFeedback(selectedElement);\n\n updateSelectedElement(selectedElement);\n updateCodeToggleForShape('text');\n\n // Update global currentShape so EventDispatcher can route to other tools later\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n const wrapper = shapes.find(s => s.element === groupElement || s.group === groupElement);\n if (wrapper) {\n currentShape = wrapper;\n }\n }\n\n // Show text property panel when text is selected\n if (window.__showSidebarForShape) window.__showSidebarForShape('text');\n}\n\nfunction deselectElement() {\n const activeEditor = document.querySelector(\"textarea.svg-text-editor\");\n if (activeEditor) {\n let textElement = activeEditor.originalTextElement;\n if (textElement) {\n renderText(activeEditor, textElement, true);\n } else if (document.body.contains(activeEditor)) {\n document.body.removeChild(activeEditor);\n }\n }\n\n if (selectedElement) {\n removeSelectionFeedback();\n selectedElement.classList.remove(\"selected\");\n selectedElement = null;\n\n updateSelectedElement(null);\n\n // Clear global currentShape if it was a text shape\n if (currentShape && (currentShape.shapeName === 'text' || currentShape.shapeName === 'code')) {\n currentShape = null;\n }\n\n // Hide text property panel if we're in selection mode (not text tool)\n if (isSelectionToolActive) {\n textSideBar.classList.add(\"hidden\");\n }\n }\n\n if (isRotating) {\n isRotating = false;\n rotationStartAngle = 0;\n rotationStartTransform = null;\n svg.style.cursor = 'default';\n\n window.removeEventListener('pointermove', handleMouseMove);\n window.removeEventListener('pointerup', handleMouseUp);\n }\n}\n\nfunction startDrag(event) {\n if (!selectedElement || event.button !== 0) return;\n\n if (event.target.closest('.resize-handle')) {\n return;\n }\n\n isDragging = true;\n isResizing = false;\n event.preventDefault();\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n const initialTranslateX = currentTransform ? currentTransform.matrix.e : 0;\n const initialTranslateY = currentTransform ? currentTransform.matrix.f : 0;\n\n startPoint = getSVGCoordinates(event);\n\n dragOffsetX = startPoint.x - initialTranslateX;\n dragOffsetY = startPoint.y - initialTranslateY;\n\n // Find the TextShape wrapper for frame functionality\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n }\n\n if (textShape) {\n // Store initial frame state\n draggedShapeInitialFrameText = textShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (textShape.parentFrame) {\n textShape.parentFrame.temporarilyRemoveFromFrame(textShape);\n }\n }\n\n svg.style.cursor = 'grabbing';\n\n svg.addEventListener('pointermove', handleMouseMove);\n svg.addEventListener('pointerup', handleMouseUp);\n}\n\nfunction startResize(event, anchor) {\n if (!selectedElement || event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n\n isResizing = true;\n isDragging = false;\n currentResizeHandle = anchor;\n\n const textElement = selectedElement.querySelector('text');\n if (!textElement) {\n isResizing = false;\n return;\n }\n\n startBBox = textElement.getBBox();\n startFontSize = parseFloat(textElement.getAttribute(\"font-size\") || 30);\n if (isNaN(startFontSize)) startFontSize = 30;\n\n startPoint = getSVGCoordinates(event, selectedElement);\n\n // Freeze the group's screen CTM at resize start so mouse\u2192local mapping stays stable\n const groupScreenCTM = selectedElement.getScreenCTM();\n initialInverseScreenCTM = groupScreenCTM ? groupScreenCTM.inverse() : null;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n initialGroupTx = currentTransform ? currentTransform.matrix.e : 0;\n initialGroupTy = currentTransform ? currentTransform.matrix.f : 0;\n\n const padding = 3;\n const startX = startBBox.x - padding;\n const startY = startBBox.y - padding;\n const startWidth = startBBox.width + 2 * padding;\n const startHeight = startBBox.height + 2 * padding;\n\n let hx = startX;\n let hy = startY;\n if (anchor.includes('e')) { hx = startX + startWidth; }\n if (anchor.includes('s')) { hy = startY + startHeight; }\n initialHandlePosRelGroup = { x: hx, y: hy };\n\n svg.style.cursor = resizeHandles[anchor]?.style.cursor || 'default';\n\n svg.addEventListener('pointermove', handleMouseMove);\n svg.addEventListener('pointerup', handleMouseUp);\n}\n\n\nconst handleMouseMove = (event) => {\n if (!selectedElement) return;\n event.preventDefault();\n\n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: event.clientX - svgRect.left, \n y: event.clientY - svgRect.top\n };\n\n if (isDragging) {\n const currentPoint = getSVGCoordinates(event);\n const newTranslateX = currentPoint.x - dragOffsetX;\n const newTranslateY = currentPoint.y - dragOffsetY;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n selectedElement.setAttribute('transform',\n `translate(${newTranslateX}, ${newTranslateY}) rotate(${angle}, ${centerX}, ${centerY})`\n );\n } else {\n selectedElement.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY})`);\n }\n } else {\n selectedElement.setAttribute('transform', `translate(${newTranslateX}, ${newTranslateY})`);\n }\n\n // Update frame containment for TextShape wrapper\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n const textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n if (textShape) {\n textShape.updateFrameContainment();\n }\n }\n\n // Update attached arrows during dragging\n updateAttachedArrows(selectedElement);\n\n } else if (isResizing) {\n const textElement = selectedElement.querySelector('text');\n if (!textElement || !startBBox || startFontSize === null || !startPoint || !initialHandlePosRelGroup) return;\n\n // Use the frozen initial CTM so the mapping doesn't shift as we change the group transform\n let currentPoint;\n if (initialInverseScreenCTM) {\n const pt = svg.createSVGPoint();\n pt.x = event.clientX;\n pt.y = event.clientY;\n currentPoint = pt.matrixTransform(initialInverseScreenCTM);\n } else {\n currentPoint = getSVGCoordinates(event, selectedElement);\n }\n\n const startX = startBBox.x;\n const startY = startBBox.y;\n const startWidth = startBBox.width;\n const startHeight = startBBox.height;\n\n let anchorX, anchorY;\n\n switch (currentResizeHandle) {\n case 'nw':\n anchorX = startX + startWidth;\n anchorY = startY + startHeight;\n break;\n case 'ne':\n anchorX = startX;\n anchorY = startY + startHeight;\n break;\n case 'sw':\n anchorX = startX + startWidth;\n anchorY = startY;\n break;\n case 'se':\n anchorX = startX;\n anchorY = startY;\n break;\n }\n\n const newWidth = Math.abs(currentPoint.x - anchorX);\n const newHeight = Math.abs(currentPoint.y - anchorY);\n\n const chosenScale = newHeight / startHeight;\n\n const minScale = 0.1;\n const maxScale = 10.0;\n const clampedScale = Math.max(minScale, Math.min(chosenScale, maxScale));\n\n const newFontSize = startFontSize * clampedScale;\n const minFontSize = 5;\n const finalFontSize = Math.max(newFontSize, minFontSize);\n\n textElement.setAttribute(\"font-size\", `${finalFontSize}px`);\n\n const currentBBox = textElement.getBBox();\n\n let newAnchorX, newAnchorY;\n\n switch (currentResizeHandle) {\n case 'nw':\n newAnchorX = currentBBox.x + currentBBox.width;\n newAnchorY = currentBBox.y + currentBBox.height;\n break;\n case 'ne':\n newAnchorX = currentBBox.x;\n newAnchorY = currentBBox.y + currentBBox.height;\n break;\n case 'sw':\n newAnchorX = currentBBox.x + currentBBox.width;\n newAnchorY = currentBBox.y;\n break;\n case 'se':\n newAnchorX = currentBBox.x;\n newAnchorY = currentBBox.y;\n break;\n }\n\n const deltaX = anchorX - newAnchorX;\n const deltaY = anchorY - newAnchorY;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n\n const newGroupTx = initialGroupTx + deltaX;\n const newGroupTy = initialGroupTy + deltaY;\n\n const centerX = currentBBox.x + currentBBox.width / 2;\n const centerY = currentBBox.y + currentBBox.height / 2;\n\n selectedElement.setAttribute('transform',\n `translate(${newGroupTx}, ${newGroupTy}) rotate(${angle}, ${centerX}, ${centerY})`\n );\n } else {\n const newGroupTx = initialGroupTx + deltaX;\n const newGroupTy = initialGroupTy + deltaY;\n selectedElement.setAttribute('transform', `translate(${newGroupTx}, ${newGroupTy})`);\n }\n\n // Update attached arrows during resizing\n updateAttachedArrows(selectedElement);\n\n clearTimeout(selectedElement.updateFeedbackTimeout);\n selectedElement.updateFeedbackTimeout = setTimeout(() => {\n updateSelectionFeedback();\n delete selectedElement.updateFeedbackTimeout;\n }, 0);\n\n } else if (isRotating) {\n const textElement = selectedElement.querySelector('text');\n if (!textElement) return;\n\n const bbox = textElement.getBBox();\n const centerX = bbox.x + bbox.width / 2;\n const centerY = bbox.y + bbox.height / 2;\n\n const mousePos = getSVGCoordinates(event);\n\n let centerPoint = svg.createSVGPoint();\n centerPoint.x = centerX;\n centerPoint.y = centerY;\n\n const groupTransform = selectedElement.transform.baseVal.consolidate();\n if (groupTransform) {\n centerPoint = centerPoint.matrixTransform(groupTransform.matrix);\n }\n\n const currentAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;\n\n const rotationDiff = currentAngle - rotationStartAngle;\n\n const newTransform = `translate(${rotationStartTransform.e}, ${rotationStartTransform.f}) rotate(${rotationDiff}, ${centerX}, ${centerY})`;\n selectedElement.setAttribute('transform', newTransform);\n\n // Update attached arrows during rotation\n updateAttachedArrows(selectedElement);\n\n updateSelectionFeedback();\n }\n};\n\n\n\nconst handleMouseUp = (event) => {\n if (event.button !== 0) return;\n\n if (isDragging && selectedElement) {\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform) {\n const finalTranslateX = currentTransform.matrix.e;\n const finalTranslateY = currentTransform.matrix.f;\n\n const initialX = parseFloat(selectedElement.getAttribute(\"data-x\")) || 0;\n const initialY = parseFloat(selectedElement.getAttribute(\"data-y\")) || 0;\n\n // Find the TextShape wrapper for frame tracking\n let textShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n textShape = shapes.find(shape => shape.shapeName === 'text' && shape.group === selectedElement);\n }\n\n // Add frame information for undo tracking\n const oldPosWithFrame = {\n x: initialX,\n y: initialY,\n rotation: extractRotationFromTransform(selectedElement) || 0,\n parentFrame: draggedShapeInitialFrameText\n };\n // Issue #34 bug #2: hoveredFrameText is the actual destination\n // tracked during drag \u2014 textShape.parentFrame is still the OLD\n // frame at this point.\n const newPosWithFrame = {\n x: finalTranslateX,\n y: finalTranslateY,\n rotation: extractRotationFromTransform(selectedElement) || 0,\n parentFrame: hoveredFrameText || null,\n };\n\n const stateChanged = initialX !== finalTranslateX || initialY !== finalTranslateY;\n const frameChanged = oldPosWithFrame.parentFrame !== newPosWithFrame.parentFrame;\n\n if (stateChanged || frameChanged) {\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n oldPosWithFrame,\n newPosWithFrame\n );\n }\n\n // Handle frame containment changes after drag\n if (textShape) {\n const finalFrame = hoveredFrameText;\n \n // If shape moved to a different frame\n if (draggedShapeInitialFrameText !== finalFrame) {\n // Remove from initial frame\n if (draggedShapeInitialFrameText) {\n draggedShapeInitialFrameText.removeShapeFromFrame(textShape);\n }\n \n // Add to new frame\n if (finalFrame) {\n finalFrame.addShapeToFrame(textShape);\n }\n \n // Track the frame change for undo\n if (frameChanged) {\n pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameText, textShape, \n finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameText);\n }\n } else if (draggedShapeInitialFrameText) {\n // Shape stayed in same frame, restore clipping\n draggedShapeInitialFrameText.restoreToFrame(textShape);\n }\n }\n\n selectedElement.setAttribute(\"data-x\", finalTranslateX);\n selectedElement.setAttribute(\"data-y\", finalTranslateY);\n }\n\n draggedShapeInitialFrameText = null;\n\n } else if (isResizing && selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const finalFontSize = textElement.getAttribute(\"font-size\");\n const initialFontSize = startFontSize;\n\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform && initialFontSize !== parseFloat(finalFontSize)) {\n const finalTranslateX = currentTransform.matrix.e;\n const finalTranslateY = currentTransform.matrix.f;\n\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n x: initialGroupTx,\n y: initialGroupTy,\n fontSize: initialFontSize,\n rotation: extractRotationFromTransform(selectedElement) || 0\n },\n {\n x: finalTranslateX,\n y: finalTranslateY,\n fontSize: parseFloat(finalFontSize),\n rotation: extractRotationFromTransform(selectedElement) || 0\n }\n );\n\n selectedElement.setAttribute(\"data-x\", finalTranslateX);\n selectedElement.setAttribute(\"data-y\", finalTranslateY);\n }\n\n clearTimeout(selectedElement.updateFeedbackTimeout);\n updateSelectionFeedback();\n }\n } else if (isRotating && selectedElement) {\n const currentTransform = selectedElement.transform.baseVal.consolidate();\n if (currentTransform && rotationStartTransform) {\n const initialRotation = Math.atan2(rotationStartTransform.b, rotationStartTransform.a) * 180 / Math.PI;\n const finalRotation = extractRotationFromTransform(selectedElement) || 0;\n\n if (Math.abs(initialRotation - finalRotation) > 1) {\n pushTransformAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n x: rotationStartTransform.e,\n y: rotationStartTransform.f,\n rotation: initialRotation\n },\n {\n x: currentTransform.matrix.e,\n y: currentTransform.matrix.f,\n rotation: finalRotation\n }\n );\n }\n\n }\n updateSelectionFeedback();\n }\n\n // Clear frame highlighting\n if (hoveredFrameText) {\n hoveredFrameText.removeHighlight();\n hoveredFrameText = null;\n }\n\n isDragging = false;\n isResizing = false;\n isRotating = false;\n currentResizeHandle = null;\n startPoint = null;\n startBBox = null;\n startFontSize = null;\n dragOffsetX = undefined;\n dragOffsetY = undefined;\n initialHandlePosRelGroup = null;\n initialGroupTx = 0;\n initialGroupTy = 0;\n rotationStartAngle = 0;\n rotationStartTransform = null;\n\n // Restore cursor based on context - keep pointer if over selected text\n svg.style.cursor = isSelectionToolActive ? 'default' : (isTextToolActive ? 'text' : 'default');\n\n // Ensure selection feedback is refreshed after transforms\n if (selectedElement) {\n setTimeout(updateSelectionFeedback, 0);\n }\n\n svg.removeEventListener('pointermove', handleMouseMove);\n svg.removeEventListener('pointerup', handleMouseUp);\n window.removeEventListener('pointermove', handleMouseMove);\n window.removeEventListener('pointerup', handleMouseUp);\n};\n\nfunction extractRotationFromTransform(element) {\n const currentTransform = element.transform.baseVal.consolidate();\n if (currentTransform) {\n const matrix = currentTransform.matrix;\n return Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;\n }\n return 0;\n}\n\n// EXPORTED EVENT HANDLERS\nconst handleTextMouseDown = function (e) {\n if (!e.target) return;\n const activeEditor = document.querySelector(\"textarea.svg-text-editor\");\n if (activeEditor && activeEditor.contains(e.target)) {\n return;\n }\n if (activeEditor && !activeEditor.contains(e.target)) {\n let textElement = activeEditor.originalTextElement;\n if (textElement) {\n // renderText switches to selection tool and auto-selects the text as a side effect.\n // Return early so we don't continue with stale tool state.\n renderText(activeEditor, textElement, true);\n return;\n } else if (document.body.contains(activeEditor)){\n document.body.removeChild(activeEditor);\n }\n }\n\n const targetGroup = e.target.closest('g[data-type=\"text-group\"]');\n\n if (isSelectionToolActive && e.button === 0) {\n if (targetGroup) {\n if (e.target.closest('.resize-handle')) {\n return;\n }\n\n // Double-click on selected text: enter edit mode\n if (e.detail >= 2 && targetGroup === selectedElement) {\n enterEditMode(targetGroup);\n e.stopPropagation();\n return;\n }\n\n if (targetGroup === selectedElement) {\n startDrag(e);\n } else {\n selectElement(targetGroup);\n startDrag(e);\n }\n } else {\n deselectElement();\n }\n\n } else if (isTextToolActive && e.button === 0) {\n if (targetGroup) {\n // Double-click: enter edit mode\n if (e.detail >= 2) {\n let textEl = targetGroup.querySelector('text');\n if (textEl) {\n makeTextEditable(textEl, targetGroup);\n e.stopPropagation();\n return;\n }\n }\n // Single-click: select and drag (same as selection tool behavior)\n if (targetGroup === selectedElement) {\n startDrag(e);\n } else {\n selectElement(targetGroup);\n startDrag(e);\n }\n } else {\n deselectElement();\n addText(e);\n }\n }\n};\n\nfunction enterEditMode(groupElement) {\n const textEl = groupElement.querySelector('text');\n if (!textEl) return;\n\n // Switch to text tool with property panel (go through store to clear other flags)\n if (window.__sketchStoreApi) {\n window.__sketchStoreApi.setActiveTool('text');\n } else {\n window.isTextToolActive = true;\n window.isSelectionToolActive = false;\n }\n toolExtraPopup();\n\n // Deselect (removes selection feedback) then open editor\n deselectElement();\n makeTextEditable(textEl, groupElement);\n}\n\nconst handleTextMouseMove = function (e) {\n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: e.clientX - svgRect.left, \n y: e.clientY - svgRect.top\n };\n\n // Handle cursor changes for text tool\n if (isTextToolActive) {\n const targetGroup = e.target?.closest?.('g[data-type=\"text-group\"]');\n if (targetGroup) {\n svg.style.cursor = 'pointer';\n } else {\n svg.style.cursor = 'crosshair';\n }\n } else if (isSelectionToolActive) {\n const targetGroup = e.target?.closest?.('g[data-type=\"text-group\"]');\n if (targetGroup) {\n svg.style.cursor = 'default';\n }\n }\n\n // Check for frame containment while creating text\n if (isTextToolActive && !isDragging && !isResizing && !isRotating) {\n // Get current mouse position for frame highlighting preview\n const { x, y } = getSVGCoordinates(e);\n \n // Create temporary text bounds for frame checking\n const tempTextBounds = {\n x: x - 50,\n y: y - 20,\n width: 100,\n height: 40\n };\n \n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.forEach(frame => {\n if (frame.shapeName === 'frame') {\n if (frame.isShapeInFrame(tempTextBounds)) {\n frame.highlightFrame();\n hoveredFrameText = frame;\n } else if (hoveredFrameText === frame) {\n frame.removeHighlight();\n hoveredFrameText = null;\n }\n }\n });\n }\n }\n};\n\nconst handleTextMouseUp = function (e) {\n // Clear frame highlighting when done with text tool operations\n if (hoveredFrameText) {\n hoveredFrameText.removeHighlight();\n hoveredFrameText = null;\n }\n};\n\n// updateAttachedArrows is imported from drawArrow.js\n\n\ntextColorOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textColorOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newColor = span.getAttribute(\"data-id\");\n const oldColor = textColor;\n textColor = newColor;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentColor = textElement.getAttribute('fill');\n\n if (currentColor !== newColor) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: currentColor,\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: newColor,\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('fill', newColor);\n }\n }\n });\n});\n\ntextFontOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textFontOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newFont = span.getAttribute(\"data-id\");\n const oldFont = textFont;\n textFont = newFont;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentFont = textElement.getAttribute('font-family');\n\n if (currentFont !== newFont) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: currentFont,\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: textElement.getAttribute('fill'),\n font: newFont,\n size: textElement.getAttribute('font-size'),\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('font-family', newFont);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\ntextSizeOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textSizeOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newSize = span.getAttribute(\"data-id\") + \"px\";\n const oldSize = textSize;\n textSize = newSize;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentSize = textElement.getAttribute('font-size');\n\n if (currentSize !== newSize) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: currentSize,\n align: textElement.getAttribute('text-anchor')\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: newSize,\n align: textElement.getAttribute('text-anchor')\n }\n );\n }\n\n textElement.setAttribute('font-size', newSize);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\ntextAlignOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textAlignOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const newAlign = span.getAttribute(\"data-id\");\n const oldAlign = textAlign;\n textAlign = newAlign;\n\n if (selectedElement) {\n const textElement = selectedElement.querySelector('text');\n if (textElement) {\n const currentAnchor = textElement.getAttribute('text-anchor');\n let newAnchor = 'start';\n if (newAlign === 'center') newAnchor = 'middle';\n else if (newAlign === 'right') newAnchor = 'end';\n\n if (currentAnchor !== newAnchor) {\n pushOptionsChangeAction(\n {\n type: 'text',\n element: selectedElement,\n shapeName: 'text'\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: currentAnchor\n },\n {\n color: textElement.getAttribute('fill'),\n font: textElement.getAttribute('font-family'),\n size: textElement.getAttribute('font-size'),\n align: newAnchor\n }\n );\n }\n\n textElement.setAttribute('text-anchor', newAnchor);\n setTimeout(updateSelectionFeedback, 0);\n }\n }\n });\n});\n\n\n// --- Code/Text Toggle Handler ---\nconst textCodeOptions = document.querySelectorAll(\".textCodeSpan\");\nconst languageSelector = document.getElementById(\"textLanguageSelector\");\nconst codeLanguageSelect = document.getElementById(\"codeLanguageSelect\");\n\ntextCodeOptions.forEach((span) => {\n span.addEventListener(\"click\", (event) => {\n event.stopPropagation();\n textCodeOptions.forEach((el) => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n\n const isCodeMode = span.getAttribute(\"data-id\") === \"true\";\n isTextInCodeMode = isCodeMode;\n\n // Show/hide language selector\n if (languageSelector) {\n languageSelector.classList.toggle(\"hidden\", !isCodeMode);\n }\n\n // Update tool flags\n if (isTextToolActive) {\n isCodeToolActive = isCodeMode;\n }\n\n // If a shape is selected, convert it\n if (isCodeMode && selectedElement) {\n // Convert text \u2192 code\n convertTextToCode(selectedElement);\n } else if (!isCodeMode && getSelectedCodeBlock()) {\n // Convert code \u2192 text\n convertCodeToText(getSelectedCodeBlock());\n }\n });\n});\n\n// Language selector handler\nif (codeLanguageSelect) {\n codeLanguageSelect.addEventListener(\"change\", (event) => {\n const lang = event.target.value;\n setCodeLanguage(lang);\n\n // If a code block is selected, re-highlight it with the new language\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeElement = selectedCode.querySelector('text');\n if (codeElement) {\n codeElement.setAttribute(\"data-language\", lang);\n // Re-render with new language highlighting\n const content = extractTextFromCodeElement(codeElement);\n while (codeElement.firstChild) {\n codeElement.removeChild(codeElement.firstChild);\n }\n const highlighted = applySyntaxHighlightingToSVG(content, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(selectedCode, codeElement);\n }\n }\n });\n}\n\nfunction convertTextToCode(textGroupElement) {\n const textElement = textGroupElement.querySelector('text');\n if (!textElement) return;\n\n // Get text content\n let textContent = \"\";\n const tspans = textElement.querySelectorAll('tspan');\n if (tspans.length > 0) {\n tspans.forEach((tspan, index) => {\n textContent += tspan.textContent;\n if (index < tspans.length - 1) textContent += \"\\n\";\n });\n } else {\n textContent = textElement.textContent || \"\";\n }\n\n // Get position from transform\n const currentTransform = textGroupElement.transform.baseVal.consolidate();\n const tx = currentTransform ? currentTransform.matrix.e : 0;\n const ty = currentTransform ? currentTransform.matrix.f : 0;\n const fontSize = textElement.getAttribute('font-size') || \"25px\";\n const color = textElement.getAttribute('fill') || \"#fff\";\n\n // Find and remove old TextShape from shapes array\n let oldTextShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n oldTextShape = shapes.find(s => s.shapeName === 'text' && s.group === textGroupElement);\n if (oldTextShape) {\n const idx = shapes.indexOf(oldTextShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Deselect current text\n deselectElement();\n\n // Remove old text group from SVG\n if (textGroupElement.parentNode) {\n textGroupElement.parentNode.removeChild(textGroupElement);\n }\n\n // Create new code block\n const gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"code-group\");\n gElement.setAttribute(\"transform\", `translate(${tx}, ${ty})`);\n\n const backgroundRect = document.createElementNS(\"http://www.w3.org/2000/svg\", \"rect\");\n backgroundRect.setAttribute(\"class\", \"code-background\");\n backgroundRect.setAttribute(\"x\", -10);\n backgroundRect.setAttribute(\"y\", -10);\n backgroundRect.setAttribute(\"width\", 300);\n backgroundRect.setAttribute(\"height\", 60);\n backgroundRect.setAttribute(\"fill\", \"#161b22\");\n backgroundRect.setAttribute(\"stroke\", \"#30363d\");\n backgroundRect.setAttribute(\"stroke-width\", \"1\");\n backgroundRect.setAttribute(\"rx\", \"6\");\n backgroundRect.setAttribute(\"ry\", \"6\");\n gElement.appendChild(backgroundRect);\n\n const codeElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n codeElement.setAttribute(\"x\", 0);\n codeElement.setAttribute(\"y\", 0);\n codeElement.setAttribute(\"fill\", color);\n codeElement.setAttribute(\"font-size\", fontSize);\n codeElement.setAttribute(\"font-family\", \"lixCode\");\n codeElement.setAttribute(\"text-anchor\", \"start\");\n codeElement.setAttribute(\"cursor\", \"default\");\n codeElement.setAttribute(\"white-space\", \"pre\");\n codeElement.setAttribute(\"dominant-baseline\", \"hanging\");\n codeElement.setAttribute(\"data-language\", getCodeLanguage());\n codeElement.setAttribute(\"data-type\", \"code\");\n\n const shapeID = `code-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute(\"id\", shapeID);\n gElement.setAttribute(\"data-x\", tx);\n gElement.setAttribute(\"data-y\", ty);\n codeElement.setAttribute(\"id\", `${shapeID}-code`);\n gElement.appendChild(codeElement);\n svg.appendChild(gElement);\n\n // Apply syntax highlighting and add content\n if (textContent.trim()) {\n const lang = getCodeLanguage();\n const highlighted = applySyntaxHighlightingToSVG(textContent, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(gElement, codeElement);\n }\n\n // Create CodeShape wrapper\n const codeShape = wrapCodeElement(gElement);\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(codeShape);\n }\n\n // Push undo action for mode conversion\n pushModeConvertAction(oldTextShape, textGroupElement, 'text', codeShape, gElement, 'code');\n\n // Select the new code block\n selectCodeBlock(gElement);\n}\n\nfunction convertCodeToText(codeGroupElement) {\n const codeElement = codeGroupElement.querySelector('text');\n if (!codeElement) return;\n\n // Get text content\n const textContent = extractTextFromCodeElement(codeElement);\n\n // Get position from transform\n const currentTransform = codeGroupElement.transform.baseVal.consolidate();\n const tx = currentTransform ? currentTransform.matrix.e : 0;\n const ty = currentTransform ? currentTransform.matrix.f : 0;\n const fontSize = codeElement.getAttribute('font-size') || \"30px\";\n const color = codeElement.getAttribute('fill') || \"#fff\";\n\n // Find and remove old CodeShape from shapes array\n let oldCodeShape = null;\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n oldCodeShape = shapes.find(s => s.shapeName === 'code' && s.group === codeGroupElement);\n if (oldCodeShape) {\n const idx = shapes.indexOf(oldCodeShape);\n if (idx !== -1) shapes.splice(idx, 1);\n }\n }\n\n // Deselect current code block\n deselectCodeBlock();\n\n // Remove old code group from SVG\n if (codeGroupElement.parentNode) {\n codeGroupElement.parentNode.removeChild(codeGroupElement);\n }\n\n // Create new text shape\n const gElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n gElement.setAttribute(\"data-type\", \"text-group\");\n gElement.setAttribute(\"transform\", `translate(${tx}, ${ty})`);\n\n const textElement = document.createElementNS(\"http://www.w3.org/2000/svg\", \"text\");\n textElement.setAttribute(\"x\", 0);\n textElement.setAttribute(\"y\", 0);\n textElement.setAttribute(\"fill\", color);\n textElement.setAttribute(\"font-size\", fontSize);\n textElement.setAttribute(\"font-family\", textFont);\n textElement.setAttribute(\"text-anchor\", \"start\");\n textElement.setAttribute(\"cursor\", \"default\");\n textElement.setAttribute(\"white-space\", \"pre\");\n textElement.setAttribute(\"dominant-baseline\", \"hanging\");\n textElement.setAttribute(\"data-type\", \"text\");\n\n const shapeID = `text-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`;\n gElement.setAttribute(\"id\", shapeID);\n gElement.setAttribute(\"data-x\", tx);\n gElement.setAttribute(\"data-y\", ty);\n textElement.setAttribute(\"id\", `${shapeID}-text`);\n gElement.appendChild(textElement);\n svg.appendChild(gElement);\n\n // Add text content as tspans\n if (textContent.trim()) {\n const lines = textContent.split(\"\\n\");\n const x = textElement.getAttribute(\"x\") || 0;\n lines.forEach((line, index) => {\n const tspan = document.createElementNS(\"http://www.w3.org/2000/svg\", \"tspan\");\n tspan.setAttribute(\"x\", x);\n tspan.setAttribute(\"dy\", index === 0 ? \"0\" : \"1.2em\");\n tspan.textContent = line || \" \";\n textElement.appendChild(tspan);\n });\n }\n\n // Create TextShape wrapper\n const textShape = wrapTextElement(gElement);\n if (typeof shapes !== 'undefined' && Array.isArray(shapes)) {\n shapes.push(textShape);\n }\n\n // Push undo action for mode conversion\n pushModeConvertAction(oldCodeShape, codeGroupElement, 'code', textShape, gElement, 'text');\n\n // Select the new text shape\n selectElement(gElement);\n}\n\nfunction pushModeConvertAction(oldShape, oldElement, oldType, newShape, newElement, newType) {\n pushCreateAction({\n type: 'modeConvert',\n oldShape: oldShape,\n oldElement: oldElement,\n oldType: oldType,\n newShape: newShape,\n newElement: newElement,\n newType: newType,\n element: newShape,\n shapeName: newType\n });\n}\n\n// Update toggle state when a shape is selected\nfunction updateCodeToggleForShape(shapeName) {\n const isCode = shapeName === 'code';\n textCodeOptions.forEach(el => el.classList.remove(\"selected\"));\n textCodeOptions.forEach(el => {\n if ((el.getAttribute(\"data-id\") === \"true\") === isCode) {\n el.classList.add(\"selected\");\n }\n });\n if (languageSelector) {\n languageSelector.classList.toggle(\"hidden\", !isCode);\n }\n // Update the language dropdown to match the selected code block\n if (isCode && codeLanguageSelect) {\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeEl = selectedCode.querySelector('text');\n const lang = codeEl?.getAttribute(\"data-language\") || \"auto\";\n codeLanguageSelect.value = lang;\n }\n }\n}\n\n// React sidebar bridge \u2014 update currently selected text/code shape\nwindow.updateSelectedTextStyle = function(changes) {\n const el = selectedElement || (window.currentShape && window.currentShape.shapeName === 'text' ? window.currentShape.group : null);\n if (!el) return;\n const textElement = el.querySelector('text');\n if (!textElement) return;\n\n if (changes.color) {\n textElement.setAttribute('fill', changes.color);\n textColor = changes.color;\n }\n if (changes.font) {\n textElement.setAttribute('font-family', changes.font);\n textFont = changes.font;\n }\n if (changes.fontSize) {\n textElement.setAttribute('font-size', changes.fontSize);\n textSize = changes.fontSize;\n }\n};\n\n// Expose select/deselect for external callers (Selection.js, TextShape.selectShape)\nwindow.__deselectTextElement = deselectElement;\nwindow.__selectTextElement = selectElement;\n\n// React sidebar bridge \u2014 text \u2194 code conversion\nwindow.__convertTextToCode = function() {\n if (selectedElement && selectedElement.getAttribute('data-type') === 'text-group') {\n convertTextToCode(selectedElement);\n }\n};\nwindow.__convertCodeToText = function() {\n // Try codeTool's selectedCodeBlock first, then check textTool's selectedElement\n let codeBlock = getSelectedCodeBlock();\n if (!codeBlock && selectedElement && selectedElement.getAttribute('data-type') === 'code-group') {\n codeBlock = selectedElement;\n }\n if (codeBlock) {\n convertCodeToText(codeBlock);\n }\n};\nwindow.__setCodeLanguage = function(lang) {\n setCodeLanguage(lang);\n // Re-highlight selected code block with new language\n const selectedCode = getSelectedCodeBlock();\n if (selectedCode) {\n const codeElement = selectedCode.querySelector('text');\n if (codeElement) {\n codeElement.setAttribute('data-language', lang);\n const textContent = extractTextFromCodeElement(codeElement);\n // Clear and re-highlight\n while (codeElement.firstChild) codeElement.removeChild(codeElement.firstChild);\n const highlighted = applySyntaxHighlightingToSVG(textContent, lang);\n createHighlightedSVGText(highlighted, codeElement);\n updateCodeBackground(selectedCode, codeElement);\n }\n }\n};\n\nexport { handleTextMouseDown, handleTextMouseMove, handleTextMouseUp, updateCodeToggleForShape, deselectElement as deselectTextElement, enterEditMode };\n"],
5
- "mappings": "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",
6
- "names": []
7
- }