@node-edit-utils/core 2.2.9 → 2.3.0

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 +1 @@
1
- export declare const handleTextChange: (node: HTMLElement, mutations: MutationRecord[]) => void;
1
+ export declare const handleTextChange: (node: HTMLElement, mutations: MutationRecord[], final?: boolean) => void;
@@ -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.9
4
+ * @version 2.3.0
5
5
  */
6
6
  'use strict';
7
7
 
@@ -645,24 +645,25 @@ const connectMutationObserver = (element, handler) => {
645
645
  return mutationObserver;
646
646
  };
647
647
 
648
- const handleTextChange = (node, mutations) => {
648
+ const handleTextChange = (node, mutations, final = false) => {
649
649
  // Check if any mutation is a text content change
650
650
  const hasTextChange = mutations.some((mutation) => {
651
651
  return (mutation.type === "characterData" ||
652
652
  (mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
653
653
  });
654
- if (!hasTextChange) {
654
+ if (!hasTextChange && !final) {
655
655
  return;
656
656
  }
657
657
  // Get the text content of the node
658
658
  const textContent = node.textContent ?? "";
659
659
  // Get the node ID
660
660
  const nodeId = node.getAttribute("data-node-id");
661
- console.log("textContentChanged", nodeId, textContent);
661
+ console.log("textContentChanged", textContent, final);
662
662
  // Send postMessage with the text change
663
663
  sendPostMessage("textContentChanged", {
664
664
  nodeId,
665
665
  textContent,
666
+ final,
666
667
  });
667
668
  };
668
669
 
@@ -675,7 +676,7 @@ const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
675
676
  if (pendingMutations.length > 0) {
676
677
  const mutationsToProcess = [...pendingMutations];
677
678
  pendingMutations = [];
678
- handleTextChange(node, mutationsToProcess);
679
+ handleTextChange(node, mutationsToProcess, false);
679
680
  }
680
681
  };
681
682
  const scheduleProcess = () => {
@@ -775,6 +776,8 @@ const nodeText = (canvasName = "canvas") => {
775
776
  }
776
777
  blurInProgress = true;
777
778
  const nodeToCleanup = editableNode;
779
+ // Send final textContentChanged message before cleanup
780
+ handleTextChange(nodeToCleanup, [], true);
778
781
  makeNodeNonEditable(nodeToCleanup);
779
782
  disableCanvasTextMode(canvasName);
780
783
  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.9
4
+ * @version 2.3.0
5
5
  */
6
6
  const getCanvasWindowValue = (path, canvasName = "canvas") => {
7
7
  // biome-ignore lint/suspicious/noExplicitAny: global window extension
@@ -643,24 +643,25 @@ const connectMutationObserver = (element, handler) => {
643
643
  return mutationObserver;
644
644
  };
645
645
 
646
- const handleTextChange = (node, mutations) => {
646
+ const handleTextChange = (node, mutations, final = false) => {
647
647
  // Check if any mutation is a text content change
648
648
  const hasTextChange = mutations.some((mutation) => {
649
649
  return (mutation.type === "characterData" ||
650
650
  (mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
651
651
  });
652
- if (!hasTextChange) {
652
+ if (!hasTextChange && !final) {
653
653
  return;
654
654
  }
655
655
  // Get the text content of the node
656
656
  const textContent = node.textContent ?? "";
657
657
  // Get the node ID
658
658
  const nodeId = node.getAttribute("data-node-id");
659
- console.log("textContentChanged", nodeId, textContent);
659
+ console.log("textContentChanged", textContent, final);
660
660
  // Send postMessage with the text change
661
661
  sendPostMessage("textContentChanged", {
662
662
  nodeId,
663
663
  textContent,
664
+ final,
664
665
  });
665
666
  };
666
667
 
@@ -673,7 +674,7 @@ const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
673
674
  if (pendingMutations.length > 0) {
674
675
  const mutationsToProcess = [...pendingMutations];
675
676
  pendingMutations = [];
676
- handleTextChange(node, mutationsToProcess);
677
+ handleTextChange(node, mutationsToProcess, false);
677
678
  }
678
679
  };
679
680
  const scheduleProcess = () => {
@@ -773,6 +774,8 @@ const nodeText = (canvasName = "canvas") => {
773
774
  }
774
775
  blurInProgress = true;
775
776
  const nodeToCleanup = editableNode;
777
+ // Send final textContentChanged message before cleanup
778
+ handleTextChange(nodeToCleanup, [], true);
776
779
  makeNodeNonEditable(nodeToCleanup);
777
780
  disableCanvasTextMode(canvasName);
778
781
  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.9
4
+ * @version 2.3.0
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
@@ -649,24 +649,25 @@
649
649
  return mutationObserver;
650
650
  };
651
651
 
652
- const handleTextChange = (node, mutations) => {
652
+ const handleTextChange = (node, mutations, final = false) => {
653
653
  // Check if any mutation is a text content change
654
654
  const hasTextChange = mutations.some((mutation) => {
655
655
  return (mutation.type === "characterData" ||
656
656
  (mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
657
657
  });
658
- if (!hasTextChange) {
658
+ if (!hasTextChange && !final) {
659
659
  return;
660
660
  }
661
661
  // Get the text content of the node
662
662
  const textContent = node.textContent ?? "";
663
663
  // Get the node ID
664
664
  const nodeId = node.getAttribute("data-node-id");
665
- console.log("textContentChanged", nodeId, textContent);
665
+ console.log("textContentChanged", textContent, final);
666
666
  // Send postMessage with the text change
667
667
  sendPostMessage("textContentChanged", {
668
668
  nodeId,
669
669
  textContent,
670
+ final,
670
671
  });
671
672
  };
672
673
 
@@ -679,7 +680,7 @@
679
680
  if (pendingMutations.length > 0) {
680
681
  const mutationsToProcess = [...pendingMutations];
681
682
  pendingMutations = [];
682
- handleTextChange(node, mutationsToProcess);
683
+ handleTextChange(node, mutationsToProcess, false);
683
684
  }
684
685
  };
685
686
  const scheduleProcess = () => {
@@ -779,6 +780,8 @@
779
780
  }
780
781
  blurInProgress = true;
781
782
  const nodeToCleanup = editableNode;
783
+ // Send final textContentChanged message before cleanup
784
+ handleTextChange(nodeToCleanup, [], true);
782
785
  makeNodeNonEditable(nodeToCleanup);
783
786
  disableCanvasTextMode(canvasName);
784
787
  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,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const o=()=>document.querySelector(".canvas-container"),r=()=>{const e=o()||document.body,t=e.querySelector(".highlight-frame-overlay");t&&t.remove();const n=e.querySelector(".highlight-frame-tools-wrapper");n&&n.remove()},i=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],l=0,d=null;const c=(e,t,n)=>{let o=null;const r=e.clientX,c=e.clientY,u=e.metaKey||e.ctrlKey,m=((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})(r,c).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)),h=n.getEditableNode();if(h&&m.includes(h))return o=h,d=o,o;if(u)return a=[],o=m[0],d&&d===o&&i(o,t,n),d=o,o;var g,p;p=m,(g=a).length===p.length&&g.every((e,t)=>e===p[t])?l<=m.length-2&&l++:l=0;return o=m[m.length-1-l],a=m,d&&d===o&&i(o,t,n),d=o,o},u=(e,t,n,o)=>{const i=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)},s=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(),void o(null);o(c(e,t,n))})(n,e,o,t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",i),document.addEventListener("click",s),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",i),document.removeEventListener("click",s),document.removeEventListener("keydown",a)}},m=e=>"true"===e.getAttribute("data-instance"),h=(e,t,n,o,r=!1,i=!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)"):i&&s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),e.appendChild(s),s};function g(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const p=(e,t=!1,n=!1)=>{const{top:r,left:i,width:s,height:a}=g(e),l=Math.max(s,3),d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.classList.add("highlight-frame-overlay"),t&&d.classList.add("is-instance"),n&&d.classList.add("is-text-edit"),d.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),d.style.position="absolute",d.style.top="0",d.style.left="0",d.style.width="100vw",d.style.height="100vh",d.style.pointerEvents="none",d.style.zIndex="500";const c=document.documentElement.clientWidth||window.innerWidth,u=document.documentElement.clientHeight||window.innerHeight;d.setAttribute("width",c.toString()),d.setAttribute("height",u.toString());const m=document.createElementNS("http://www.w3.org/2000/svg","g");m.classList.add("highlight-frame-group"),m.setAttribute("transform",`translate(${i}, ${r})`);const p=document.createElementNS("http://www.w3.org/2000/svg","rect");p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("width",l.toString()),p.setAttribute("height",a.toString()),p.setAttribute("vector-effect","non-scaling-stroke"),p.classList.add("highlight-frame-rect"),t?p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):n&&p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),m.appendChild(p),((e,t,n,o=!1,r=!1)=>{h(e,0,0,"handle-top-left",o,r),h(e,t,0,"handle-top-right",o,r),h(e,t,n,"handle-bottom-right",o,r),h(e,0,n,"handle-bottom-left",o,r)})(m,l,a,t,n),d.appendChild(m);const b=o();return b?b.appendChild(d):document.body.appendChild(d),d},b={div:"Container",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",h4:"Heading 4",h5:"Heading 5",h6:"Heading 6",p:"Text",li:"List Item",ul:"Unordered List",ol:"Ordered List",img:"Image",a:"Link"},v=(e,t,n=!1,o=!1)=>{const r=document.createElement("div");r.className="node-tools",n&&r.classList.add("is-instance"),o&&r.classList.add("is-text-edit"),t.appendChild(r),((e,t)=>{const n=document.createElement("div");n.className="tag-label";const o=e.getAttribute("data-instance-name"),r=e.tagName.toLowerCase(),i=o||b[r]||r;var s;n.textContent=(s=i)?s.charAt(0).toUpperCase()+s.slice(1):s,t.appendChild(n)})(e,r)};function y(){return(o()||document.body).querySelector(".highlight-frame-overlay")}const f=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",w=()=>getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)",E=(e,n,r="canvas")=>{const i=y();if(!i)return;const s=m(e),a="true"===e.contentEditable,l=document.documentElement.clientWidth||window.innerWidth,d=document.documentElement.clientHeight||window.innerHeight;i.setAttribute("width",l.toString()),i.setAttribute("height",d.toString()),s?i.classList.add("is-instance"):i.classList.remove("is-instance"),a?i.classList.add("is-text-edit"):i.classList.remove("is-text-edit");const c=i.querySelector(".highlight-frame-group");if(!c)return;const u=c.querySelector("rect");if(!u)return;s?u.setAttribute("stroke",f()):a?u.setAttribute("stroke",w()):u.removeAttribute("stroke");const h=(o()||document.body).querySelector(".highlight-frame-tools-wrapper"),p=h?.querySelector(".node-tools"),b=t(["zoom","current"],r)??1,v=g(e),{top:E,left:L,width:A,height:x}=v,S=Math.max(A,3),C=E+x;h&&(s?h.classList.add("is-instance"):h.classList.remove("is-instance"),a?h.classList.add("is-text-edit"):h.classList.remove("is-text-edit")),p&&(s?p.classList.add("is-instance"):p.classList.remove("is-instance"),a?p.classList.add("is-text-edit"):p.classList.remove("is-text-edit")),c.setAttribute("transform",`translate(${L}, ${E})`),u.setAttribute("width",S.toString()),u.setAttribute("height",x.toString());const k=c.querySelector(".handle-top-left"),N=c.querySelector(".handle-top-right"),q=c.querySelector(".handle-bottom-right"),M=c.querySelector(".handle-bottom-left");[k,N,q,M].forEach(e=>{e&&(s?e.setAttribute("stroke",f()):a?e.setAttribute("stroke",w()):e.removeAttribute("stroke"))}),k&&(k.setAttribute("x",(-3).toString()),k.setAttribute("y",(-3).toString())),N&&(N.setAttribute("x",(S-3).toString()),N.setAttribute("y",(-3).toString())),q&&(q.setAttribute("x",(S-3).toString()),q.setAttribute("y",(x-3).toString())),M&&(M.setAttribute("x",(-3).toString()),M.setAttribute("y",(x-3).toString())),h&&(h.style.transform=`translate(${L}px, ${C}px)`),b<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},L=e=>{const t=y();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const r=(o()||document.body).querySelector(".highlight-frame-tools-wrapper");r&&(r.style.display=n)},A=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)}},x=(e,t,o="canvas")=>{let r=[],i=null,s=null;const a=()=>{if(r.length>0){const t=[...r];r=[],((e,t)=>{if(!t.some(e=>"characterData"===e.type||"childList"===e.type&&(e.addedNodes.length>0||e.removedNodes.length>0)))return;const o=e.textContent??"",r=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:r,textContent:o})})(e,t)}},l=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,n=>{r.push(...n),null===i&&(i=requestAnimationFrame(()=>{s=requestAnimationFrame(()=>{a(),i=null,s=null})})),E(e,t,o)});return()=>{l.disconnect(),null!==i&&(cancelAnimationFrame(i),i=null),null!==s&&(cancelAnimationFrame(s),s=null),r=[]}},S=(e="canvas")=>{let n=null,o=!1,r=null;const i=()=>{if(o||!n)return;o=!0;var i;(i=n).contentEditable="false",i.classList.remove("is-editable"),i.style.outline="none",((e="canvas")=>{const n=t(["keyboard","disableTextEditMode"],e);n?.()})(e),r?.(),n=null,o=!1};return{enableEditMode:(o,s)=>{if(n===o)return;n&&n!==o&&i();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=A(e),i=x(e,t,o);return()=>{e.removeEventListener("blur",n),r(),i?.()}})(o,s,i,e))},blurEditMode:i,getEditableNode:()=>n,isEditing:()=>null!==n}};const C=320,k=1680,N=[{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"}],q=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(C,Math.min(k,n))})(n,(e.clientX-t)/o);return r},M=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<N.length&&(N[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 i=e;let s=null,a=null,l=null,d=null;const c=S(t),h=e=>{if(d!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}if(s?.disconnect(),a?.disconnect(),l?.disconnect(),e&&i){const o=()=>{if(!document.contains(e))return r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect(),void n("selectedNodeChanged",null)};a=new MutationObserver(()=>{o(),document.contains(e)&&(E(e,i,t),L(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(l=new MutationObserver(t=>{for(const n of t)if("childList"===n.type)for(const t of Array.from(n.removedNodes))if(t===e||t instanceof Node&&t.contains(e))return void o()}),l.observe(c,{childList:!0,subtree:!1})),s=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{o(),document.contains(e)&&(E(e,i,t),L(e))})}d=e,n("selectedNodeChanged",e?.getAttribute("data-node-id")??null),(e=>{if(!e)return;const t=y(),n=o(),r=n?.querySelector(".highlight-frame-tools-wrapper")||document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),r&&r.remove();const i=m(e),s="true"===e.contentEditable,a=p(e,i,s);"true"===e.contentEditable&&a.classList.add("is-editable");const{left:l,top:d,height:c}=g(e),u=d+c,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),i&&h.classList.add("is-instance"),s&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${l}px, ${u}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",v(e,h,i,s),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&i&&(L(e),L(e))}},b=u(i,h,()=>{c.isEditing()&&c.blurEditMode(),d&&i&&(r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect())},c),f={selectNode:h,getSelectedNode:()=>d,refreshHighlightFrame:()=>{d&&i&&(E(d,i,t),L(d))},clearSelectedNode:()=>{r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{b(),s?.disconnect(),a?.disconnect(),l?.disconnect(),c.blurEditMode(),r(),d=null,n("selectedNodeChanged",null)}};var w,A;return w="nodeTools",A=f,"undefined"!=typeof window&&(window[w]=A),f},e.createViewport=e=>{const t=o(),n=e.querySelector(".resize-handle");n&&n.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",N.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,M);let i=!1,s=0,a=0;const l=function(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}(n=>{if(!i)return;t&&(t.style.cursor="ew-resize");const o=q(n,s,a);M(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(),i=!0,s=t.clientX,a=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),i=!1},()=>{i=!1});return{setWidth:t=>{M(e,t)},cleanup:()=>{i=!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,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const o=()=>document.querySelector(".canvas-container"),r=()=>{const e=o()||document.body,t=e.querySelector(".highlight-frame-overlay");t&&t.remove();const n=e.querySelector(".highlight-frame-tools-wrapper");n&&n.remove()},i=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],l=0,d=null;const c=(e,t,n)=>{let o=null;const r=e.clientX,c=e.clientY,u=e.metaKey||e.ctrlKey,m=((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})(r,c).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)),h=n.getEditableNode();if(h&&m.includes(h))return o=h,d=o,o;if(u)return a=[],o=m[0],d&&d===o&&i(o,t,n),d=o,o;var g,p;p=m,(g=a).length===p.length&&g.every((e,t)=>e===p[t])?l<=m.length-2&&l++:l=0;return o=m[m.length-1-l],a=m,d&&d===o&&i(o,t,n),d=o,o},u=(e,t,n,o)=>{const i=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)},s=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(),void o(null);o(c(e,t,n))})(n,e,o,t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",i),document.addEventListener("click",s),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",i),document.removeEventListener("click",s),document.removeEventListener("keydown",a)}},m=e=>"true"===e.getAttribute("data-instance"),h=(e,t,n,o,r=!1,i=!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)"):i&&s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),e.appendChild(s),s};function g(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const p=(e,t=!1,n=!1)=>{const{top:r,left:i,width:s,height:a}=g(e),l=Math.max(s,3),d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.classList.add("highlight-frame-overlay"),t&&d.classList.add("is-instance"),n&&d.classList.add("is-text-edit"),d.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),d.style.position="absolute",d.style.top="0",d.style.left="0",d.style.width="100vw",d.style.height="100vh",d.style.pointerEvents="none",d.style.zIndex="500";const c=document.documentElement.clientWidth||window.innerWidth,u=document.documentElement.clientHeight||window.innerHeight;d.setAttribute("width",c.toString()),d.setAttribute("height",u.toString());const m=document.createElementNS("http://www.w3.org/2000/svg","g");m.classList.add("highlight-frame-group"),m.setAttribute("transform",`translate(${i}, ${r})`);const p=document.createElementNS("http://www.w3.org/2000/svg","rect");p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("width",l.toString()),p.setAttribute("height",a.toString()),p.setAttribute("vector-effect","non-scaling-stroke"),p.classList.add("highlight-frame-rect"),t?p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):n&&p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),m.appendChild(p),((e,t,n,o=!1,r=!1)=>{h(e,0,0,"handle-top-left",o,r),h(e,t,0,"handle-top-right",o,r),h(e,t,n,"handle-bottom-right",o,r),h(e,0,n,"handle-bottom-left",o,r)})(m,l,a,t,n),d.appendChild(m);const b=o();return b?b.appendChild(d):document.body.appendChild(d),d},b={div:"Container",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",h4:"Heading 4",h5:"Heading 5",h6:"Heading 6",p:"Text",li:"List Item",ul:"Unordered List",ol:"Ordered List",img:"Image",a:"Link"},v=(e,t,n=!1,o=!1)=>{const r=document.createElement("div");r.className="node-tools",n&&r.classList.add("is-instance"),o&&r.classList.add("is-text-edit"),t.appendChild(r),((e,t)=>{const n=document.createElement("div");n.className="tag-label";const o=e.getAttribute("data-instance-name"),r=e.tagName.toLowerCase(),i=o||b[r]||r;var s;n.textContent=(s=i)?s.charAt(0).toUpperCase()+s.slice(1):s,t.appendChild(n)})(e,r)};function y(){return(o()||document.body).querySelector(".highlight-frame-overlay")}const f=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",w=()=>getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)",E=(e,n,r="canvas")=>{const i=y();if(!i)return;const s=m(e),a="true"===e.contentEditable,l=document.documentElement.clientWidth||window.innerWidth,d=document.documentElement.clientHeight||window.innerHeight;i.setAttribute("width",l.toString()),i.setAttribute("height",d.toString()),s?i.classList.add("is-instance"):i.classList.remove("is-instance"),a?i.classList.add("is-text-edit"):i.classList.remove("is-text-edit");const c=i.querySelector(".highlight-frame-group");if(!c)return;const u=c.querySelector("rect");if(!u)return;s?u.setAttribute("stroke",f()):a?u.setAttribute("stroke",w()):u.removeAttribute("stroke");const h=(o()||document.body).querySelector(".highlight-frame-tools-wrapper"),p=h?.querySelector(".node-tools"),b=t(["zoom","current"],r)??1,v=g(e),{top:E,left:L,width:A,height:x}=v,S=Math.max(A,3),C=E+x;h&&(s?h.classList.add("is-instance"):h.classList.remove("is-instance"),a?h.classList.add("is-text-edit"):h.classList.remove("is-text-edit")),p&&(s?p.classList.add("is-instance"):p.classList.remove("is-instance"),a?p.classList.add("is-text-edit"):p.classList.remove("is-text-edit")),c.setAttribute("transform",`translate(${L}, ${E})`),u.setAttribute("width",S.toString()),u.setAttribute("height",x.toString());const k=c.querySelector(".handle-top-left"),N=c.querySelector(".handle-top-right"),q=c.querySelector(".handle-bottom-right"),M=c.querySelector(".handle-bottom-left");[k,N,q,M].forEach(e=>{e&&(s?e.setAttribute("stroke",f()):a?e.setAttribute("stroke",w()):e.removeAttribute("stroke"))}),k&&(k.setAttribute("x",(-3).toString()),k.setAttribute("y",(-3).toString())),N&&(N.setAttribute("x",(S-3).toString()),N.setAttribute("y",(-3).toString())),q&&(q.setAttribute("x",(S-3).toString()),q.setAttribute("y",(x-3).toString())),M&&(M.setAttribute("x",(-3).toString()),M.setAttribute("y",(x-3).toString())),h&&(h.style.transform=`translate(${L}px, ${C}px)`),b<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},L=e=>{const t=y();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const r=(o()||document.body).querySelector(".highlight-frame-tools-wrapper");r&&(r.style.display=n)},A=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)}},x=(e,t,o=!1)=>{if(!t.some(e=>"characterData"===e.type||"childList"===e.type&&(e.addedNodes.length>0||e.removedNodes.length>0))&&!o)return;const r=e.textContent??"",i=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:i,textContent:r,final:o})},S=(e,t,n="canvas")=>{let o=[],r=null,i=null;const s=()=>{null===r&&(r=requestAnimationFrame(()=>{i=requestAnimationFrame(()=>{(()=>{if(o.length>0){const t=[...o];o=[],x(e,t,!1)}})(),r=null,i=null})}))},a=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,r=>{o.push(...r),s(),E(e,t,n)});return()=>{a.disconnect(),null!==r&&(cancelAnimationFrame(r),r=null),null!==i&&(cancelAnimationFrame(i),i=null),o=[]}},C=(e="canvas")=>{let n=null,o=!1,r=null;const i=()=>{if(o||!n)return;o=!0;const i=n;var s;x(i,[],!0),(s=i).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,s)=>{if(n===o)return;n&&n!==o&&i();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=A(e),i=S(e,t,o);return()=>{e.removeEventListener("blur",n),r(),i?.()}})(o,s,i,e))},blurEditMode:i,getEditableNode:()=>n,isEditing:()=>null!==n}};const k=320,N=1680,q=[{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"}],M=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(k,Math.min(N,n))})(n,(e.clientX-t)/o);return r},P=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<q.length&&(q[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 i=e;let s=null,a=null,l=null,d=null;const c=C(t),h=e=>{if(d!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}if(s?.disconnect(),a?.disconnect(),l?.disconnect(),e&&i){const o=()=>{if(!document.contains(e))return r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect(),void n("selectedNodeChanged",null)};a=new MutationObserver(()=>{o(),document.contains(e)&&(E(e,i,t),L(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(l=new MutationObserver(t=>{for(const n of t)if("childList"===n.type)for(const t of Array.from(n.removedNodes))if(t===e||t instanceof Node&&t.contains(e))return void o()}),l.observe(c,{childList:!0,subtree:!1})),s=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{o(),document.contains(e)&&(E(e,i,t),L(e))})}d=e,n("selectedNodeChanged",e?.getAttribute("data-node-id")??null),(e=>{if(!e)return;const t=y(),n=o(),r=n?.querySelector(".highlight-frame-tools-wrapper")||document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),r&&r.remove();const i=m(e),s="true"===e.contentEditable,a=p(e,i,s);"true"===e.contentEditable&&a.classList.add("is-editable");const{left:l,top:d,height:c}=g(e),u=d+c,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),i&&h.classList.add("is-instance"),s&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${l}px, ${u}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",v(e,h,i,s),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&i&&(L(e),L(e))}},b=u(i,h,()=>{c.isEditing()&&c.blurEditMode(),d&&i&&(r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect())},c),f={selectNode:h,getSelectedNode:()=>d,refreshHighlightFrame:()=>{d&&i&&(E(d,i,t),L(d))},clearSelectedNode:()=>{r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{b(),s?.disconnect(),a?.disconnect(),l?.disconnect(),c.blurEditMode(),r(),d=null,n("selectedNodeChanged",null)}};var w,A;return w="nodeTools",A=f,"undefined"!=typeof window&&(window[w]=A),f},e.createViewport=e=>{const t=o(),n=e.querySelector(".resize-handle");n&&n.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",q.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,P);let i=!1,s=0,a=0;const l=function(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}(n=>{if(!i)return;t&&(t.style.cursor="ew-resize");const o=M(n,s,a);P(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(),i=!0,s=t.clientX,a=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),i=!1},()=>{i=!1});return{setWidth:t=>{P(e,t)},cleanup:()=>{i=!1,l?.cleanup(),d(),r.remove()}}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-edit-utils/core",
3
- "version": "2.2.9",
3
+ "version": "2.3.0",
4
4
  "description": "Utilities for editing nodes in a dom tree.",
5
5
  "type": "module",
6
6
  "main": "dist/node-edit-utils.cjs.js",
@@ -16,7 +16,7 @@ export const setupMutationObserver = (
16
16
  if (pendingMutations.length > 0) {
17
17
  const mutationsToProcess = [...pendingMutations];
18
18
  pendingMutations = [];
19
- handleTextChange(node, mutationsToProcess);
19
+ handleTextChange(node, mutationsToProcess, false);
20
20
  }
21
21
  };
22
22
 
@@ -1,6 +1,6 @@
1
1
  import { sendPostMessage } from "@/lib/post-message/sendPostMessage";
2
2
 
3
- export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[]): void => {
3
+ export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[], final: boolean = false): void => {
4
4
  // Check if any mutation is a text content change
5
5
  const hasTextChange = mutations.some((mutation) => {
6
6
  return (
@@ -9,7 +9,7 @@ export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[])
9
9
  );
10
10
  });
11
11
 
12
- if (!hasTextChange) {
12
+ if (!hasTextChange && !final) {
13
13
  return;
14
14
  }
15
15
 
@@ -19,11 +19,12 @@ export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[])
19
19
  // Get the node ID
20
20
  const nodeId = node.getAttribute("data-node-id");
21
21
 
22
- console.log("textContentChanged", nodeId, textContent);
22
+ console.log("textContentChanged", textContent, final);
23
23
 
24
24
  // Send postMessage with the text change
25
25
  sendPostMessage("textContentChanged", {
26
26
  nodeId,
27
27
  textContent,
28
+ final,
28
29
  });
29
30
  };
@@ -1,6 +1,7 @@
1
1
  import { disableCanvasTextMode } from "@/lib/canvas/disableCanvasTextMode";
2
2
  import { enableCanvasTextMode } from "@/lib/canvas/enableCanvasTextMode";
3
3
  import { setupNodeListeners } from "./events/setupNodeListeners";
4
+ import { handleTextChange } from "./helpers/handleTextChange";
4
5
  import { hasTextContent } from "./helpers/hasTextContent";
5
6
  import { makeNodeEditable } from "./helpers/makeNodeEditable";
6
7
  import { makeNodeNonEditable } from "./helpers/makeNodeNonEditable";
@@ -49,6 +50,9 @@ export const nodeText = (canvasName: string = "canvas"): NodeText => {
49
50
 
50
51
  const nodeToCleanup = editableNode;
51
52
 
53
+ // Send final textContentChanged message before cleanup
54
+ handleTextChange(nodeToCleanup, [], true);
55
+
52
56
  makeNodeNonEditable(nodeToCleanup);
53
57
  disableCanvasTextMode(canvasName);
54
58
  cleanup?.();