@node-edit-utils/core 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/lib/node-tools/select/helpers/isInsideViewport.d.ts +1 -0
  2. package/dist/lib/viewport/constants.d.ts +2 -2
  3. package/dist/lib/viewport/createViewport.d.ts +1 -1
  4. package/dist/lib/viewport/label/getViewportLabelsOverlay.d.ts +1 -0
  5. package/dist/lib/viewport/label/helpers/getLabelPosition.d.ts +4 -0
  6. package/dist/lib/viewport/label/helpers/getTransformValues.d.ts +4 -0
  7. package/dist/lib/viewport/label/helpers/getZoomValue.d.ts +1 -0
  8. package/dist/lib/viewport/label/index.d.ts +4 -0
  9. package/dist/lib/viewport/label/isViewportLabelDragging.d.ts +2 -0
  10. package/dist/lib/viewport/label/refreshViewportLabels.d.ts +1 -0
  11. package/dist/lib/viewport/label/setupViewportLabelDrag.d.ts +1 -0
  12. package/dist/node-edit-utils.cjs.js +288 -63
  13. package/dist/node-edit-utils.esm.js +288 -63
  14. package/dist/node-edit-utils.umd.js +288 -63
  15. package/dist/node-edit-utils.umd.min.js +1 -1
  16. package/dist/styles.css +1 -1
  17. package/package.json +1 -1
  18. package/src/lib/canvas/createCanvasObserver.ts +14 -0
  19. package/src/lib/node-tools/createNodeTools.ts +12 -5
  20. package/src/lib/node-tools/select/helpers/isInsideViewport.ts +19 -0
  21. package/src/lib/node-tools/select/selectNode.ts +13 -1
  22. package/src/lib/node-tools/text/events/setupMutationObserver.ts +1 -0
  23. package/src/lib/styles/styles.css +48 -1
  24. package/src/lib/viewport/constants.ts +2 -2
  25. package/src/lib/viewport/createViewport.ts +26 -7
  26. package/src/lib/viewport/events/setupEventListener.ts +9 -0
  27. package/src/lib/viewport/label/getViewportLabelsOverlay.ts +33 -0
  28. package/src/lib/viewport/label/helpers/getLabelPosition.ts +8 -0
  29. package/src/lib/viewport/label/helpers/getTransformValues.ts +8 -0
  30. package/src/lib/viewport/label/helpers/getZoomValue.ts +4 -0
  31. package/src/lib/viewport/label/index.ts +4 -0
  32. package/src/lib/viewport/label/isViewportLabelDragging.ts +9 -0
  33. package/src/lib/viewport/label/refreshViewportLabels.ts +69 -0
  34. package/src/lib/viewport/label/setupViewportLabelDrag.ts +98 -0
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Markup Canvas
3
3
  * High-performance markup canvas with zoom and pan capabilities
4
- * @version 2.3.0
4
+ * @version 2.3.2
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
@@ -9,6 +9,214 @@
9
9
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MarkupCanvas = {}));
10
10
  })(this, (function (exports) { 'use strict';
11
11
 
12
+ function getScreenBounds(element) {
13
+ const rect = element.getBoundingClientRect();
14
+ return {
15
+ top: rect.top,
16
+ left: rect.left,
17
+ width: rect.width,
18
+ height: rect.height,
19
+ };
20
+ }
21
+
22
+ const getCanvasContainer = () => {
23
+ return document.querySelector(".canvas-container");
24
+ };
25
+
26
+ const getViewportLabelsOverlay = () => {
27
+ const canvasContainer = getCanvasContainer();
28
+ const container = canvasContainer || document.body;
29
+ // Check if overlay already exists
30
+ let overlay = container.querySelector(".viewport-labels-overlay");
31
+ if (!overlay) {
32
+ // Create new SVG overlay
33
+ overlay = document.createElementNS("http://www.w3.org/2000/svg", "svg");
34
+ overlay.classList.add("viewport-labels-overlay");
35
+ // Set fixed positioning
36
+ overlay.style.position = "absolute";
37
+ overlay.style.top = "0";
38
+ overlay.style.left = "0";
39
+ overlay.style.width = "100vw";
40
+ overlay.style.height = "100vh";
41
+ overlay.style.pointerEvents = "none";
42
+ overlay.style.zIndex = "500";
43
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
44
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
45
+ overlay.setAttribute("width", viewportWidth.toString());
46
+ overlay.setAttribute("height", viewportHeight.toString());
47
+ container.appendChild(overlay);
48
+ }
49
+ return overlay;
50
+ };
51
+
52
+ // Global flag to prevent refreshViewportLabels during drag
53
+ let globalIsDragging = false;
54
+ const isViewportLabelDragging = () => globalIsDragging;
55
+ const setViewportLabelDragging = (isDragging) => {
56
+ globalIsDragging = isDragging;
57
+ };
58
+
59
+ function sendPostMessage(action, data) {
60
+ window.parent.postMessage({
61
+ source: "node-edit-utils",
62
+ action,
63
+ data,
64
+ timestamp: Date.now(),
65
+ }, "*");
66
+ }
67
+
68
+ const getLabelPosition = (labelGroup) => {
69
+ const transform = labelGroup.getAttribute("transform");
70
+ const match = transform?.match(/translate\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)/);
71
+ if (match) {
72
+ return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
73
+ }
74
+ return { x: 0, y: 0 };
75
+ };
76
+
77
+ const getTransformValues = (element) => {
78
+ const style = element.style.transform;
79
+ const match = style.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/);
80
+ if (match) {
81
+ return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
82
+ }
83
+ return { x: 0, y: 0 };
84
+ };
85
+
86
+ const getZoomValue = () => {
87
+ const zoomValue = getComputedStyle(document.body).getPropertyValue("--zoom").trim();
88
+ return zoomValue ? parseFloat(zoomValue) : 1;
89
+ };
90
+
91
+ const setupViewportLabelDrag = (labelElement, viewportElement, viewportName) => {
92
+ let isDragging = false;
93
+ let startX = 0;
94
+ let startY = 0;
95
+ let initialTransform = { x: 0, y: 0 };
96
+ let initialLabelPosition = { x: 0, y: 0 };
97
+ // Get the parent group element that contains the label
98
+ const labelGroup = labelElement.parentElement;
99
+ const startDrag = (event) => {
100
+ event.preventDefault();
101
+ event.stopPropagation();
102
+ isDragging = true;
103
+ setViewportLabelDragging(true);
104
+ startX = event.clientX;
105
+ startY = event.clientY;
106
+ initialTransform = getTransformValues(viewportElement);
107
+ initialLabelPosition = getLabelPosition(labelGroup);
108
+ };
109
+ const handleDrag = (event) => {
110
+ if (!isDragging)
111
+ return;
112
+ const zoom = getZoomValue();
113
+ // Calculate mouse delta
114
+ const rawDeltaX = event.clientX - startX;
115
+ const rawDeltaY = event.clientY - startY;
116
+ // Adjust delta for zoom level
117
+ const deltaX = rawDeltaX / zoom;
118
+ const deltaY = rawDeltaY / zoom;
119
+ const newX = initialTransform.x + deltaX;
120
+ const newY = initialTransform.y + deltaY;
121
+ // Update label position with raw delta (labels are in screen space)
122
+ const newLabelX = initialLabelPosition.x + rawDeltaX;
123
+ const newLabelY = initialLabelPosition.y + rawDeltaY;
124
+ labelGroup.setAttribute("transform", `translate(${newLabelX}, ${newLabelY})`);
125
+ // Update viewport position with zoom-adjusted delta
126
+ viewportElement.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;
127
+ };
128
+ const stopDrag = (event) => {
129
+ if (!isDragging)
130
+ return;
131
+ event.preventDefault();
132
+ event.stopPropagation();
133
+ isDragging = false;
134
+ setViewportLabelDragging(false);
135
+ const finalTransform = getTransformValues(viewportElement);
136
+ // Trigger refresh after drag completes to update highlight frame and labels
137
+ // biome-ignore lint/suspicious/noExplicitAny: global window extension
138
+ const nodeTools = window.nodeTools;
139
+ if (nodeTools?.refreshHighlightFrame) {
140
+ nodeTools.refreshHighlightFrame();
141
+ }
142
+ // Notify parent about the new position
143
+ sendPostMessage("viewport-position-changed", {
144
+ viewportName,
145
+ x: finalTransform.x,
146
+ y: finalTransform.y,
147
+ });
148
+ };
149
+ const cancelDrag = () => {
150
+ isDragging = false;
151
+ setViewportLabelDragging(false);
152
+ };
153
+ // Attach event listeners
154
+ labelElement.addEventListener("mousedown", startDrag);
155
+ document.addEventListener("mousemove", handleDrag);
156
+ document.addEventListener("mouseup", stopDrag);
157
+ window.addEventListener("blur", cancelDrag);
158
+ // Return cleanup function
159
+ return () => {
160
+ labelElement.removeEventListener("mousedown", startDrag);
161
+ document.removeEventListener("mousemove", handleDrag);
162
+ document.removeEventListener("mouseup", stopDrag);
163
+ window.removeEventListener("blur", cancelDrag);
164
+ };
165
+ };
166
+
167
+ // Store cleanup functions for drag listeners
168
+ const dragCleanupFunctions = new Map();
169
+ const refreshViewportLabels = () => {
170
+ // Skip refresh if a viewport label is being dragged
171
+ if (isViewportLabelDragging()) {
172
+ return;
173
+ }
174
+ const overlay = getViewportLabelsOverlay();
175
+ // Update SVG dimensions to match current viewport
176
+ const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
177
+ const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
178
+ overlay.setAttribute("width", viewportWidth.toString());
179
+ overlay.setAttribute("height", viewportHeight.toString());
180
+ // Find all viewports with names
181
+ const viewports = document.querySelectorAll(".viewport[data-viewport-name]");
182
+ // Clean up existing drag listeners
183
+ dragCleanupFunctions.forEach((cleanup) => {
184
+ cleanup();
185
+ });
186
+ dragCleanupFunctions.clear();
187
+ // Remove existing label groups
188
+ const existingGroups = overlay.querySelectorAll(".viewport-label-group");
189
+ existingGroups.forEach((group) => {
190
+ group.remove();
191
+ });
192
+ // Create/update labels for each viewport
193
+ viewports.forEach((viewport) => {
194
+ const viewportElement = viewport;
195
+ const viewportName = viewportElement.getAttribute("data-viewport-name");
196
+ if (!viewportName)
197
+ return;
198
+ const bounds = getScreenBounds(viewportElement);
199
+ // Create group for this viewport label
200
+ const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
201
+ group.classList.add("viewport-label-group");
202
+ group.setAttribute("data-viewport-name", viewportName);
203
+ group.setAttribute("transform", `translate(${bounds.left}, ${bounds.top})`);
204
+ // Create text element
205
+ const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
206
+ text.classList.add("viewport-label-text");
207
+ text.setAttribute("x", "0");
208
+ text.setAttribute("y", "-8");
209
+ text.setAttribute("vector-effect", "non-scaling-stroke");
210
+ text.setAttribute("pointer-events", "auto");
211
+ text.textContent = viewportName;
212
+ group.appendChild(text);
213
+ overlay.appendChild(group);
214
+ // Setup drag functionality for this label
215
+ const cleanup = setupViewportLabelDrag(text, viewportElement, viewportName);
216
+ dragCleanupFunctions.set(viewportName, cleanup);
217
+ });
218
+ };
219
+
12
220
  const getCanvasWindowValue = (path, canvasName = "canvas") => {
13
221
  // biome-ignore lint/suspicious/noExplicitAny: global window extension
14
222
  const canvas = window[canvasName];
@@ -38,6 +246,8 @@
38
246
  if (nodeTools?.refreshHighlightFrame) {
39
247
  nodeTools.refreshHighlightFrame();
40
248
  }
249
+ // Refresh viewport labels
250
+ refreshViewportLabels();
41
251
  });
42
252
  observer.observe(transformLayer, {
43
253
  attributes: true,
@@ -45,8 +255,16 @@
45
255
  subtree: true,
46
256
  childList: true,
47
257
  });
258
+ // Handle window resize for viewport labels
259
+ const handleResize = () => {
260
+ refreshViewportLabels();
261
+ };
262
+ window.addEventListener("resize", handleResize);
263
+ // Initial refresh of viewport labels
264
+ refreshViewportLabels();
48
265
  function disconnect() {
49
266
  observer.disconnect();
267
+ window.removeEventListener("resize", handleResize);
50
268
  }
51
269
  return {
52
270
  disconnect,
@@ -61,15 +279,6 @@
61
279
  return resizeObserver;
62
280
  };
63
281
 
64
- function sendPostMessage(action, data) {
65
- window.parent.postMessage({
66
- source: "node-edit-utils",
67
- action,
68
- data,
69
- timestamp: Date.now(),
70
- }, "*");
71
- }
72
-
73
282
  const bindToWindow = (key, value) => {
74
283
  if (typeof window !== "undefined") {
75
284
  // biome-ignore lint/suspicious/noExplicitAny: global window extension requires flexibility
@@ -97,10 +306,6 @@
97
306
  }
98
307
  };
99
308
 
100
- const getCanvasContainer = () => {
101
- return document.querySelector(".canvas-container");
102
- };
103
-
104
309
  const clearHighlightFrame = () => {
105
310
  const canvasContainer = getCanvasContainer();
106
311
  const container = canvasContainer || document.body;
@@ -152,6 +357,21 @@
152
357
  return false;
153
358
  };
154
359
 
360
+ const isInsideViewport = (element) => {
361
+ let current = element;
362
+ while (current) {
363
+ if (current.classList.contains("viewport")) {
364
+ return true;
365
+ }
366
+ // Stop at node-provider to avoid checking beyond the editable area
367
+ if (current.getAttribute("data-role") === "node-provider") {
368
+ break;
369
+ }
370
+ current = current.parentElement;
371
+ }
372
+ return false;
373
+ };
374
+
155
375
  const targetSameCandidates = (cache, current) => cache.length === current.length && cache.every((el, i) => el === current[i]);
156
376
 
157
377
  let candidateCache = [];
@@ -164,7 +384,16 @@
164
384
  const clickThrough = event.metaKey || event.ctrlKey;
165
385
  const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) &&
166
386
  !element.classList.contains("select-none") &&
167
- !isInsideComponent(element));
387
+ !element.classList.contains("content-layer") &&
388
+ !element.classList.contains("resize-handle") &&
389
+ !element.classList.contains("resize-presets") &&
390
+ !isInsideComponent(element) &&
391
+ isInsideViewport(element));
392
+ console.log("candidates", candidates);
393
+ if (candidates.length === 0) {
394
+ lastSelectedNode = null;
395
+ return null;
396
+ }
168
397
  const editableNode = text.getEditableNode();
169
398
  if (editableNode && candidates.includes(editableNode)) {
170
399
  selectedNode = editableNode;
@@ -269,16 +498,6 @@
269
498
  createCornerHandle(group, 0, height, "handle-bottom-left", isInstance, isTextEdit);
270
499
  };
271
500
 
272
- function getScreenBounds(element) {
273
- const rect = element.getBoundingClientRect();
274
- return {
275
- top: rect.top,
276
- left: rect.left,
277
- width: rect.width,
278
- height: rect.height,
279
- };
280
- }
281
-
282
501
  const getComponentColor$1 = () => {
283
502
  return getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim() || "oklch(65.6% 0.241 354.308)";
284
503
  };
@@ -711,6 +930,7 @@
711
930
  // Accumulate mutations instead of replacing
712
931
  pendingMutations.push(...mutations);
713
932
  scheduleProcess();
933
+ console.log("refreshHighlightFrame in mutationObserver");
714
934
  refreshHighlightFrame(node, nodeProvider, canvasName);
715
935
  });
716
936
  return () => {
@@ -848,14 +1068,15 @@
848
1068
  checkNodeExists();
849
1069
  if (!document.contains(node))
850
1070
  return;
1071
+ console.log("refreshHighlightFrame in mutationObserver 2");
851
1072
  refreshHighlightFrame(node, nodeProvider, canvasName);
852
1073
  updateHighlightFrameVisibility(node);
853
1074
  });
854
1075
  mutationObserver.observe(node, {
855
1076
  attributes: true,
856
1077
  characterData: true,
857
- childList: true,
858
- subtree: true,
1078
+ //childList: true,
1079
+ //subtree: true,
859
1080
  });
860
1081
  // Also observe parent node to catch when this node is removed
861
1082
  const parentNode = node.parentElement;
@@ -884,15 +1105,22 @@
884
1105
  if (!document.contains(node))
885
1106
  return; // Exit early if node was removed
886
1107
  refreshHighlightFrame(node, nodeProvider, canvasName);
1108
+ console.log("refreshHighlightFrame in resizeObserver");
887
1109
  updateHighlightFrameVisibility(node);
888
1110
  });
889
1111
  }
890
1112
  selectedNode = node;
891
1113
  sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
892
1114
  highlightNode(node) ?? null;
893
- if (node && nodeProvider) {
894
- updateHighlightFrameVisibility(node);
895
- updateHighlightFrameVisibility(node);
1115
+ if (node) {
1116
+ highlightNode(node);
1117
+ if (nodeProvider) {
1118
+ updateHighlightFrameVisibility(node);
1119
+ updateHighlightFrameVisibility(node);
1120
+ }
1121
+ }
1122
+ else {
1123
+ clearHighlightFrame();
896
1124
  }
897
1125
  };
898
1126
  // Setup event listener
@@ -931,36 +1159,10 @@
931
1159
  return nodeTools;
932
1160
  };
933
1161
 
934
- // biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
935
- function withRAFThrottle(func) {
936
- let rafId = null;
937
- let lastArgs = null;
938
- const throttled = (...args) => {
939
- lastArgs = args;
940
- if (rafId === null) {
941
- rafId = requestAnimationFrame(() => {
942
- if (lastArgs) {
943
- func(...lastArgs);
944
- }
945
- rafId = null;
946
- lastArgs = null;
947
- });
948
- }
949
- };
950
- throttled.cleanup = () => {
951
- if (rafId !== null) {
952
- cancelAnimationFrame(rafId);
953
- rafId = null;
954
- lastArgs = null;
955
- }
956
- };
957
- return throttled;
958
- }
959
-
960
1162
  const DEFAULT_WIDTH = 400;
961
1163
  const RESIZE_CONFIG = {
962
- minWidth: 320,
963
- maxWidth: 1680,
1164
+ minWidth: 4,
1165
+ maxWidth: 2560,
964
1166
  };
965
1167
  const RESIZE_PRESETS = [
966
1168
  {
@@ -991,14 +1193,22 @@
991
1193
  ];
992
1194
 
993
1195
  const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
1196
+ const handleMouseLeave = (event) => {
1197
+ // Check if mouse is leaving the window/document
1198
+ if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
1199
+ blurResize();
1200
+ }
1201
+ };
994
1202
  resizeHandle.addEventListener("mousedown", startResize);
995
1203
  document.addEventListener("mousemove", handleResize);
996
1204
  document.addEventListener("mouseup", stopResize);
1205
+ document.addEventListener("mouseleave", handleMouseLeave);
997
1206
  window.addEventListener("blur", blurResize);
998
1207
  return () => {
999
1208
  resizeHandle.removeEventListener("mousedown", startResize);
1000
1209
  document.removeEventListener("mousemove", handleResize);
1001
1210
  document.removeEventListener("mouseup", stopResize);
1211
+ document.removeEventListener("mouseleave", handleMouseLeave);
1002
1212
  window.removeEventListener("blur", blurResize);
1003
1213
  };
1004
1214
  };
@@ -1058,7 +1268,7 @@
1058
1268
  updateActivePreset(container, width);
1059
1269
  };
1060
1270
 
1061
- const createViewport = (container) => {
1271
+ const createViewport = (container, initialWidth) => {
1062
1272
  const canvas = getCanvasContainer();
1063
1273
  // Remove any existing resize handle to prevent duplicates
1064
1274
  const existingHandle = container.querySelector(".resize-handle");
@@ -1066,7 +1276,8 @@
1066
1276
  existingHandle.remove();
1067
1277
  }
1068
1278
  const resizeHandle = createResizeHandle(container);
1069
- container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
1279
+ const width = initialWidth ?? DEFAULT_WIDTH;
1280
+ container.style.setProperty("--container-width", `${width}px`);
1070
1281
  createResizePresets(resizeHandle, container, updateWidth);
1071
1282
  let isDragging = false;
1072
1283
  let startX = 0;
@@ -1087,7 +1298,6 @@
1087
1298
  const width = calcWidth(event, startX, startWidth);
1088
1299
  updateWidth(container, width);
1089
1300
  };
1090
- const throttledHandleResize = withRAFThrottle(handleResize);
1091
1301
  const stopResize = (event) => {
1092
1302
  event.preventDefault();
1093
1303
  event.stopPropagation();
@@ -1097,18 +1307,33 @@
1097
1307
  isDragging = false;
1098
1308
  };
1099
1309
  const blurResize = () => {
1310
+ if (canvas) {
1311
+ canvas.style.cursor = "default";
1312
+ }
1100
1313
  isDragging = false;
1101
1314
  };
1102
- const removeListeners = setupEventListener(resizeHandle, startResize, throttledHandleResize, stopResize, blurResize);
1315
+ const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
1316
+ // Refresh viewport labels when viewport is created
1317
+ refreshViewportLabels();
1103
1318
  const cleanup = () => {
1104
1319
  isDragging = false;
1105
- throttledHandleResize?.cleanup();
1106
1320
  removeListeners();
1107
1321
  resizeHandle.remove();
1322
+ // Refresh labels after cleanup to remove this viewport's label if needed
1323
+ refreshViewportLabels();
1108
1324
  };
1109
1325
  return {
1110
1326
  setWidth: (width) => {
1111
1327
  updateWidth(container, width);
1328
+ refreshViewportLabels();
1329
+ // Refresh highlight frame when viewport width changes to update node positions
1330
+ // biome-ignore lint/suspicious/noExplicitAny: global window extension
1331
+ const nodeTools = window.nodeTools;
1332
+ const selectedNode = nodeTools?.getSelectedNode?.();
1333
+ const nodeProvider = document.querySelector('[data-role="node-provider"]');
1334
+ if (selectedNode && nodeProvider) {
1335
+ refreshHighlightFrame(selectedNode, nodeProvider);
1336
+ }
1112
1337
  },
1113
1338
  cleanup,
1114
1339
  };
@@ -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=!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()}}}});
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";function t(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const n=()=>document.querySelector(".canvas-container");let o=!1;const r=e=>{o=e};function s(e,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const i=e=>{const t=e.style.transform.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/);return t?{x:parseFloat(t[1]),y:parseFloat(t[2])}:{x:0,y:0}},a=(e,t,n)=>{let o=!1,a=0,d=0,l={x:0,y:0},c={x:0,y:0};const u=e.parentElement,m=e=>{e.preventDefault(),e.stopPropagation(),o=!0,r(!0),a=e.clientX,d=e.clientY,l=i(t),c=(e=>{const t=e.getAttribute("transform"),n=t?.match(/translate\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)/);return n?{x:parseFloat(n[1]),y:parseFloat(n[2])}:{x:0,y:0}})(u)},h=e=>{if(!o)return;const n=(()=>{const e=getComputedStyle(document.body).getPropertyValue("--zoom").trim();return e?parseFloat(e):1})(),r=e.clientX-a,s=e.clientY-d,i=r/n,m=s/n,h=l.x+i,g=l.y+m,p=c.x+r,v=c.y+s;u.setAttribute("transform",`translate(${p}, ${v})`),t.style.transform=`translate3d(${h}px, ${g}px, 0)`},g=e=>{if(!o)return;e.preventDefault(),e.stopPropagation(),o=!1,r(!1);const a=i(t),d=window.nodeTools;d?.refreshHighlightFrame&&d.refreshHighlightFrame(),s("viewport-position-changed",{viewportName:n,x:a.x,y:a.y})},p=()=>{o=!1,r(!1)};return e.addEventListener("mousedown",m),document.addEventListener("mousemove",h),document.addEventListener("mouseup",g),window.addEventListener("blur",p),()=>{e.removeEventListener("mousedown",m),document.removeEventListener("mousemove",h),document.removeEventListener("mouseup",g),window.removeEventListener("blur",p)}},d=new Map,l=()=>{if(o)return;const e=(()=>{const e=n()||document.body;let t=e.querySelector(".viewport-labels-overlay");if(!t){t=document.createElementNS("http://www.w3.org/2000/svg","svg"),t.classList.add("viewport-labels-overlay"),t.style.position="absolute",t.style.top="0",t.style.left="0",t.style.width="100vw",t.style.height="100vh",t.style.pointerEvents="none",t.style.zIndex="500";const n=document.documentElement.clientWidth||window.innerWidth,o=document.documentElement.clientHeight||window.innerHeight;t.setAttribute("width",n.toString()),t.setAttribute("height",o.toString()),e.appendChild(t)}return t})(),r=document.documentElement.clientWidth||window.innerWidth,s=document.documentElement.clientHeight||window.innerHeight;e.setAttribute("width",r.toString()),e.setAttribute("height",s.toString());const i=document.querySelectorAll(".viewport[data-viewport-name]");d.forEach(e=>{e()}),d.clear();e.querySelectorAll(".viewport-label-group").forEach(e=>{e.remove()}),i.forEach(n=>{const o=n,r=o.getAttribute("data-viewport-name");if(!r)return;const s=t(o),i=document.createElementNS("http://www.w3.org/2000/svg","g");i.classList.add("viewport-label-group"),i.setAttribute("data-viewport-name",r),i.setAttribute("transform",`translate(${s.left}, ${s.top})`);const l=document.createElementNS("http://www.w3.org/2000/svg","text");l.classList.add("viewport-label-text"),l.setAttribute("x","0"),l.setAttribute("y","-8"),l.setAttribute("vector-effect","non-scaling-stroke"),l.setAttribute("pointer-events","auto"),l.textContent=r,i.appendChild(l),e.appendChild(i);const c=a(l,o,r);d.set(r,c)})},c=(e,t="canvas")=>{const n=window[t];return e.reduce((e,t)=>e?.[t],n)};const u=()=>{const e=n()||document.body,t=e.querySelector(".highlight-frame-overlay");t&&t.remove();const o=e.querySelector(".highlight-frame-tools-wrapper");o&&o.remove()},m=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},h=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let g=[],p=0,v=null;const b=(e,t,n)=>{let o=null;const r=e.clientX,s=e.clientY,i=e.metaKey||e.ctrlKey,a=((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,s).filter(e=>!h.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none")&&!e.classList.contains("content-layer")&&!e.classList.contains("resize-handle")&&!e.classList.contains("resize-presets")&&!(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)&&(e=>{let t=e;for(;t;){if(t.classList.contains("viewport"))return!0;if("node-provider"===t.getAttribute("data-role"))break;t=t.parentElement}return!1})(e));if(console.log("candidates",a),0===a.length)return v=null,null;const d=n.getEditableNode();if(d&&a.includes(d))return o=d,v=o,o;if(i)return g=[],o=a[0],v&&v===o&&m(o,t,n),v=o,o;var l,c;c=a,(l=g).length===c.length&&l.every((e,t)=>e===c[t])?p<=a.length-2&&p++:p=0;return o=a[a.length-1-p],g=a,v&&v===o&&m(o,t,n),v=o,o},y=(e,t,n,o)=>{const r=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 u(),void o(null);o(b(e,t,n))})(n,e,o,t)},i=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",r),document.addEventListener("click",s),document.addEventListener("keydown",i),()=>{window.removeEventListener("message",r),document.removeEventListener("click",s),document.removeEventListener("keydown",i)}},f=e=>"true"===e.getAttribute("data-instance"),w=(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},E=(e,o=!1,r=!1)=>{const{top:s,left:i,width:a,height:d}=t(e),l=Math.max(a,3),c=document.createElementNS("http://www.w3.org/2000/svg","svg");c.classList.add("highlight-frame-overlay"),o&&c.classList.add("is-instance"),r&&c.classList.add("is-text-edit"),c.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),c.style.position="absolute",c.style.top="0",c.style.left="0",c.style.width="100vw",c.style.height="100vh",c.style.pointerEvents="none",c.style.zIndex="500";const u=document.documentElement.clientWidth||window.innerWidth,m=document.documentElement.clientHeight||window.innerHeight;c.setAttribute("width",u.toString()),c.setAttribute("height",m.toString());const h=document.createElementNS("http://www.w3.org/2000/svg","g");h.classList.add("highlight-frame-group"),h.setAttribute("transform",`translate(${i}, ${s})`);const g=document.createElementNS("http://www.w3.org/2000/svg","rect");g.setAttribute("x","0"),g.setAttribute("y","0"),g.setAttribute("width",l.toString()),g.setAttribute("height",d.toString()),g.setAttribute("vector-effect","non-scaling-stroke"),g.classList.add("highlight-frame-rect"),o?g.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):r&&g.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),h.appendChild(g),((e,t,n,o=!1,r=!1)=>{w(e,0,0,"handle-top-left",o,r),w(e,t,0,"handle-top-right",o,r),w(e,t,n,"handle-bottom-right",o,r),w(e,0,n,"handle-bottom-left",o,r)})(h,l,d,o,r),c.appendChild(h);const p=n();return p?p.appendChild(c):document.body.appendChild(c),c},L={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"},A=(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||L[r]||r;var i;n.textContent=(i=s)?i.charAt(0).toUpperCase()+i.slice(1):i,t.appendChild(n)})(e,r)};function x(){return(n()||document.body).querySelector(".highlight-frame-overlay")}const S=e=>{if(!e)return;const o=x(),r=n(),s=r?.querySelector(".highlight-frame-tools-wrapper")||document.body.querySelector(".highlight-frame-tools-wrapper");o&&o.remove(),s&&s.remove();const i=f(e),a="true"===e.contentEditable,d=E(e,i,a);"true"===e.contentEditable&&d.classList.add("is-editable");const{left:l,top:c,height:u}=t(e),m=c+u,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),i&&h.classList.add("is-instance"),a&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${l}px, ${m}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",A(e,h,i,a),r?r.appendChild(h):document.body.appendChild(h)},C=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",k=()=>getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)",N=(e,o,r="canvas")=>{const s=x();if(!s)return;const i=f(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 u=s.querySelector(".highlight-frame-group");if(!u)return;const m=u.querySelector("rect");if(!m)return;i?m.setAttribute("stroke",C()):a?m.setAttribute("stroke",k()):m.removeAttribute("stroke");const h=(n()||document.body).querySelector(".highlight-frame-tools-wrapper"),g=h?.querySelector(".node-tools"),p=c(["zoom","current"],r)??1,v=t(e),{top:b,left:y,width:w,height:E}=v,L=Math.max(w,3),A=b+E;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")),g&&(i?g.classList.add("is-instance"):g.classList.remove("is-instance"),a?g.classList.add("is-text-edit"):g.classList.remove("is-text-edit")),u.setAttribute("transform",`translate(${y}, ${b})`),m.setAttribute("width",L.toString()),m.setAttribute("height",E.toString());const S=u.querySelector(".handle-top-left"),N=u.querySelector(".handle-top-right"),q=u.querySelector(".handle-bottom-right"),F=u.querySelector(".handle-bottom-left");[S,N,q,F].forEach(e=>{e&&(i?e.setAttribute("stroke",C()):a?e.setAttribute("stroke",k()):e.removeAttribute("stroke"))}),S&&(S.setAttribute("x",(-3).toString()),S.setAttribute("y",(-3).toString())),N&&(N.setAttribute("x",(L-3).toString()),N.setAttribute("y",(-3).toString())),q&&(q.setAttribute("x",(L-3).toString()),q.setAttribute("y",(E-3).toString())),F&&(F.setAttribute("x",(-3).toString()),F.setAttribute("y",(E-3).toString())),h&&(h.style.transform=`translate(${y}px, ${A}px)`),p<=10?o.style.setProperty("--tool-opacity","1"):o.style.setProperty("--tool-opacity","0")},q=e=>{const t=x();if(!t)return;const o=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=o;const r=(n()||document.body).querySelector(".highlight-frame-tools-wrapper");r&&(r.style.display=o)},F=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)}},H=(e,t,n=!1)=>{if(!t.some(e=>"characterData"===e.type||"childList"===e.type&&(e.addedNodes.length>0||e.removedNodes.length>0))&&!n)return;const o=e.textContent??"",r=e.getAttribute("data-node-id");console.log("textContentChanged",o,n),s("textContentChanged",{nodeId:r,textContent:o,final:n})},P=(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=[],H(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(),console.log("refreshHighlightFrame in mutationObserver"),N(e,t,n)});return()=>{a.disconnect(),null!==r&&(cancelAnimationFrame(r),r=null),null!==s&&(cancelAnimationFrame(s),s=null),o=[]}},z=(e="canvas")=>{let t=null,n=!1,o=null;const r=()=>{if(n||!t)return;n=!0;const r=t;var s;H(r,[],!0),(s=r).contentEditable="false",s.classList.remove("is-editable"),s.style.outline="none",((e="canvas")=>{const t=c(["keyboard","disableTextEditMode"],e);t?.()})(e),o?.(),t=null,n=!1};return{enableEditMode:(n,s)=>{if(t===n)return;t&&t!==n&&r();const i=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(n);i&&(t=n,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(n),((e="canvas")=>{const t=c(["keyboard","enableTextEditMode"],e);t?.()})(e),o=((e,t,n,o="canvas")=>{if(!t)return()=>{};e.addEventListener("blur",n);const r=F(e),s=P(e,t,o);return()=>{e.removeEventListener("blur",n),r(),s?.()}})(n,s,r,e))},blurEditMode:r,getEditableNode:()=>t,isEditing:()=>null!==t}},M=4,$=2560,T=[{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"}],V=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(M,Math.min($,n))})(n,(e.clientX-t)/o);return r},D=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<T.length&&(T[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(e="canvas"){const t=document.querySelector(".transform-layer");if(!t)return{disconnect:()=>{}};const n=new MutationObserver(()=>{((e="canvas")=>{const t=c(["zoom","current"],e)??1;document.body.style.setProperty("--zoom",t.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/t).toFixed(3)),document.body.dataset.zoom=t.toFixed(5),document.body.dataset.strokeWidth=(2/t).toFixed(3)})(e);const t=window.nodeTools;t?.refreshHighlightFrame&&t.refreshHighlightFrame(),l()});n.observe(t,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0});const o=()=>{l()};return window.addEventListener("resize",o),l(),{disconnect:function(){n.disconnect(),window.removeEventListener("resize",o)}}},e.createNodeTools=(e,t="canvas")=>{const n=e;let o=null,r=null,i=null,a=null;const d=z(t),l=e=>{if(a!==e){if(d.isEditing()){const t=d.getEditableNode();t&&t!==e&&d.blurEditMode()}if(o?.disconnect(),r?.disconnect(),i?.disconnect(),e&&n){const d=()=>{if(!document.contains(e))return u(),a=null,o?.disconnect(),r?.disconnect(),i?.disconnect(),void s("selectedNodeChanged",null)};r=new MutationObserver(()=>{d(),document.contains(e)&&(console.log("refreshHighlightFrame in mutationObserver 2"),N(e,n,t),q(e))}),r.observe(e,{attributes:!0,characterData:!0});const l=e.parentElement;l&&(i=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 d()}),i.observe(l,{childList:!0,subtree:!1})),o=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{d(),document.contains(e)&&(N(e,n,t),console.log("refreshHighlightFrame in resizeObserver"),q(e))})}a=e,s("selectedNodeChanged",e?.getAttribute("data-node-id")??null),S(e),e?(S(e),n&&(q(e),q(e))):u()}},c=y(n,l,()=>{d.isEditing()&&d.blurEditMode(),a&&n&&(u(),a=null,o?.disconnect(),r?.disconnect(),i?.disconnect())},d),m={selectNode:l,getSelectedNode:()=>a,refreshHighlightFrame:()=>{a&&n&&(N(a,n,t),q(a))},clearSelectedNode:()=>{u(),a=null,o?.disconnect(),r?.disconnect(),i?.disconnect()},getEditableNode:()=>d.getEditableNode(),cleanup:()=>{c(),o?.disconnect(),r?.disconnect(),i?.disconnect(),d.blurEditMode(),u(),a=null,s("selectedNodeChanged",null)}};var h,g;return h="nodeTools",g=m,"undefined"!=typeof window&&(window[h]=g),m},e.createViewport=(e,t)=>{const o=n(),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",T.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,D);let a=!1,d=0,c=0;const u=((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,c=e.offsetWidth},t=>{if(!a)return;o&&(o.style.cursor="ew-resize");const n=V(t,d,c);D(e,n)},e=>{e.preventDefault(),e.stopPropagation(),o&&(o.style.cursor="default"),a=!1},()=>{o&&(o.style.cursor="default"),a=!1});l();return{setWidth:t=>{D(e,t),l();const n=window.nodeTools,o=n?.getSelectedNode?.(),r=document.querySelector('[data-role="node-provider"]');o&&r&&N(o,r)},cleanup:()=>{a=!1,u(),s.remove(),l()}}}});
package/dist/styles.css CHANGED
@@ -1 +1 @@
1
- :root{--primary-color:oklch(0.6235 0.22 294);--uncode-color:oklch(45.7% 0.24 277.023);--component-color:oklch(65.6% 0.241 354.308);--text-edit-color:oklch(62.3% 0.214 259.815);--text-edit-selection-color:oklch(62.3% 0.214 259.815/0.2);--handle-color:oklch(55.2% 0.016 285.938);--handle-color-transparent:oklch(55.2% 0.016 285.938/0.7);--primary-color-selection:oklch(0.59 0.18 294/0.3);--text-color-white:#fff;--spacing-2xs:0.1875rem;--spacing-xs:0.25rem;--spacing-sm:0.375rem;--spacing-md:0.5rem;--spacing-lg:1.25rem;--transition-fast:0.1s ease-in-out;--transition-medium:0.2s ease-in-out;--z-index-high:10000;--z-index-highlight:500;--z-index-medium:1000;--letter-spacing:0.02em;--font-family-primary:"Manrope",sans-serif}.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;contain:layout style;display:flex;gap:var(--spacing-xs);justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:var(--spacing-2xs);position:absolute;top:0;transform:translate3d(0,100%,0);transform-origin:bottom center;transition:opacity var(--transition-fast);z-index:var(--z-index-high)}.highlight-frame-overlay{contain:layout style paint;height:100vh;inset:0;overflow:visible;pointer-events:none;position:absolute;width:100vw;will-change:transform;z-index:var(--z-index-highlight)}.highlight-frame-rect{fill:none;stroke:var(--primary-color);stroke-width:2px}.highlight-frame-overlay.is-instance .highlight-frame-rect{stroke:var(--component-color)}.highlight-frame-overlay.is-text-edit .highlight-frame-rect{stroke:var(--text-edit-color)}.highlight-frame-handle{fill:#fff;stroke:var(--primary-color);stroke-width:1px;cursor:nwse-resize;pointer-events:auto}.highlight-frame-overlay.is-instance .highlight-frame-handle{stroke:var(--component-color)}.highlight-frame-overlay.is-text-edit .highlight-frame-handle{stroke:var(--text-edit-color)}.highlight-frame-handle.handle-top-left{cursor:nwse-resize}.highlight-frame-handle.handle-top-right{cursor:nesw-resize}.highlight-frame-handle.handle-bottom-right{cursor:nwse-resize}.highlight-frame-handle.handle-bottom-left{cursor:nesw-resize}.highlight-frame-tools-wrapper{contain:layout style;left:0;pointer-events:none;position:absolute;top:0;z-index:var(--z-index-highlight)}.tag-label{align-items:center;background-color:var(--primary-color);border-radius:var(--spacing-sm);color:var(--text-color-white);display:flex;font-size:.575rem;font-weight:500;height:1rem;justify-content:center;line-height:1.4;padding:.5625rem var(--spacing-sm);white-space:nowrap}.highlight-frame-tools-wrapper.is-instance .tag-label,.node-tools.is-instance .tag-label{background-color:var(--component-color)}.highlight-frame-tools-wrapper.is-text-edit .tag-label,.node-tools.is-text-edit .tag-label{background-color:var(--text-edit-color)}.viewport{position:relative;width:var(--container-width)}.resize-handle{align-items:center;cursor:ew-resize;display:flex;height:40px;justify-content:center;opacity:0;pointer-events:auto;position:absolute;right:-12px;top:var(--spacing-lg);transform:scale(calc(1/var(--zoom)));transform-origin:top left;transition:opacity var(--transition-fast),visibility var(--transition-fast);visibility:hidden;width:12px;z-index:var(--z-index-high)}.viewport:hover .resize-handle{opacity:1;visibility:visible}.resize-handle:before{background:var(--handle-color);border-radius:4px;content:"";height:32px;position:relative;right:0;top:0;transition:transform var(--transition-medium);width:3px}.resize-handle:hover:before{transform:scaleY(1.3)}.resize-presets{display:flex;flex-direction:column;font-family:var(--font-family-primary);gap:var(--spacing-xs);left:0;letter-spacing:var(--letter-spacing);opacity:0;padding-left:var(--spacing-lg);position:absolute;top:0;transition:visibility var(--transition-fast),opacity var(--transition-fast);visibility:hidden}.resize-handle:hover .resize-presets{opacity:1;visibility:visible}.resize-preset-button{text-wrap:nowrap;backdrop-filter:blur(8px);background:var(--handle-color-transparent);border:none;border-radius:var(--spacing-md);color:var(--text-color-white);cursor:pointer;font-size:.625rem;padding:var(--spacing-xs) var(--spacing-md);text-align:left;transition:background var(--transition-fast);width:fit-content;&:hover{background:var(--handle-color)}&.is-active{background:var(--uncode-color)}}.is-editable{outline:none;user-select:text;&::selection{background:var(--text-edit-selection-color)}&::-moz-selection{background:var(--text-edit-selection-color)}&::-webkit-selection{background:var(--text-edit-selection-color)}}
1
+ :root{--primary-color:oklch(0.6235 0.22 294);--uncode-color:oklch(45.7% 0.24 277.023);--component-color:oklch(65.6% 0.241 354.308);--text-edit-color:oklch(62.3% 0.214 259.815);--text-edit-selection-color:oklch(62.3% 0.214 259.815/0.2);--handle-color:oklch(55.2% 0.016 285.938);--handle-color-transparent:oklch(55.2% 0.016 285.938/0.7);--primary-color-selection:oklch(0.59 0.18 294/0.3);--text-color-white:#fff;--spacing-2xs:0.1875rem;--spacing-xs:0.25rem;--spacing-sm:0.375rem;--spacing-md:0.5rem;--spacing-lg:1.25rem;--transition-fast:0.1s ease-in-out;--transition-medium:0.2s ease-in-out;--z-index-high:10000;--z-index-highlight:500;--z-index-medium:1000;--letter-spacing:0.02em;--font-family-primary:"Manrope",sans-serif}.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;contain:layout style;display:flex;gap:var(--spacing-xs);justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:var(--spacing-2xs);position:absolute;top:0;transform:translate3d(0,100%,0);transform-origin:bottom center;transition:opacity var(--transition-fast);z-index:var(--z-index-high)}.highlight-frame-overlay,.viewport-labels-overlay{contain:layout style paint;height:100vh;inset:0;overflow:visible;pointer-events:none;position:absolute;width:100vw;will-change:transform;z-index:var(--z-index-highlight)}.viewport-label-text{fill:oklch(.5 0 0);font-family:var(--font-family-primary);font-size:.6875rem;font-weight:500;letter-spacing:var(--letter-spacing);pointer-events:auto;user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none}@media (prefers-color-scheme:dark){.viewport-label-text{fill:oklch(.3 0 0)}.viewport-label-text:hover{fill:oklch(0 0 0)}}@media (prefers-color-scheme:light){.viewport-label-text{fill:oklch(.7 0 0)}.viewport-label-text:hover{fill:oklch(.3 0 0)}}.highlight-frame-rect{fill:none;stroke:var(--primary-color);stroke-width:2px}.highlight-frame-overlay.is-instance .highlight-frame-rect{stroke:var(--component-color)}.highlight-frame-overlay.is-text-edit .highlight-frame-rect{stroke:var(--text-edit-color)}.highlight-frame-handle{fill:#fff;stroke:var(--primary-color);stroke-width:1px;cursor:nwse-resize;pointer-events:auto}.highlight-frame-overlay.is-instance .highlight-frame-handle{stroke:var(--component-color)}.highlight-frame-overlay.is-text-edit .highlight-frame-handle{stroke:var(--text-edit-color)}.highlight-frame-handle.handle-top-left{cursor:nwse-resize}.highlight-frame-handle.handle-top-right{cursor:nesw-resize}.highlight-frame-handle.handle-bottom-right{cursor:nwse-resize}.highlight-frame-handle.handle-bottom-left{cursor:nesw-resize}.highlight-frame-tools-wrapper{contain:layout style;left:0;pointer-events:none;position:absolute;top:0;z-index:var(--z-index-highlight)}.tag-label{align-items:center;background-color:var(--primary-color);border-radius:var(--spacing-sm);color:var(--text-color-white);display:flex;font-size:.575rem;font-weight:500;height:1rem;justify-content:center;line-height:1.4;padding:.5625rem var(--spacing-sm);white-space:nowrap}.highlight-frame-tools-wrapper.is-instance .tag-label,.node-tools.is-instance .tag-label{background-color:var(--component-color)}.highlight-frame-tools-wrapper.is-text-edit .tag-label,.node-tools.is-text-edit .tag-label{background-color:var(--text-edit-color)}.viewport{left:0;position:absolute;top:0;width:var(--container-width)}.resize-handle{align-items:center;cursor:ew-resize;display:flex;height:40px;justify-content:center;opacity:0;pointer-events:auto;position:absolute;right:-12px;top:var(--spacing-lg);transform:scale(calc(1/var(--zoom)));transform-origin:top left;transition:opacity var(--transition-fast),visibility var(--transition-fast);visibility:hidden;width:12px;z-index:var(--z-index-high)}.viewport:hover .resize-handle{opacity:1;visibility:visible}.resize-handle:before{background:var(--handle-color);border-radius:4px;content:"";height:32px;position:relative;right:0;top:0;transition:transform var(--transition-medium);width:3px}.resize-handle:hover:before{transform:scaleY(1.3)}.resize-presets{display:flex;flex-direction:column;font-family:var(--font-family-primary);gap:var(--spacing-xs);left:0;letter-spacing:var(--letter-spacing);opacity:0;padding-left:var(--spacing-lg);position:absolute;top:0;transition:visibility var(--transition-fast),opacity var(--transition-fast);visibility:hidden}.resize-handle:hover .resize-presets{opacity:1;visibility:visible}.resize-preset-button{text-wrap:nowrap;backdrop-filter:blur(8px);background:var(--handle-color-transparent);border:none;border-radius:var(--spacing-md);color:var(--text-color-white);cursor:pointer;font-size:.625rem;padding:var(--spacing-xs) var(--spacing-md);text-align:left;transition:background var(--transition-fast);width:fit-content;&:hover{background:var(--handle-color)}&.is-active{background:var(--uncode-color)}}.is-editable{outline:none;user-select:text;&::selection{background:var(--text-edit-selection-color)}&::-moz-selection{background:var(--text-edit-selection-color)}&::-webkit-selection{background:var(--text-edit-selection-color)}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-edit-utils/core",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Utilities for editing nodes in a dom tree.",
5
5
  "type": "module",
6
6
  "main": "dist/node-edit-utils.cjs.js",
@@ -1,3 +1,4 @@
1
+ import { refreshViewportLabels } from "../viewport/label/refreshViewportLabels";
1
2
  import { applyCanvasState } from "./helpers/applyCanvasState";
2
3
  import type { CanvasObserver } from "./types";
3
4
 
@@ -20,6 +21,9 @@ export function createCanvasObserver(canvasName: string = "canvas"): CanvasObser
20
21
  if (nodeTools?.refreshHighlightFrame) {
21
22
  nodeTools.refreshHighlightFrame();
22
23
  }
24
+
25
+ // Refresh viewport labels
26
+ refreshViewportLabels();
23
27
  });
24
28
 
25
29
  observer.observe(transformLayer, {
@@ -29,8 +33,18 @@ export function createCanvasObserver(canvasName: string = "canvas"): CanvasObser
29
33
  childList: true,
30
34
  });
31
35
 
36
+ // Handle window resize for viewport labels
37
+ const handleResize = (): void => {
38
+ refreshViewportLabels();
39
+ };
40
+ window.addEventListener("resize", handleResize);
41
+
42
+ // Initial refresh of viewport labels
43
+ refreshViewportLabels();
44
+
32
45
  function disconnect(): void {
33
46
  observer.disconnect();
47
+ window.removeEventListener("resize", handleResize);
34
48
  }
35
49
 
36
50
  return {