@node-edit-utils/core 2.2.9 → 2.3.1

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,2 +1,2 @@
1
1
  import type { Viewport } from "./types";
2
- export declare const createViewport: (container: HTMLElement) => Viewport;
2
+ export declare const createViewport: (container: HTMLElement, initialWidth?: number) => Viewport;
@@ -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.1
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?.();
@@ -924,32 +927,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
924
927
  return nodeTools;
925
928
  };
926
929
 
927
- // biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
928
- function withRAFThrottle(func) {
929
- let rafId = null;
930
- let lastArgs = null;
931
- const throttled = (...args) => {
932
- lastArgs = args;
933
- if (rafId === null) {
934
- rafId = requestAnimationFrame(() => {
935
- if (lastArgs) {
936
- func(...lastArgs);
937
- }
938
- rafId = null;
939
- lastArgs = null;
940
- });
941
- }
942
- };
943
- throttled.cleanup = () => {
944
- if (rafId !== null) {
945
- cancelAnimationFrame(rafId);
946
- rafId = null;
947
- lastArgs = null;
948
- }
949
- };
950
- return throttled;
951
- }
952
-
953
930
  const DEFAULT_WIDTH = 400;
954
931
  const RESIZE_CONFIG = {
955
932
  minWidth: 320,
@@ -984,14 +961,22 @@ const RESIZE_PRESETS = [
984
961
  ];
985
962
 
986
963
  const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
964
+ const handleMouseLeave = (event) => {
965
+ // Check if mouse is leaving the window/document
966
+ if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
967
+ blurResize();
968
+ }
969
+ };
987
970
  resizeHandle.addEventListener("mousedown", startResize);
988
971
  document.addEventListener("mousemove", handleResize);
989
972
  document.addEventListener("mouseup", stopResize);
973
+ document.addEventListener("mouseleave", handleMouseLeave);
990
974
  window.addEventListener("blur", blurResize);
991
975
  return () => {
992
976
  resizeHandle.removeEventListener("mousedown", startResize);
993
977
  document.removeEventListener("mousemove", handleResize);
994
978
  document.removeEventListener("mouseup", stopResize);
979
+ document.removeEventListener("mouseleave", handleMouseLeave);
995
980
  window.removeEventListener("blur", blurResize);
996
981
  };
997
982
  };
@@ -1051,7 +1036,7 @@ const updateWidth = (container, width) => {
1051
1036
  updateActivePreset(container, width);
1052
1037
  };
1053
1038
 
1054
- const createViewport = (container) => {
1039
+ const createViewport = (container, initialWidth) => {
1055
1040
  const canvas = getCanvasContainer();
1056
1041
  // Remove any existing resize handle to prevent duplicates
1057
1042
  const existingHandle = container.querySelector(".resize-handle");
@@ -1059,7 +1044,8 @@ const createViewport = (container) => {
1059
1044
  existingHandle.remove();
1060
1045
  }
1061
1046
  const resizeHandle = createResizeHandle(container);
1062
- container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
1047
+ const width = initialWidth ?? DEFAULT_WIDTH;
1048
+ container.style.setProperty("--container-width", `${width}px`);
1063
1049
  createResizePresets(resizeHandle, container, updateWidth);
1064
1050
  let isDragging = false;
1065
1051
  let startX = 0;
@@ -1080,7 +1066,6 @@ const createViewport = (container) => {
1080
1066
  const width = calcWidth(event, startX, startWidth);
1081
1067
  updateWidth(container, width);
1082
1068
  };
1083
- const throttledHandleResize = withRAFThrottle(handleResize);
1084
1069
  const stopResize = (event) => {
1085
1070
  event.preventDefault();
1086
1071
  event.stopPropagation();
@@ -1090,12 +1075,14 @@ const createViewport = (container) => {
1090
1075
  isDragging = false;
1091
1076
  };
1092
1077
  const blurResize = () => {
1078
+ if (canvas) {
1079
+ canvas.style.cursor = "default";
1080
+ }
1093
1081
  isDragging = false;
1094
1082
  };
1095
- const removeListeners = setupEventListener(resizeHandle, startResize, throttledHandleResize, stopResize, blurResize);
1083
+ const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
1096
1084
  const cleanup = () => {
1097
1085
  isDragging = false;
1098
- throttledHandleResize?.cleanup();
1099
1086
  removeListeners();
1100
1087
  resizeHandle.remove();
1101
1088
  };
@@ -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.1
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?.();
@@ -922,32 +925,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
922
925
  return nodeTools;
923
926
  };
924
927
 
925
- // biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
926
- function withRAFThrottle(func) {
927
- let rafId = null;
928
- let lastArgs = null;
929
- const throttled = (...args) => {
930
- lastArgs = args;
931
- if (rafId === null) {
932
- rafId = requestAnimationFrame(() => {
933
- if (lastArgs) {
934
- func(...lastArgs);
935
- }
936
- rafId = null;
937
- lastArgs = null;
938
- });
939
- }
940
- };
941
- throttled.cleanup = () => {
942
- if (rafId !== null) {
943
- cancelAnimationFrame(rafId);
944
- rafId = null;
945
- lastArgs = null;
946
- }
947
- };
948
- return throttled;
949
- }
950
-
951
928
  const DEFAULT_WIDTH = 400;
952
929
  const RESIZE_CONFIG = {
953
930
  minWidth: 320,
@@ -982,14 +959,22 @@ const RESIZE_PRESETS = [
982
959
  ];
983
960
 
984
961
  const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
962
+ const handleMouseLeave = (event) => {
963
+ // Check if mouse is leaving the window/document
964
+ if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
965
+ blurResize();
966
+ }
967
+ };
985
968
  resizeHandle.addEventListener("mousedown", startResize);
986
969
  document.addEventListener("mousemove", handleResize);
987
970
  document.addEventListener("mouseup", stopResize);
971
+ document.addEventListener("mouseleave", handleMouseLeave);
988
972
  window.addEventListener("blur", blurResize);
989
973
  return () => {
990
974
  resizeHandle.removeEventListener("mousedown", startResize);
991
975
  document.removeEventListener("mousemove", handleResize);
992
976
  document.removeEventListener("mouseup", stopResize);
977
+ document.removeEventListener("mouseleave", handleMouseLeave);
993
978
  window.removeEventListener("blur", blurResize);
994
979
  };
995
980
  };
@@ -1049,7 +1034,7 @@ const updateWidth = (container, width) => {
1049
1034
  updateActivePreset(container, width);
1050
1035
  };
1051
1036
 
1052
- const createViewport = (container) => {
1037
+ const createViewport = (container, initialWidth) => {
1053
1038
  const canvas = getCanvasContainer();
1054
1039
  // Remove any existing resize handle to prevent duplicates
1055
1040
  const existingHandle = container.querySelector(".resize-handle");
@@ -1057,7 +1042,8 @@ const createViewport = (container) => {
1057
1042
  existingHandle.remove();
1058
1043
  }
1059
1044
  const resizeHandle = createResizeHandle(container);
1060
- container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
1045
+ const width = initialWidth ?? DEFAULT_WIDTH;
1046
+ container.style.setProperty("--container-width", `${width}px`);
1061
1047
  createResizePresets(resizeHandle, container, updateWidth);
1062
1048
  let isDragging = false;
1063
1049
  let startX = 0;
@@ -1078,7 +1064,6 @@ const createViewport = (container) => {
1078
1064
  const width = calcWidth(event, startX, startWidth);
1079
1065
  updateWidth(container, width);
1080
1066
  };
1081
- const throttledHandleResize = withRAFThrottle(handleResize);
1082
1067
  const stopResize = (event) => {
1083
1068
  event.preventDefault();
1084
1069
  event.stopPropagation();
@@ -1088,12 +1073,14 @@ const createViewport = (container) => {
1088
1073
  isDragging = false;
1089
1074
  };
1090
1075
  const blurResize = () => {
1076
+ if (canvas) {
1077
+ canvas.style.cursor = "default";
1078
+ }
1091
1079
  isDragging = false;
1092
1080
  };
1093
- const removeListeners = setupEventListener(resizeHandle, startResize, throttledHandleResize, stopResize, blurResize);
1081
+ const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
1094
1082
  const cleanup = () => {
1095
1083
  isDragging = false;
1096
- throttledHandleResize?.cleanup();
1097
1084
  removeListeners();
1098
1085
  resizeHandle.remove();
1099
1086
  };
@@ -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.1
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?.();
@@ -928,32 +931,6 @@
928
931
  return nodeTools;
929
932
  };
930
933
 
931
- // biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
932
- function withRAFThrottle(func) {
933
- let rafId = null;
934
- let lastArgs = null;
935
- const throttled = (...args) => {
936
- lastArgs = args;
937
- if (rafId === null) {
938
- rafId = requestAnimationFrame(() => {
939
- if (lastArgs) {
940
- func(...lastArgs);
941
- }
942
- rafId = null;
943
- lastArgs = null;
944
- });
945
- }
946
- };
947
- throttled.cleanup = () => {
948
- if (rafId !== null) {
949
- cancelAnimationFrame(rafId);
950
- rafId = null;
951
- lastArgs = null;
952
- }
953
- };
954
- return throttled;
955
- }
956
-
957
934
  const DEFAULT_WIDTH = 400;
958
935
  const RESIZE_CONFIG = {
959
936
  minWidth: 320,
@@ -988,14 +965,22 @@
988
965
  ];
989
966
 
990
967
  const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
968
+ const handleMouseLeave = (event) => {
969
+ // Check if mouse is leaving the window/document
970
+ if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
971
+ blurResize();
972
+ }
973
+ };
991
974
  resizeHandle.addEventListener("mousedown", startResize);
992
975
  document.addEventListener("mousemove", handleResize);
993
976
  document.addEventListener("mouseup", stopResize);
977
+ document.addEventListener("mouseleave", handleMouseLeave);
994
978
  window.addEventListener("blur", blurResize);
995
979
  return () => {
996
980
  resizeHandle.removeEventListener("mousedown", startResize);
997
981
  document.removeEventListener("mousemove", handleResize);
998
982
  document.removeEventListener("mouseup", stopResize);
983
+ document.removeEventListener("mouseleave", handleMouseLeave);
999
984
  window.removeEventListener("blur", blurResize);
1000
985
  };
1001
986
  };
@@ -1055,7 +1040,7 @@
1055
1040
  updateActivePreset(container, width);
1056
1041
  };
1057
1042
 
1058
- const createViewport = (container) => {
1043
+ const createViewport = (container, initialWidth) => {
1059
1044
  const canvas = getCanvasContainer();
1060
1045
  // Remove any existing resize handle to prevent duplicates
1061
1046
  const existingHandle = container.querySelector(".resize-handle");
@@ -1063,7 +1048,8 @@
1063
1048
  existingHandle.remove();
1064
1049
  }
1065
1050
  const resizeHandle = createResizeHandle(container);
1066
- container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
1051
+ const width = initialWidth ?? DEFAULT_WIDTH;
1052
+ container.style.setProperty("--container-width", `${width}px`);
1067
1053
  createResizePresets(resizeHandle, container, updateWidth);
1068
1054
  let isDragging = false;
1069
1055
  let startX = 0;
@@ -1084,7 +1070,6 @@
1084
1070
  const width = calcWidth(event, startX, startWidth);
1085
1071
  updateWidth(container, width);
1086
1072
  };
1087
- const throttledHandleResize = withRAFThrottle(handleResize);
1088
1073
  const stopResize = (event) => {
1089
1074
  event.preventDefault();
1090
1075
  event.stopPropagation();
@@ -1094,12 +1079,14 @@
1094
1079
  isDragging = false;
1095
1080
  };
1096
1081
  const blurResize = () => {
1082
+ if (canvas) {
1083
+ canvas.style.cursor = "default";
1084
+ }
1097
1085
  isDragging = false;
1098
1086
  };
1099
- const removeListeners = setupEventListener(resizeHandle, startResize, throttledHandleResize, stopResize, blurResize);
1087
+ const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
1100
1088
  const cleanup = () => {
1101
1089
  isDragging = false;
1102
- throttledHandleResize?.cleanup();
1103
1090
  removeListeners();
1104
1091
  resizeHandle.remove();
1105
1092
  };
@@ -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()},s=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},i=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],d=0,l=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=>!i.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,l=o,o;if(u)return a=[],o=m[0],l&&l===o&&s(o,t,n),l=o,o;var g,p;p=m,(g=a).length===p.length&&g.every((e,t)=>e===p[t])?d<=m.length-2&&d++:d=0;return o=m[m.length-1-d],a=m,l&&l===o&&s(o,t,n),l=o,o},u=(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(c(e,t,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)}},m=e=>"true"===e.getAttribute("data-instance"),h=(e,t,n,o,r=!1,s=!1)=>{const i=document.createElementNS("http://www.w3.org/2000/svg","rect");return i.setAttribute("x",(t-3).toString()),i.setAttribute("y",(n-3).toString()),i.setAttribute("width",6..toString()),i.setAttribute("height",6..toString()),i.setAttribute("vector-effect","non-scaling-stroke"),i.classList.add("highlight-frame-handle",o),r?i.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):s&&i.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),e.appendChild(i),i};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:s,width:i,height:a}=g(e),d=Math.max(i,3),l=document.createElementNS("http://www.w3.org/2000/svg","svg");l.classList.add("highlight-frame-overlay"),t&&l.classList.add("is-instance"),n&&l.classList.add("is-text-edit"),l.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),l.style.position="absolute",l.style.top="0",l.style.left="0",l.style.width="100vw",l.style.height="100vh",l.style.pointerEvents="none",l.style.zIndex="500";const c=document.documentElement.clientWidth||window.innerWidth,u=document.documentElement.clientHeight||window.innerHeight;l.setAttribute("width",c.toString()),l.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(${s}, ${r})`);const p=document.createElementNS("http://www.w3.org/2000/svg","rect");p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("width",d.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,d,a,t,n),l.appendChild(m);const v=o();return v?v.appendChild(l):document.body.appendChild(l),l},v={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"},b=(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(),s=o||v[r]||r;var i;n.textContent=(i=s)?i.charAt(0).toUpperCase()+i.slice(1):i,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 s=y();if(!s)return;const i=m(e),a="true"===e.contentEditable,d=document.documentElement.clientWidth||window.innerWidth,l=document.documentElement.clientHeight||window.innerHeight;s.setAttribute("width",d.toString()),s.setAttribute("height",l.toString()),i?s.classList.add("is-instance"):s.classList.remove("is-instance"),a?s.classList.add("is-text-edit"):s.classList.remove("is-text-edit");const c=s.querySelector(".highlight-frame-group");if(!c)return;const u=c.querySelector("rect");if(!u)return;i?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"),v=t(["zoom","current"],r)??1,b=g(e),{top:E,left:L,width:A,height:x}=b,S=Math.max(A,3),C=E+x;h&&(i?h.classList.add("is-instance"):h.classList.remove("is-instance"),a?h.classList.add("is-text-edit"):h.classList.remove("is-text-edit")),p&&(i?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&&(i?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)`),v<=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??"",s=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:s,textContent:r,final:o})},S=(e,t,n="canvas")=>{let o=[],r=null,s=null;const i=()=>{null===r&&(r=requestAnimationFrame(()=>{s=requestAnimationFrame(()=>{(()=>{if(o.length>0){const t=[...o];o=[],x(e,t,!1)}})(),r=null,s=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),i(),E(e,t,n)});return()=>{a.disconnect(),null!==r&&(cancelAnimationFrame(r),r=null),null!==s&&(cancelAnimationFrame(s),s=null),o=[]}},C=(e="canvas")=>{let n=null,o=!1,r=null;const s=()=>{if(o||!n)return;o=!0;const s=n;var i;x(s,[],!0),(i=s).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,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=A(e),s=S(e,t,o);return()=>{e.removeEventListener("blur",n),r(),s?.()}})(o,i,s,e))},blurEditMode:s,getEditableNode:()=>n,isEditing:()=>null!==n}},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 s=e;let i=null,a=null,d=null,l=null;const c=C(t),h=e=>{if(l!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}if(i?.disconnect(),a?.disconnect(),d?.disconnect(),e&&s){const o=()=>{if(!document.contains(e))return r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect(),void n("selectedNodeChanged",null)};a=new MutationObserver(()=>{o(),document.contains(e)&&(E(e,s,t),L(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(d=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()}),d.observe(c,{childList:!0,subtree:!1})),i=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{o(),document.contains(e)&&(E(e,s,t),L(e))})}l=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 s=m(e),i="true"===e.contentEditable,a=p(e,s,i);"true"===e.contentEditable&&a.classList.add("is-editable");const{left:d,top:l,height:c}=g(e),u=l+c,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),s&&h.classList.add("is-instance"),i&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${d}px, ${u}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",b(e,h,s,i),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&s&&(L(e),L(e))}},v=u(s,h,()=>{c.isEditing()&&c.blurEditMode(),l&&s&&(r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect())},c),f={selectNode:h,getSelectedNode:()=>l,refreshHighlightFrame:()=>{l&&s&&(E(l,s,t),L(l))},clearSelectedNode:()=>{r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{v(),i?.disconnect(),a?.disconnect(),d?.disconnect(),c.blurEditMode(),r(),l=null,n("selectedNodeChanged",null)}};var w,A;return w="nodeTools",A=f,"undefined"!=typeof window&&(window[w]=A),f},e.createViewport=(e,t)=>{const n=o(),r=e.querySelector(".resize-handle");r&&r.remove();const s=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e),i=t??400;e.style.setProperty("--container-width",`${i}px`),((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)})(s,e,P);let a=!1,d=0,l=0;const c=((e,t,n,o,r)=>{const s=e=>{e.relatedTarget||e.target!==document&&e.target!==document.documentElement||r()};return e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),document.addEventListener("mouseleave",s),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),document.removeEventListener("mouseleave",s),window.removeEventListener("blur",r)}})(s,t=>{t.preventDefault(),t.stopPropagation(),a=!0,d=t.clientX,l=e.offsetWidth},t=>{if(!a)return;n&&(n.style.cursor="ew-resize");const o=M(t,d,l);P(e,o)},e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),a=!1},()=>{n&&(n.style.cursor="default"),a=!1});return{setWidth:t=>{P(e,t)},cleanup:()=>{a=!1,c(),s.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.1",
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?.();
@@ -1,5 +1,4 @@
1
1
  import { getCanvasContainer } from "../canvas/helpers/getCanvasContainer";
2
- import { withRAFThrottle } from "../helpers";
3
2
  import { DEFAULT_WIDTH } from "./constants";
4
3
  import { setupEventListener } from "./events/setupEventListener";
5
4
  import { createResizeHandle } from "./resize/createResizeHandle";
@@ -8,7 +7,7 @@ import type { Viewport } from "./types";
8
7
  import { calcWidth } from "./width/calcWidth";
9
8
  import { updateWidth } from "./width/updateWidth";
10
9
 
11
- export const createViewport = (container: HTMLElement): Viewport => {
10
+ export const createViewport = (container: HTMLElement, initialWidth?: number): Viewport => {
12
11
  const canvas: HTMLElement | null = getCanvasContainer();
13
12
 
14
13
  // Remove any existing resize handle to prevent duplicates
@@ -18,7 +17,8 @@ export const createViewport = (container: HTMLElement): Viewport => {
18
17
  }
19
18
 
20
19
  const resizeHandle = createResizeHandle(container);
21
- container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
20
+ const width = initialWidth ?? DEFAULT_WIDTH;
21
+ container.style.setProperty("--container-width", `${width}px`);
22
22
 
23
23
  createResizePresets(resizeHandle, container, updateWidth);
24
24
 
@@ -46,8 +46,6 @@ export const createViewport = (container: HTMLElement): Viewport => {
46
46
  updateWidth(container, width);
47
47
  };
48
48
 
49
- const throttledHandleResize = withRAFThrottle(handleResize);
50
-
51
49
  const stopResize = (event: MouseEvent): void => {
52
50
  event.preventDefault();
53
51
  event.stopPropagation();
@@ -60,14 +58,17 @@ export const createViewport = (container: HTMLElement): Viewport => {
60
58
  };
61
59
 
62
60
  const blurResize = (): void => {
61
+ if (canvas) {
62
+ canvas.style.cursor = "default";
63
+ }
64
+
63
65
  isDragging = false;
64
66
  };
65
67
 
66
- const removeListeners = setupEventListener(resizeHandle, startResize, throttledHandleResize, stopResize, blurResize);
68
+ const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
67
69
 
68
70
  const cleanup = (): void => {
69
71
  isDragging = false;
70
- throttledHandleResize?.cleanup();
71
72
  removeListeners();
72
73
  resizeHandle.remove();
73
74
  };
@@ -5,9 +5,17 @@ export const setupEventListener = (
5
5
  stopResize: (event: MouseEvent) => void,
6
6
  blurResize: () => void
7
7
  ): (() => void) => {
8
+ const handleMouseLeave = (event: MouseEvent): void => {
9
+ // Check if mouse is leaving the window/document
10
+ if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
11
+ blurResize();
12
+ }
13
+ };
14
+
8
15
  resizeHandle.addEventListener("mousedown", startResize);
9
16
  document.addEventListener("mousemove", handleResize);
10
17
  document.addEventListener("mouseup", stopResize);
18
+ document.addEventListener("mouseleave", handleMouseLeave);
11
19
 
12
20
  window.addEventListener("blur", blurResize);
13
21
 
@@ -15,6 +23,7 @@ export const setupEventListener = (
15
23
  resizeHandle.removeEventListener("mousedown", startResize);
16
24
  document.removeEventListener("mousemove", handleResize);
17
25
  document.removeEventListener("mouseup", stopResize);
26
+ document.removeEventListener("mouseleave", handleMouseLeave);
18
27
  window.removeEventListener("blur", blurResize);
19
28
  };
20
29
  };