@node-edit-utils/core 2.2.3 → 2.2.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.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 2.2.3
4
+ * @version 2.2.5
5
5
  */
6
6
  'use strict';
7
7
 
@@ -633,6 +633,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
633
633
  const nodeProvider = element;
634
634
  let resizeObserver = null;
635
635
  let mutationObserver = null;
636
+ let parentMutationObserver = null;
636
637
  let selectedNode = null;
637
638
  const text = nodeText(canvasName);
638
639
  // Combined throttled function for refresh + visibility update
@@ -650,6 +651,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
650
651
  selectedNode = null;
651
652
  resizeObserver?.disconnect();
652
653
  mutationObserver?.disconnect();
654
+ parentMutationObserver?.disconnect();
653
655
  }
654
656
  }
655
657
  };
@@ -665,9 +667,26 @@ const createNodeTools = (element, canvasName = "canvas") => {
665
667
  }
666
668
  resizeObserver?.disconnect();
667
669
  mutationObserver?.disconnect();
670
+ parentMutationObserver?.disconnect();
668
671
  if (node && nodeProvider) {
669
672
  text.enableEditMode(node, nodeProvider);
673
+ // Check if node is still in DOM and handle cleanup if removed
674
+ const checkNodeExists = () => {
675
+ if (!document.contains(node)) {
676
+ // Node was removed from DOM, clear highlight and cleanup
677
+ clearHighlightFrame();
678
+ selectedNode = null;
679
+ resizeObserver?.disconnect();
680
+ mutationObserver?.disconnect();
681
+ parentMutationObserver?.disconnect();
682
+ sendPostMessage("selectedNodeChanged", null);
683
+ return;
684
+ }
685
+ };
670
686
  mutationObserver = new MutationObserver(() => {
687
+ checkNodeExists();
688
+ if (!document.contains(node))
689
+ return; // Exit early if node was removed
671
690
  // throttledRefreshAndVisibility(node, nodeProvider);
672
691
  console.log("mutationObserver", node);
673
692
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -676,8 +695,35 @@ const createNodeTools = (element, canvasName = "canvas") => {
676
695
  mutationObserver.observe(node, {
677
696
  attributes: true,
678
697
  characterData: true,
698
+ childList: true,
699
+ subtree: true,
679
700
  });
701
+ // Also observe parent node to catch when this node is removed
702
+ const parentNode = node.parentElement;
703
+ if (parentNode) {
704
+ parentMutationObserver = new MutationObserver((mutations) => {
705
+ // Check if the selected node was removed
706
+ for (const mutation of mutations) {
707
+ if (mutation.type === "childList") {
708
+ for (const removedNode of Array.from(mutation.removedNodes)) {
709
+ // Check if the removed node is the selected node, or if the selected node is contained within the removed subtree
710
+ if (removedNode === node || (removedNode instanceof Node && removedNode.contains(node))) {
711
+ checkNodeExists();
712
+ return;
713
+ }
714
+ }
715
+ }
716
+ }
717
+ });
718
+ parentMutationObserver.observe(parentNode, {
719
+ childList: true,
720
+ subtree: false, // Only direct children to avoid too many callbacks
721
+ });
722
+ }
680
723
  resizeObserver = connectResizeObserver(node, () => {
724
+ checkNodeExists();
725
+ if (!document.contains(node))
726
+ return; // Exit early if node was removed
681
727
  // throttledRefreshAndVisibility(node, nodeProvider);
682
728
  console.log("resizeObserver", node);
683
729
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -697,6 +743,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
697
743
  removeListeners();
698
744
  resizeObserver?.disconnect();
699
745
  mutationObserver?.disconnect();
746
+ parentMutationObserver?.disconnect();
700
747
  text.blurEditMode();
701
748
  throttledRefreshAndVisibility.cleanup();
702
749
  };
@@ -705,8 +752,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
705
752
  getSelectedNode: () => selectedNode,
706
753
  refreshHighlightFrame: () => {
707
754
  if (selectedNode && nodeProvider) {
708
- // Call directly (not throttled) since this is typically called from already-throttled contexts
709
- // to avoid double RAF
710
755
  refreshHighlightFrame(selectedNode, nodeProvider, canvasName);
711
756
  updateHighlightFrameVisibility(selectedNode);
712
757
  }
@@ -716,6 +761,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
716
761
  selectedNode = null;
717
762
  resizeObserver?.disconnect();
718
763
  mutationObserver?.disconnect();
764
+ parentMutationObserver?.disconnect();
719
765
  },
720
766
  getEditableNode: () => text.getEditableNode(),
721
767
  cleanup,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 2.2.3
4
+ * @version 2.2.5
5
5
  */
6
6
  const getCanvasWindowValue = (path, canvasName = "canvas") => {
7
7
  // biome-ignore lint/suspicious/noExplicitAny: global window extension
@@ -631,6 +631,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
631
631
  const nodeProvider = element;
632
632
  let resizeObserver = null;
633
633
  let mutationObserver = null;
634
+ let parentMutationObserver = null;
634
635
  let selectedNode = null;
635
636
  const text = nodeText(canvasName);
636
637
  // Combined throttled function for refresh + visibility update
@@ -648,6 +649,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
648
649
  selectedNode = null;
649
650
  resizeObserver?.disconnect();
650
651
  mutationObserver?.disconnect();
652
+ parentMutationObserver?.disconnect();
651
653
  }
652
654
  }
653
655
  };
@@ -663,9 +665,26 @@ const createNodeTools = (element, canvasName = "canvas") => {
663
665
  }
664
666
  resizeObserver?.disconnect();
665
667
  mutationObserver?.disconnect();
668
+ parentMutationObserver?.disconnect();
666
669
  if (node && nodeProvider) {
667
670
  text.enableEditMode(node, nodeProvider);
671
+ // Check if node is still in DOM and handle cleanup if removed
672
+ const checkNodeExists = () => {
673
+ if (!document.contains(node)) {
674
+ // Node was removed from DOM, clear highlight and cleanup
675
+ clearHighlightFrame();
676
+ selectedNode = null;
677
+ resizeObserver?.disconnect();
678
+ mutationObserver?.disconnect();
679
+ parentMutationObserver?.disconnect();
680
+ sendPostMessage("selectedNodeChanged", null);
681
+ return;
682
+ }
683
+ };
668
684
  mutationObserver = new MutationObserver(() => {
685
+ checkNodeExists();
686
+ if (!document.contains(node))
687
+ return; // Exit early if node was removed
669
688
  // throttledRefreshAndVisibility(node, nodeProvider);
670
689
  console.log("mutationObserver", node);
671
690
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -674,8 +693,35 @@ const createNodeTools = (element, canvasName = "canvas") => {
674
693
  mutationObserver.observe(node, {
675
694
  attributes: true,
676
695
  characterData: true,
696
+ childList: true,
697
+ subtree: true,
677
698
  });
699
+ // Also observe parent node to catch when this node is removed
700
+ const parentNode = node.parentElement;
701
+ if (parentNode) {
702
+ parentMutationObserver = new MutationObserver((mutations) => {
703
+ // Check if the selected node was removed
704
+ for (const mutation of mutations) {
705
+ if (mutation.type === "childList") {
706
+ for (const removedNode of Array.from(mutation.removedNodes)) {
707
+ // Check if the removed node is the selected node, or if the selected node is contained within the removed subtree
708
+ if (removedNode === node || (removedNode instanceof Node && removedNode.contains(node))) {
709
+ checkNodeExists();
710
+ return;
711
+ }
712
+ }
713
+ }
714
+ }
715
+ });
716
+ parentMutationObserver.observe(parentNode, {
717
+ childList: true,
718
+ subtree: false, // Only direct children to avoid too many callbacks
719
+ });
720
+ }
678
721
  resizeObserver = connectResizeObserver(node, () => {
722
+ checkNodeExists();
723
+ if (!document.contains(node))
724
+ return; // Exit early if node was removed
679
725
  // throttledRefreshAndVisibility(node, nodeProvider);
680
726
  console.log("resizeObserver", node);
681
727
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -695,6 +741,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
695
741
  removeListeners();
696
742
  resizeObserver?.disconnect();
697
743
  mutationObserver?.disconnect();
744
+ parentMutationObserver?.disconnect();
698
745
  text.blurEditMode();
699
746
  throttledRefreshAndVisibility.cleanup();
700
747
  };
@@ -703,8 +750,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
703
750
  getSelectedNode: () => selectedNode,
704
751
  refreshHighlightFrame: () => {
705
752
  if (selectedNode && nodeProvider) {
706
- // Call directly (not throttled) since this is typically called from already-throttled contexts
707
- // to avoid double RAF
708
753
  refreshHighlightFrame(selectedNode, nodeProvider, canvasName);
709
754
  updateHighlightFrameVisibility(selectedNode);
710
755
  }
@@ -714,6 +759,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
714
759
  selectedNode = null;
715
760
  resizeObserver?.disconnect();
716
761
  mutationObserver?.disconnect();
762
+ parentMutationObserver?.disconnect();
717
763
  },
718
764
  getEditableNode: () => text.getEditableNode(),
719
765
  cleanup,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 2.2.3
4
+ * @version 2.2.5
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
@@ -637,6 +637,7 @@
637
637
  const nodeProvider = element;
638
638
  let resizeObserver = null;
639
639
  let mutationObserver = null;
640
+ let parentMutationObserver = null;
640
641
  let selectedNode = null;
641
642
  const text = nodeText(canvasName);
642
643
  // Combined throttled function for refresh + visibility update
@@ -654,6 +655,7 @@
654
655
  selectedNode = null;
655
656
  resizeObserver?.disconnect();
656
657
  mutationObserver?.disconnect();
658
+ parentMutationObserver?.disconnect();
657
659
  }
658
660
  }
659
661
  };
@@ -669,9 +671,26 @@
669
671
  }
670
672
  resizeObserver?.disconnect();
671
673
  mutationObserver?.disconnect();
674
+ parentMutationObserver?.disconnect();
672
675
  if (node && nodeProvider) {
673
676
  text.enableEditMode(node, nodeProvider);
677
+ // Check if node is still in DOM and handle cleanup if removed
678
+ const checkNodeExists = () => {
679
+ if (!document.contains(node)) {
680
+ // Node was removed from DOM, clear highlight and cleanup
681
+ clearHighlightFrame();
682
+ selectedNode = null;
683
+ resizeObserver?.disconnect();
684
+ mutationObserver?.disconnect();
685
+ parentMutationObserver?.disconnect();
686
+ sendPostMessage("selectedNodeChanged", null);
687
+ return;
688
+ }
689
+ };
674
690
  mutationObserver = new MutationObserver(() => {
691
+ checkNodeExists();
692
+ if (!document.contains(node))
693
+ return; // Exit early if node was removed
675
694
  // throttledRefreshAndVisibility(node, nodeProvider);
676
695
  console.log("mutationObserver", node);
677
696
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -680,8 +699,35 @@
680
699
  mutationObserver.observe(node, {
681
700
  attributes: true,
682
701
  characterData: true,
702
+ childList: true,
703
+ subtree: true,
683
704
  });
705
+ // Also observe parent node to catch when this node is removed
706
+ const parentNode = node.parentElement;
707
+ if (parentNode) {
708
+ parentMutationObserver = new MutationObserver((mutations) => {
709
+ // Check if the selected node was removed
710
+ for (const mutation of mutations) {
711
+ if (mutation.type === "childList") {
712
+ for (const removedNode of Array.from(mutation.removedNodes)) {
713
+ // Check if the removed node is the selected node, or if the selected node is contained within the removed subtree
714
+ if (removedNode === node || (removedNode instanceof Node && removedNode.contains(node))) {
715
+ checkNodeExists();
716
+ return;
717
+ }
718
+ }
719
+ }
720
+ }
721
+ });
722
+ parentMutationObserver.observe(parentNode, {
723
+ childList: true,
724
+ subtree: false, // Only direct children to avoid too many callbacks
725
+ });
726
+ }
684
727
  resizeObserver = connectResizeObserver(node, () => {
728
+ checkNodeExists();
729
+ if (!document.contains(node))
730
+ return; // Exit early if node was removed
685
731
  // throttledRefreshAndVisibility(node, nodeProvider);
686
732
  console.log("resizeObserver", node);
687
733
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -701,6 +747,7 @@
701
747
  removeListeners();
702
748
  resizeObserver?.disconnect();
703
749
  mutationObserver?.disconnect();
750
+ parentMutationObserver?.disconnect();
704
751
  text.blurEditMode();
705
752
  throttledRefreshAndVisibility.cleanup();
706
753
  };
@@ -709,8 +756,6 @@
709
756
  getSelectedNode: () => selectedNode,
710
757
  refreshHighlightFrame: () => {
711
758
  if (selectedNode && nodeProvider) {
712
- // Call directly (not throttled) since this is typically called from already-throttled contexts
713
- // to avoid double RAF
714
759
  refreshHighlightFrame(selectedNode, nodeProvider, canvasName);
715
760
  updateHighlightFrameVisibility(selectedNode);
716
761
  }
@@ -720,6 +765,7 @@
720
765
  selectedNode = null;
721
766
  resizeObserver?.disconnect();
722
767
  mutationObserver?.disconnect();
768
+ parentMutationObserver?.disconnect();
723
769
  },
724
770
  getEditableNode: () => text.getEditableNode(),
725
771
  cleanup,
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";const t=(e,t="canvas")=>{const n=window[t];return e.reduce((e,t)=>e?.[t],n)};function n(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}const o=()=>{const e=document.body.querySelector(".highlight-frame-overlay");e&&e.remove();const t=document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove()},r=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let s=[],i=0;const a=(e,t)=>{let n=null;const o=e.clientX,a=e.clientY,l=e.metaKey||e.ctrlKey,d=((e,t)=>{const n=document.elementsFromPoint(e,t);return Array.from(n).reduce((e,t)=>e.found?e:"node-provider"===t.getAttribute("data-role")?(e.found=!0,e):(e.elements.push(t),e),{elements:[],found:!1}).elements})(o,a).filter(e=>!r.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none")&&!(e=>{let t=e.parentElement;for(;t;){if("true"===t.getAttribute("data-instance"))return!0;if("node-provider"===t.getAttribute("data-role"))break;t=t.parentElement}return!1})(e));if(t&&d.includes(t))return t;if(l)return s=[],n=d[0],n;var c,u;u=d,(c=s).length===u.length&&c.every((e,t)=>e===u[t])?i<=d.length&&i++:i=0;return n=d[d.length-1-i],s=d,n},l=(e,t,n,r)=>{const s=e=>{((e,t)=>{if("application"===e.data.source&&"selectedNodeChanged"===e.data.action){const o=e.data.data,r=document.querySelector(`[data-node-id="${o}"]`);if(n=r,n?.classList.contains("select-none"))return void t?.(null);r&&t?.(r)}var n})(e,t)},i=n=>{((e,t,n,r)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return o(),void r(null);r(a(e,n))})(n,e,r(),t)},l=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",s),document.addEventListener("click",i),document.addEventListener("keydown",l),()=>{window.removeEventListener("message",s),document.removeEventListener("click",i),document.removeEventListener("keydown",l)}},d=e=>"true"===e.getAttribute("data-instance"),c=(e,t,n,o,r=!1)=>{const s=document.createElementNS("http://www.w3.org/2000/svg","rect");return s.setAttribute("x",(t-3).toString()),s.setAttribute("y",(n-3).toString()),s.setAttribute("width",6..toString()),s.setAttribute("height",6..toString()),s.setAttribute("vector-effect","non-scaling-stroke"),s.classList.add("highlight-frame-handle",o),r&&s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"),e.appendChild(s),s};function u(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const m=(e,t=!1)=>{const{top:n,left:o,width:r,height:s}=u(e),i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.classList.add("highlight-frame-overlay"),t&&i.classList.add("is-instance"),i.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),i.style.position="fixed",i.style.top="0",i.style.left="0",i.style.width="100vw",i.style.height="100vh",i.style.pointerEvents="none",i.style.zIndex="5000";const a=document.documentElement.clientWidth||window.innerWidth,l=document.documentElement.clientHeight||window.innerHeight;i.setAttribute("width",a.toString()),i.setAttribute("height",l.toString());const d=document.createElementNS("http://www.w3.org/2000/svg","g");d.classList.add("highlight-frame-group"),d.setAttribute("transform",`translate(${o}, ${n})`);const m=document.createElementNS("http://www.w3.org/2000/svg","rect");return m.setAttribute("x","0"),m.setAttribute("y","0"),m.setAttribute("width",r.toString()),m.setAttribute("height",s.toString()),m.setAttribute("vector-effect","non-scaling-stroke"),m.classList.add("highlight-frame-rect"),t&&m.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"),d.appendChild(m),((e,t,n,o=!1)=>{c(e,0,0,"handle-top-left",o),c(e,t,0,"handle-top-right",o),c(e,t,n,"handle-bottom-right",o),c(e,0,n,"handle-bottom-left",o)})(d,r,s,t),i.appendChild(d),document.body.appendChild(i),i},h=(e,t,n=!1)=>{const o=document.createElement("div");o.className="node-tools",n&&o.classList.add("is-instance"),t.appendChild(o),((e,t)=>{const n=document.createElement("div");n.className="tag-label",n.textContent=e.tagName.toLowerCase(),t.appendChild(n)})(e,o)};function g(){return document.body.querySelector(".highlight-frame-overlay")}const p=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",b=(e,n,o="canvas")=>{const r=g();if(!r)return;const s=d(e),i=document.documentElement.clientWidth||window.innerWidth,a=document.documentElement.clientHeight||window.innerHeight;r.setAttribute("width",i.toString()),r.setAttribute("height",a.toString()),s?r.classList.add("is-instance"):r.classList.remove("is-instance");const l=r.querySelector(".highlight-frame-group");if(!l)return;const c=l.querySelector("rect");if(!c)return;s?c.setAttribute("stroke",p()):c.removeAttribute("stroke");const m=document.body.querySelector(".highlight-frame-tools-wrapper"),h=m?.querySelector(".node-tools"),b=t(["zoom","current"],o)??1,v=u(e),{top:y,left:f,width:w,height:E}=v,S=y+E;m&&(s?m.classList.add("is-instance"):m.classList.remove("is-instance")),h&&(s?h.classList.add("is-instance"):h.classList.remove("is-instance")),l.setAttribute("transform",`translate(${f}, ${y})`),c.setAttribute("width",w.toString()),c.setAttribute("height",E.toString());const A=l.querySelector(".handle-top-left"),L=l.querySelector(".handle-top-right"),x=l.querySelector(".handle-bottom-right"),k=l.querySelector(".handle-bottom-left");[A,L,x,k].forEach(e=>{e&&(s?e.setAttribute("stroke",p()):e.removeAttribute("stroke"))}),A&&(A.setAttribute("x",(-3).toString()),A.setAttribute("y",(-3).toString())),L&&(L.setAttribute("x",(w-3).toString()),L.setAttribute("y",(-3).toString())),x&&(x.setAttribute("x",(w-3).toString()),x.setAttribute("y",(E-3).toString())),k&&(k.setAttribute("x",(-3).toString()),k.setAttribute("y",(E-3).toString())),m&&(m.style.left=`${f}px`,m.style.top=`${S}px`),b<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},v=e=>{const t=g();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const o=document.body.querySelector(".highlight-frame-tools-wrapper");o&&(o.style.display=n)},y=e=>{const t=e=>{"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),(()=>{const e=window.getSelection();if(e&&e.rangeCount>0){const t=e.getRangeAt(0);t.deleteContents();const n=document.createElement("br");t.insertNode(n),t.setStartAfter(n),t.setEndAfter(n),e.removeAllRanges(),e.addRange(t)}})())};return e.addEventListener("keydown",t),()=>{e.removeEventListener("keydown",t)}},f=(e,t,n="canvas")=>{const o=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,()=>{b(e,t,n)});return()=>o.disconnect()},w=(e="canvas")=>{let n=null,o=!1,r=null;const s=()=>{if(o||!n)return;o=!0;var s;(s=n).contentEditable="false",s.classList.remove("is-editable"),s.style.outline="none",((e="canvas")=>{const n=t(["keyboard","disableTextEditMode"],e);n?.()})(e),r?.(),n=null,o=!1};return{enableEditMode:(o,i)=>{if(n===o)return;n&&n!==o&&s();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(o);a&&(n=o,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(o),((e="canvas")=>{const n=t(["keyboard","enableTextEditMode"],e);n?.()})(e),r=((e,t,n,o="canvas")=>{if(!t)return()=>{};e.addEventListener("blur",n);const r=y(e),s=f(e,t,o);return()=>{e.removeEventListener("blur",n),r(),s?.()}})(o,i,s,e))},blurEditMode:s,getEditableNode:()=>n,isEditing:()=>null!==n}},E=320,S=1680,A=[{name:"Mobile",rawValue:390,value:"320px"},{name:"Tablet Portrait",rawValue:768,value:"768px"},{name:"Tablet Landscape",rawValue:1024,value:"1024px"},{name:"Notebook",rawValue:1280,value:"1280px"},{name:"Desktop",rawValue:1680,value:"1680px"}],L=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(E,Math.min(S,n))})(n,(e.clientX-t)/o);return r},x=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<A.length&&(A[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(e="canvas"){const n=document.querySelector(".transform-layer");if(!n)return{disconnect:()=>{}};const o=new MutationObserver(()=>{((e="canvas")=>{const n=t(["zoom","current"],e)??1;document.body.style.setProperty("--zoom",n.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/n).toFixed(3)),document.body.dataset.zoom=n.toFixed(5),document.body.dataset.strokeWidth=(2/n).toFixed(3)})(e);const n=window.nodeTools;n?.refreshHighlightFrame&&n.refreshHighlightFrame()});return o.observe(n,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.disconnect()}}},e.createNodeTools=(e,t="canvas")=>{const r=e;let s=null,i=null,a=null;const c=w(t),p=n((e,n)=>{b(e,n,t),v(e)}),y=e=>{if(a!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}var n,o;s?.disconnect(),i?.disconnect(),e&&r&&(c.enableEditMode(e,r),i=new MutationObserver(()=>{console.log("mutationObserver",e),b(e,r,t),v(e)}),i.observe(e,{attributes:!0,characterData:!0}),s=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{console.log("resizeObserver",e),b(e,r,t),v(e)})),a=e,n="selectedNodeChanged",o=e?.getAttribute("data-node-id")??null,window.parent.postMessage({source:"node-edit-utils",action:n,data:o,timestamp:Date.now()},"*"),(e=>{if(!e)return;const t=g(),n=document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),n&&n.remove();const o=d(e),r=m(e,o);"true"===e.contentEditable&&r.classList.add("is-editable");const{left:s,top:i,height:a}=u(e),l=i+a,c=document.createElement("div");c.classList.add("highlight-frame-tools-wrapper"),o&&c.classList.add("is-instance"),c.style.position="fixed",c.style.left=`${s}px`,c.style.top=`${l}px`,c.style.transform="translateX(-50%)",c.style.transformOrigin="center",c.style.pointerEvents="none",c.style.zIndex="5000",h(e,c,o),document.body.appendChild(c)})(e),e&&r&&v(e)}},f=l(r,y,()=>{c.isEditing()&&c.blurEditMode(),a&&r&&(o(),a=null,s?.disconnect(),i?.disconnect())},c.getEditableNode),E={selectNode:y,getSelectedNode:()=>a,refreshHighlightFrame:()=>{a&&r&&(b(a,r,t),v(a))},clearSelectedNode:()=>{o(),a=null,s?.disconnect(),i?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{f(),s?.disconnect(),i?.disconnect(),c.blurEditMode(),p.cleanup()}};var S,A;return S="nodeTools",A=E,"undefined"!=typeof window&&(window[S]=A),E},e.createViewport=e=>{const t=document.querySelector(".canvas-container"),o=e.querySelector(".resize-handle");o&&o.remove();const r=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e);e.style.setProperty("--container-width","400px"),((e,t,n)=>{const o=document.createElement("div");o.className="resize-presets",A.forEach(e=>{const r=document.createElement("button");r.textContent=e.name,r.className="resize-preset-button",r.addEventListener("click",()=>{n(t,e.rawValue)}),o.appendChild(r)}),e.appendChild(o)})(r,e,x);let s=!1,i=0,a=0;const l=n(n=>{if(!s)return;t&&(t.style.cursor="ew-resize");const o=L(n,i,a);x(e,o)}),d=((e,t,n,o,r)=>(e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),window.removeEventListener("blur",r)}))(r,t=>{t.preventDefault(),t.stopPropagation(),s=!0,i=t.clientX,a=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{x(e,t)},cleanup:()=>{s=!1,l?.cleanup(),d(),r.remove()}}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";const t=(e,t="canvas")=>{const n=window[t];return e.reduce((e,t)=>e?.[t],n)};function n(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}function o(e,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const r=()=>{const e=document.body.querySelector(".highlight-frame-overlay");e&&e.remove();const t=document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove()},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let i=[],a=0;const l=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,l=e.metaKey||e.ctrlKey,c=((e,t)=>{const n=document.elementsFromPoint(e,t);return Array.from(n).reduce((e,t)=>e.found?e:"node-provider"===t.getAttribute("data-role")?(e.found=!0,e):(e.elements.push(t),e),{elements:[],found:!1}).elements})(o,r).filter(e=>!s.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none")&&!(e=>{let t=e.parentElement;for(;t;){if("true"===t.getAttribute("data-instance"))return!0;if("node-provider"===t.getAttribute("data-role"))break;t=t.parentElement}return!1})(e));if(t&&c.includes(t))return t;if(l)return i=[],n=c[0],n;var d,u;u=c,(d=i).length===u.length&&d.every((e,t)=>e===u[t])?a<=c.length&&a++:a=0;return n=c[c.length-1-a],i=c,n},c=(e,t,n,o)=>{const s=e=>{((e,t)=>{if("application"===e.data.source&&"selectedNodeChanged"===e.data.action){const o=e.data.data,r=document.querySelector(`[data-node-id="${o}"]`);if(n=r,n?.classList.contains("select-none"))return void t?.(null);r&&t?.(r)}var n})(e,t)},i=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(),void o(null);o(l(e,n))})(n,e,o(),t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",s),document.addEventListener("click",i),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",s),document.removeEventListener("click",i),document.removeEventListener("keydown",a)}},d=e=>"true"===e.getAttribute("data-instance"),u=(e,t,n,o,r=!1)=>{const s=document.createElementNS("http://www.w3.org/2000/svg","rect");return s.setAttribute("x",(t-3).toString()),s.setAttribute("y",(n-3).toString()),s.setAttribute("width",6..toString()),s.setAttribute("height",6..toString()),s.setAttribute("vector-effect","non-scaling-stroke"),s.classList.add("highlight-frame-handle",o),r&&s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"),e.appendChild(s),s};function m(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const h=(e,t=!1)=>{const{top:n,left:o,width:r,height:s}=m(e),i=document.createElementNS("http://www.w3.org/2000/svg","svg");i.classList.add("highlight-frame-overlay"),t&&i.classList.add("is-instance"),i.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),i.style.position="fixed",i.style.top="0",i.style.left="0",i.style.width="100vw",i.style.height="100vh",i.style.pointerEvents="none",i.style.zIndex="5000";const a=document.documentElement.clientWidth||window.innerWidth,l=document.documentElement.clientHeight||window.innerHeight;i.setAttribute("width",a.toString()),i.setAttribute("height",l.toString());const c=document.createElementNS("http://www.w3.org/2000/svg","g");c.classList.add("highlight-frame-group"),c.setAttribute("transform",`translate(${o}, ${n})`);const d=document.createElementNS("http://www.w3.org/2000/svg","rect");return d.setAttribute("x","0"),d.setAttribute("y","0"),d.setAttribute("width",r.toString()),d.setAttribute("height",s.toString()),d.setAttribute("vector-effect","non-scaling-stroke"),d.classList.add("highlight-frame-rect"),t&&d.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"),c.appendChild(d),((e,t,n,o=!1)=>{u(e,0,0,"handle-top-left",o),u(e,t,0,"handle-top-right",o),u(e,t,n,"handle-bottom-right",o),u(e,0,n,"handle-bottom-left",o)})(c,r,s,t),i.appendChild(c),document.body.appendChild(i),i},g=(e,t,n=!1)=>{const o=document.createElement("div");o.className="node-tools",n&&o.classList.add("is-instance"),t.appendChild(o),((e,t)=>{const n=document.createElement("div");n.className="tag-label",n.textContent=e.tagName.toLowerCase(),t.appendChild(n)})(e,o)};function p(){return document.body.querySelector(".highlight-frame-overlay")}const b=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",v=(e,n,o="canvas")=>{const r=p();if(!r)return;const s=d(e),i=document.documentElement.clientWidth||window.innerWidth,a=document.documentElement.clientHeight||window.innerHeight;r.setAttribute("width",i.toString()),r.setAttribute("height",a.toString()),s?r.classList.add("is-instance"):r.classList.remove("is-instance");const l=r.querySelector(".highlight-frame-group");if(!l)return;const c=l.querySelector("rect");if(!c)return;s?c.setAttribute("stroke",b()):c.removeAttribute("stroke");const u=document.body.querySelector(".highlight-frame-tools-wrapper"),h=u?.querySelector(".node-tools"),g=t(["zoom","current"],o)??1,v=m(e),{top:f,left:y,width:w,height:E}=v,L=f+E;u&&(s?u.classList.add("is-instance"):u.classList.remove("is-instance")),h&&(s?h.classList.add("is-instance"):h.classList.remove("is-instance")),l.setAttribute("transform",`translate(${y}, ${f})`),c.setAttribute("width",w.toString()),c.setAttribute("height",E.toString());const A=l.querySelector(".handle-top-left"),S=l.querySelector(".handle-top-right"),x=l.querySelector(".handle-bottom-right"),N=l.querySelector(".handle-bottom-left");[A,S,x,N].forEach(e=>{e&&(s?e.setAttribute("stroke",b()):e.removeAttribute("stroke"))}),A&&(A.setAttribute("x",(-3).toString()),A.setAttribute("y",(-3).toString())),S&&(S.setAttribute("x",(w-3).toString()),S.setAttribute("y",(-3).toString())),x&&(x.setAttribute("x",(w-3).toString()),x.setAttribute("y",(E-3).toString())),N&&(N.setAttribute("x",(-3).toString()),N.setAttribute("y",(E-3).toString())),u&&(u.style.left=`${y}px`,u.style.top=`${L}px`),g<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},f=e=>{const t=p();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const o=document.body.querySelector(".highlight-frame-tools-wrapper");o&&(o.style.display=n)},y=e=>{const t=e=>{"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),(()=>{const e=window.getSelection();if(e&&e.rangeCount>0){const t=e.getRangeAt(0);t.deleteContents();const n=document.createElement("br");t.insertNode(n),t.setStartAfter(n),t.setEndAfter(n),e.removeAllRanges(),e.addRange(t)}})())};return e.addEventListener("keydown",t),()=>{e.removeEventListener("keydown",t)}},w=(e,t,n="canvas")=>{const o=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,()=>{v(e,t,n)});return()=>o.disconnect()},E=(e="canvas")=>{let n=null,o=!1,r=null;const s=()=>{if(o||!n)return;o=!0;var s;(s=n).contentEditable="false",s.classList.remove("is-editable"),s.style.outline="none",((e="canvas")=>{const n=t(["keyboard","disableTextEditMode"],e);n?.()})(e),r?.(),n=null,o=!1};return{enableEditMode:(o,i)=>{if(n===o)return;n&&n!==o&&s();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(o);a&&(n=o,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(o),((e="canvas")=>{const n=t(["keyboard","enableTextEditMode"],e);n?.()})(e),r=((e,t,n,o="canvas")=>{if(!t)return()=>{};e.addEventListener("blur",n);const r=y(e),s=w(e,t,o);return()=>{e.removeEventListener("blur",n),r(),s?.()}})(o,i,s,e))},blurEditMode:s,getEditableNode:()=>n,isEditing:()=>null!==n}},L=320,A=1680,S=[{name:"Mobile",rawValue:390,value:"320px"},{name:"Tablet Portrait",rawValue:768,value:"768px"},{name:"Tablet Landscape",rawValue:1024,value:"1024px"},{name:"Notebook",rawValue:1280,value:"1280px"},{name:"Desktop",rawValue:1680,value:"1680px"}],x=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(L,Math.min(A,n))})(n,(e.clientX-t)/o);return r},N=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<S.length&&(S[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(e="canvas"){const n=document.querySelector(".transform-layer");if(!n)return{disconnect:()=>{}};const o=new MutationObserver(()=>{((e="canvas")=>{const n=t(["zoom","current"],e)??1;document.body.style.setProperty("--zoom",n.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/n).toFixed(3)),document.body.dataset.zoom=n.toFixed(5),document.body.dataset.strokeWidth=(2/n).toFixed(3)})(e);const n=window.nodeTools;n?.refreshHighlightFrame&&n.refreshHighlightFrame()});return o.observe(n,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.disconnect()}}},e.createNodeTools=(e,t="canvas")=>{const s=e;let i=null,a=null,l=null,u=null;const b=E(t),y=n((e,n)=>{v(e,n,t),f(e)}),w=e=>{if(u!==e){if(b.isEditing()){const t=b.getEditableNode();t&&t!==e&&b.blurEditMode()}if(i?.disconnect(),a?.disconnect(),l?.disconnect(),e&&s){b.enableEditMode(e,s);const n=()=>{if(!document.contains(e))return r(),u=null,i?.disconnect(),a?.disconnect(),l?.disconnect(),void o("selectedNodeChanged",null)};a=new MutationObserver(()=>{n(),document.contains(e)&&(console.log("mutationObserver",e),v(e,s,t),f(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(l=new MutationObserver(t=>{for(const o of t)if("childList"===o.type)for(const t of Array.from(o.removedNodes))if(t===e||t instanceof Node&&t.contains(e))return void n()}),l.observe(c,{childList:!0,subtree:!1})),i=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{n(),document.contains(e)&&(console.log("resizeObserver",e),v(e,s,t),f(e))})}u=e,o("selectedNodeChanged",e?.getAttribute("data-node-id")??null),(e=>{if(!e)return;const t=p(),n=document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),n&&n.remove();const o=d(e),r=h(e,o);"true"===e.contentEditable&&r.classList.add("is-editable");const{left:s,top:i,height:a}=m(e),l=i+a,c=document.createElement("div");c.classList.add("highlight-frame-tools-wrapper"),o&&c.classList.add("is-instance"),c.style.position="fixed",c.style.left=`${s}px`,c.style.top=`${l}px`,c.style.transform="translateX(-50%)",c.style.transformOrigin="center",c.style.pointerEvents="none",c.style.zIndex="5000",g(e,c,o),document.body.appendChild(c)})(e),e&&s&&f(e)}},L=c(s,w,()=>{b.isEditing()&&b.blurEditMode(),u&&s&&(r(),u=null,i?.disconnect(),a?.disconnect(),l?.disconnect())},b.getEditableNode),A={selectNode:w,getSelectedNode:()=>u,refreshHighlightFrame:()=>{u&&s&&(v(u,s,t),f(u))},clearSelectedNode:()=>{r(),u=null,i?.disconnect(),a?.disconnect(),l?.disconnect()},getEditableNode:()=>b.getEditableNode(),cleanup:()=>{L(),i?.disconnect(),a?.disconnect(),l?.disconnect(),b.blurEditMode(),y.cleanup()}};var S,x;return S="nodeTools",x=A,"undefined"!=typeof window&&(window[S]=x),A},e.createViewport=e=>{const t=document.querySelector(".canvas-container"),o=e.querySelector(".resize-handle");o&&o.remove();const r=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e);e.style.setProperty("--container-width","400px"),((e,t,n)=>{const o=document.createElement("div");o.className="resize-presets",S.forEach(e=>{const r=document.createElement("button");r.textContent=e.name,r.className="resize-preset-button",r.addEventListener("click",()=>{n(t,e.rawValue)}),o.appendChild(r)}),e.appendChild(o)})(r,e,N);let s=!1,i=0,a=0;const l=n(n=>{if(!s)return;t&&(t.style.cursor="ew-resize");const o=x(n,i,a);N(e,o)}),c=((e,t,n,o,r)=>(e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),window.removeEventListener("blur",r)}))(r,t=>{t.preventDefault(),t.stopPropagation(),s=!0,i=t.clientX,a=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{N(e,t)},cleanup:()=>{s=!1,l?.cleanup(),c(),r.remove()}}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-edit-utils/core",
3
- "version": "2.2.3",
3
+ "version": "2.2.5",
4
4
  "description": "Utilities for editing nodes in a dom tree.",
5
5
  "type": "module",
6
6
  "main": "dist/node-edit-utils.cjs.js",
@@ -15,6 +15,7 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
15
15
 
16
16
  let resizeObserver: ResizeObserver | null = null;
17
17
  let mutationObserver: MutationObserver | null = null;
18
+ let parentMutationObserver: MutationObserver | null = null;
18
19
  let selectedNode: HTMLElement | null = null;
19
20
 
20
21
  const text = nodeText(canvasName);
@@ -37,6 +38,7 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
37
38
 
38
39
  resizeObserver?.disconnect();
39
40
  mutationObserver?.disconnect();
41
+ parentMutationObserver?.disconnect();
40
42
  }
41
43
  }
42
44
  };
@@ -55,11 +57,29 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
55
57
 
56
58
  resizeObserver?.disconnect();
57
59
  mutationObserver?.disconnect();
60
+ parentMutationObserver?.disconnect();
58
61
 
59
62
  if (node && nodeProvider) {
60
63
  text.enableEditMode(node, nodeProvider);
61
64
 
65
+ // Check if node is still in DOM and handle cleanup if removed
66
+ const checkNodeExists = (): void => {
67
+ if (!document.contains(node)) {
68
+ // Node was removed from DOM, clear highlight and cleanup
69
+ clearHighlightFrame();
70
+ selectedNode = null;
71
+ resizeObserver?.disconnect();
72
+ mutationObserver?.disconnect();
73
+ parentMutationObserver?.disconnect();
74
+ sendPostMessage("selectedNodeChanged", null);
75
+ return;
76
+ }
77
+ };
78
+
62
79
  mutationObserver = new MutationObserver(() => {
80
+ checkNodeExists();
81
+ if (!document.contains(node)) return; // Exit early if node was removed
82
+
63
83
  // throttledRefreshAndVisibility(node, nodeProvider);
64
84
  console.log("mutationObserver", node);
65
85
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -69,9 +89,38 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
69
89
  mutationObserver.observe(node, {
70
90
  attributes: true,
71
91
  characterData: true,
92
+ childList: true,
93
+ subtree: true,
72
94
  });
73
95
 
96
+ // Also observe parent node to catch when this node is removed
97
+ const parentNode = node.parentElement;
98
+ if (parentNode) {
99
+ parentMutationObserver = new MutationObserver((mutations) => {
100
+ // Check if the selected node was removed
101
+ for (const mutation of mutations) {
102
+ if (mutation.type === "childList") {
103
+ for (const removedNode of Array.from(mutation.removedNodes)) {
104
+ // Check if the removed node is the selected node, or if the selected node is contained within the removed subtree
105
+ if (removedNode === node || (removedNode instanceof Node && removedNode.contains(node))) {
106
+ checkNodeExists();
107
+ return;
108
+ }
109
+ }
110
+ }
111
+ }
112
+ });
113
+
114
+ parentMutationObserver.observe(parentNode, {
115
+ childList: true,
116
+ subtree: false, // Only direct children to avoid too many callbacks
117
+ });
118
+ }
119
+
74
120
  resizeObserver = connectResizeObserver(node, () => {
121
+ checkNodeExists();
122
+ if (!document.contains(node)) return; // Exit early if node was removed
123
+
75
124
  // throttledRefreshAndVisibility(node, nodeProvider);
76
125
  console.log("resizeObserver", node);
77
126
  refreshHighlightFrame(node, nodeProvider, canvasName);
@@ -95,6 +144,7 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
95
144
  removeListeners();
96
145
  resizeObserver?.disconnect();
97
146
  mutationObserver?.disconnect();
147
+ parentMutationObserver?.disconnect();
98
148
 
99
149
  text.blurEditMode();
100
150
  throttledRefreshAndVisibility.cleanup();
@@ -105,8 +155,6 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
105
155
  getSelectedNode: () => selectedNode,
106
156
  refreshHighlightFrame: () => {
107
157
  if (selectedNode && nodeProvider) {
108
- // Call directly (not throttled) since this is typically called from already-throttled contexts
109
- // to avoid double RAF
110
158
  refreshHighlightFrame(selectedNode, nodeProvider, canvasName);
111
159
  updateHighlightFrameVisibility(selectedNode);
112
160
  }
@@ -116,6 +164,7 @@ export const createNodeTools = (element: HTMLElement | null, canvasName: string
116
164
  selectedNode = null;
117
165
  resizeObserver?.disconnect();
118
166
  mutationObserver?.disconnect();
167
+ parentMutationObserver?.disconnect();
119
168
  },
120
169
  getEditableNode: () => text.getEditableNode(),
121
170
  cleanup,