@node-edit-utils/core 2.3.1 → 2.3.3
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.
- package/dist/lib/canvas/helpers/getCanvasContainerOrBody.d.ts +1 -0
- package/dist/lib/canvas/helpers/getCanvasWindowValue.d.ts +1 -1
- package/dist/lib/helpers/adjustForZoom.d.ts +1 -0
- package/dist/lib/helpers/createDragHandler.d.ts +69 -0
- package/dist/lib/helpers/getNodeProvider.d.ts +1 -0
- package/dist/lib/helpers/getNodeTools.d.ts +2 -0
- package/dist/lib/helpers/getViewportDimensions.d.ts +4 -0
- package/dist/lib/helpers/index.d.ts +9 -1
- package/dist/lib/helpers/parseTransform.d.ts +8 -0
- package/dist/lib/helpers/toggleClass.d.ts +1 -0
- package/dist/lib/node-tools/select/helpers/isInsideViewport.d.ts +1 -0
- package/dist/lib/viewport/constants.d.ts +2 -2
- package/dist/lib/viewport/label/getViewportLabelsOverlay.d.ts +1 -0
- package/dist/lib/viewport/label/helpers/getLabelPosition.d.ts +4 -0
- package/dist/lib/viewport/label/helpers/getTransformValues.d.ts +4 -0
- package/dist/lib/viewport/label/helpers/getZoomValue.d.ts +1 -0
- package/dist/lib/viewport/label/helpers/selectFirstViewportNode.d.ts +5 -0
- package/dist/lib/viewport/label/index.d.ts +4 -0
- package/dist/lib/viewport/label/isViewportLabelDragging.d.ts +2 -0
- package/dist/lib/viewport/label/refreshViewportLabels.d.ts +1 -0
- package/dist/lib/viewport/label/setupViewportLabelDrag.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +437 -172
- package/dist/node-edit-utils.esm.js +437 -172
- package/dist/node-edit-utils.umd.js +437 -172
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/lib/canvas/createCanvasObserver.ts +16 -2
- package/src/lib/canvas/helpers/getCanvasContainerOrBody.ts +6 -0
- package/src/lib/canvas/helpers/getCanvasWindowValue.ts +2 -3
- package/src/lib/helpers/adjustForZoom.ts +4 -0
- package/src/lib/helpers/createDragHandler.ts +171 -0
- package/src/lib/helpers/getNodeProvider.ts +4 -0
- package/src/lib/helpers/getNodeTools.ts +6 -0
- package/src/lib/helpers/getViewportDimensions.ts +7 -0
- package/src/lib/helpers/index.ts +9 -1
- package/src/lib/helpers/parseTransform.ts +9 -0
- package/src/lib/helpers/toggleClass.ts +9 -0
- package/src/lib/node-tools/createNodeTools.ts +11 -5
- package/src/lib/node-tools/highlight/clearHighlightFrame.ts +2 -3
- package/src/lib/node-tools/highlight/createHighlightFrame.ts +5 -9
- package/src/lib/node-tools/highlight/createToolsContainer.ts +3 -6
- package/src/lib/node-tools/highlight/helpers/getElementBounds.ts +6 -5
- package/src/lib/node-tools/highlight/helpers/getHighlightFrameElement.ts +2 -3
- package/src/lib/node-tools/highlight/highlightNode.ts +7 -15
- package/src/lib/node-tools/highlight/refreshHighlightFrame.ts +12 -42
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.ts +2 -3
- package/src/lib/node-tools/select/helpers/isInsideViewport.ts +19 -0
- package/src/lib/node-tools/select/selectNode.ts +13 -1
- package/src/lib/node-tools/text/events/setupMutationObserver.ts +1 -0
- package/src/lib/styles/styles.css +48 -1
- package/src/lib/viewport/constants.ts +2 -2
- package/src/lib/viewport/createViewport.ts +56 -41
- package/src/lib/viewport/label/getViewportLabelsOverlay.ts +32 -0
- package/src/lib/viewport/label/helpers/getLabelPosition.ts +6 -0
- package/src/lib/viewport/label/helpers/getTransformValues.ts +6 -0
- package/src/lib/viewport/label/helpers/getZoomValue.ts +4 -0
- package/src/lib/viewport/label/helpers/selectFirstViewportNode.ts +18 -0
- package/src/lib/viewport/label/index.ts +4 -0
- package/src/lib/viewport/label/isViewportLabelDragging.ts +9 -0
- package/src/lib/viewport/label/refreshViewportLabels.ts +69 -0
- package/src/lib/viewport/label/setupViewportLabelDrag.ts +70 -0
- package/src/lib/window/bindToWindow.ts +1 -2
|
@@ -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.
|
|
4
|
+
* @version 2.3.3
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -9,8 +9,310 @@
|
|
|
9
9
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MarkupCanvas = {}));
|
|
10
10
|
})(this, (function (exports) { 'use strict';
|
|
11
11
|
|
|
12
|
+
const getNodeTools = () => {
|
|
13
|
+
return window.nodeTools;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const getViewportDimensions = () => {
|
|
17
|
+
return {
|
|
18
|
+
width: document.documentElement.clientWidth || window.innerWidth,
|
|
19
|
+
height: document.documentElement.clientHeight || window.innerHeight,
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function getScreenBounds(element) {
|
|
24
|
+
const rect = element.getBoundingClientRect();
|
|
25
|
+
return {
|
|
26
|
+
top: rect.top,
|
|
27
|
+
left: rect.left,
|
|
28
|
+
width: rect.width,
|
|
29
|
+
height: rect.height,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const getCanvasContainer = () => {
|
|
34
|
+
return document.querySelector(".canvas-container");
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const getCanvasContainerOrBody = () => {
|
|
38
|
+
return getCanvasContainer() || document.body;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const getViewportLabelsOverlay = () => {
|
|
42
|
+
const container = getCanvasContainerOrBody();
|
|
43
|
+
// Check if overlay already exists
|
|
44
|
+
let overlay = container.querySelector(".viewport-labels-overlay");
|
|
45
|
+
if (!overlay) {
|
|
46
|
+
// Create new SVG overlay
|
|
47
|
+
overlay = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
48
|
+
overlay.classList.add("viewport-labels-overlay");
|
|
49
|
+
// Set fixed positioning
|
|
50
|
+
overlay.style.position = "absolute";
|
|
51
|
+
overlay.style.top = "0";
|
|
52
|
+
overlay.style.left = "0";
|
|
53
|
+
overlay.style.width = "100vw";
|
|
54
|
+
overlay.style.height = "100vh";
|
|
55
|
+
overlay.style.pointerEvents = "none";
|
|
56
|
+
overlay.style.zIndex = "500";
|
|
57
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
58
|
+
overlay.setAttribute("width", viewportWidth.toString());
|
|
59
|
+
overlay.setAttribute("height", viewportHeight.toString());
|
|
60
|
+
container.appendChild(overlay);
|
|
61
|
+
}
|
|
62
|
+
return overlay;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Global flag to prevent refreshViewportLabels during drag
|
|
66
|
+
let globalIsDragging = false;
|
|
67
|
+
const isViewportLabelDragging = () => globalIsDragging;
|
|
68
|
+
const setViewportLabelDragging = (isDragging) => {
|
|
69
|
+
globalIsDragging = isDragging;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a reusable drag handler for mouse drag operations
|
|
74
|
+
* @param element - Element that triggers the drag (mousedown listener attached here)
|
|
75
|
+
* @param callbacks - Callbacks for drag lifecycle events
|
|
76
|
+
* @param options - Configuration options
|
|
77
|
+
* @returns Cleanup function to remove all event listeners
|
|
78
|
+
*/
|
|
79
|
+
function createDragHandler(element, callbacks, options = {}) {
|
|
80
|
+
const { preventDefault = true, stopPropagation = true } = options;
|
|
81
|
+
const state = {
|
|
82
|
+
isDragging: false,
|
|
83
|
+
hasDragged: false,
|
|
84
|
+
startX: 0,
|
|
85
|
+
startY: 0,
|
|
86
|
+
};
|
|
87
|
+
const startDrag = (event) => {
|
|
88
|
+
if (preventDefault) {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
}
|
|
91
|
+
if (stopPropagation) {
|
|
92
|
+
event.stopPropagation();
|
|
93
|
+
}
|
|
94
|
+
state.isDragging = true;
|
|
95
|
+
state.hasDragged = false;
|
|
96
|
+
state.startX = event.clientX;
|
|
97
|
+
state.startY = event.clientY;
|
|
98
|
+
callbacks.onStart?.(event, state);
|
|
99
|
+
};
|
|
100
|
+
const handleDrag = (event) => {
|
|
101
|
+
if (!state.isDragging)
|
|
102
|
+
return;
|
|
103
|
+
const deltaX = event.clientX - state.startX;
|
|
104
|
+
const deltaY = event.clientY - state.startY;
|
|
105
|
+
state.hasDragged = true;
|
|
106
|
+
callbacks.onDrag(event, {
|
|
107
|
+
...state,
|
|
108
|
+
deltaX,
|
|
109
|
+
deltaY,
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
const stopDrag = (event) => {
|
|
113
|
+
if (!state.isDragging)
|
|
114
|
+
return;
|
|
115
|
+
if (preventDefault) {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
}
|
|
118
|
+
if (stopPropagation) {
|
|
119
|
+
event.stopPropagation();
|
|
120
|
+
}
|
|
121
|
+
state.isDragging = false;
|
|
122
|
+
callbacks.onStop?.(event, state);
|
|
123
|
+
};
|
|
124
|
+
const cancelDrag = () => {
|
|
125
|
+
if (!state.isDragging)
|
|
126
|
+
return;
|
|
127
|
+
state.isDragging = false;
|
|
128
|
+
callbacks.onCancel?.(state);
|
|
129
|
+
};
|
|
130
|
+
const preventClick = (event) => {
|
|
131
|
+
if (preventDefault) {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
}
|
|
134
|
+
if (stopPropagation) {
|
|
135
|
+
event.stopPropagation();
|
|
136
|
+
}
|
|
137
|
+
callbacks.onPreventClick?.(event, state);
|
|
138
|
+
// Reset hasDragged flag after handling the click
|
|
139
|
+
if (state.hasDragged) {
|
|
140
|
+
state.hasDragged = false;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
// Attach event listeners
|
|
144
|
+
element.addEventListener("mousedown", startDrag);
|
|
145
|
+
if (callbacks.onPreventClick) {
|
|
146
|
+
element.addEventListener("click", preventClick);
|
|
147
|
+
}
|
|
148
|
+
document.addEventListener("mousemove", handleDrag);
|
|
149
|
+
document.addEventListener("mouseup", stopDrag);
|
|
150
|
+
window.addEventListener("blur", cancelDrag);
|
|
151
|
+
// Return cleanup function
|
|
152
|
+
return () => {
|
|
153
|
+
element.removeEventListener("mousedown", startDrag);
|
|
154
|
+
if (callbacks.onPreventClick) {
|
|
155
|
+
element.removeEventListener("click", preventClick);
|
|
156
|
+
}
|
|
157
|
+
document.removeEventListener("mousemove", handleDrag);
|
|
158
|
+
document.removeEventListener("mouseup", stopDrag);
|
|
159
|
+
window.removeEventListener("blur", cancelDrag);
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function sendPostMessage(action, data) {
|
|
164
|
+
window.parent.postMessage({
|
|
165
|
+
source: "node-edit-utils",
|
|
166
|
+
action,
|
|
167
|
+
data,
|
|
168
|
+
timestamp: Date.now(),
|
|
169
|
+
}, "*");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const parseTransform3d = (transform) => {
|
|
173
|
+
const match = transform.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/);
|
|
174
|
+
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
|
175
|
+
};
|
|
176
|
+
const parseTransform2d = (transform) => {
|
|
177
|
+
const match = transform?.match(/translate\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)/);
|
|
178
|
+
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const getLabelPosition = (labelGroup) => {
|
|
182
|
+
const transform = labelGroup.getAttribute("transform");
|
|
183
|
+
return parseTransform2d(transform);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const getTransformValues = (element) => {
|
|
187
|
+
const style = element.style.transform;
|
|
188
|
+
return parseTransform3d(style);
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const getZoomValue = () => {
|
|
192
|
+
const zoomValue = getComputedStyle(document.body).getPropertyValue("--zoom").trim();
|
|
193
|
+
return zoomValue ? parseFloat(zoomValue) : 1;
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Selects the first child node inside a viewport element.
|
|
198
|
+
* Skips the resize-handle element if present.
|
|
199
|
+
*/
|
|
200
|
+
const selectFirstViewportNode = (viewportElement) => {
|
|
201
|
+
const firstChild = Array.from(viewportElement.children).find((child) => !child.classList.contains("resize-handle"));
|
|
202
|
+
if (firstChild) {
|
|
203
|
+
const nodeTools = getNodeTools();
|
|
204
|
+
if (nodeTools?.selectNode) {
|
|
205
|
+
nodeTools.selectNode(firstChild);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const setupViewportLabelDrag = (labelElement, viewportElement, viewportName) => {
|
|
211
|
+
// Get the parent group element that contains the label
|
|
212
|
+
const labelGroup = labelElement.parentElement;
|
|
213
|
+
// Track initial positions for calculations
|
|
214
|
+
let initialTransform = { x: 0, y: 0 };
|
|
215
|
+
let initialLabelPosition = { x: 0, y: 0 };
|
|
216
|
+
return createDragHandler(labelElement, {
|
|
217
|
+
onStart: () => {
|
|
218
|
+
setViewportLabelDragging(true);
|
|
219
|
+
initialTransform = getTransformValues(viewportElement);
|
|
220
|
+
initialLabelPosition = getLabelPosition(labelGroup);
|
|
221
|
+
selectFirstViewportNode(viewportElement);
|
|
222
|
+
},
|
|
223
|
+
onDrag: (_event, { deltaX, deltaY }) => {
|
|
224
|
+
const zoom = getZoomValue();
|
|
225
|
+
// Adjust delta for zoom level (viewport is in canvas space)
|
|
226
|
+
const deltaXZoomed = deltaX / zoom;
|
|
227
|
+
const deltaYZoomed = deltaY / zoom;
|
|
228
|
+
// Calculate new positions
|
|
229
|
+
const newX = initialTransform.x + deltaXZoomed;
|
|
230
|
+
const newY = initialTransform.y + deltaYZoomed;
|
|
231
|
+
// Update label position with raw delta (labels are in screen space)
|
|
232
|
+
const newLabelX = initialLabelPosition.x + deltaX;
|
|
233
|
+
const newLabelY = initialLabelPosition.y + deltaY;
|
|
234
|
+
labelGroup.setAttribute("transform", `translate(${newLabelX}, ${newLabelY})`);
|
|
235
|
+
// Update viewport position with zoom-adjusted delta
|
|
236
|
+
viewportElement.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;
|
|
237
|
+
},
|
|
238
|
+
onStop: (_event, { hasDragged }) => {
|
|
239
|
+
setViewportLabelDragging(false);
|
|
240
|
+
// If it was a drag, handle drag completion
|
|
241
|
+
if (hasDragged) {
|
|
242
|
+
const finalTransform = getTransformValues(viewportElement);
|
|
243
|
+
// Trigger refresh after drag completes to update highlight frame and labels
|
|
244
|
+
const nodeTools = getNodeTools();
|
|
245
|
+
if (nodeTools?.refreshHighlightFrame) {
|
|
246
|
+
nodeTools.refreshHighlightFrame();
|
|
247
|
+
}
|
|
248
|
+
// Notify parent about the new position
|
|
249
|
+
sendPostMessage("viewport-position-changed", {
|
|
250
|
+
viewportName,
|
|
251
|
+
x: finalTransform.x,
|
|
252
|
+
y: finalTransform.y,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
onCancel: () => {
|
|
257
|
+
setViewportLabelDragging(false);
|
|
258
|
+
},
|
|
259
|
+
onPreventClick: () => { },
|
|
260
|
+
});
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// Store cleanup functions for drag listeners
|
|
264
|
+
const dragCleanupFunctions = new Map();
|
|
265
|
+
const refreshViewportLabels = () => {
|
|
266
|
+
// Skip refresh if a viewport label is being dragged
|
|
267
|
+
if (isViewportLabelDragging()) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
const overlay = getViewportLabelsOverlay();
|
|
271
|
+
// Update SVG dimensions to match current viewport
|
|
272
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
273
|
+
overlay.setAttribute("width", viewportWidth.toString());
|
|
274
|
+
overlay.setAttribute("height", viewportHeight.toString());
|
|
275
|
+
// Find all viewports with names
|
|
276
|
+
const viewports = document.querySelectorAll(".viewport[data-viewport-name]");
|
|
277
|
+
// Clean up existing drag listeners
|
|
278
|
+
dragCleanupFunctions.forEach((cleanup) => {
|
|
279
|
+
cleanup();
|
|
280
|
+
});
|
|
281
|
+
dragCleanupFunctions.clear();
|
|
282
|
+
// Remove existing label groups
|
|
283
|
+
const existingGroups = overlay.querySelectorAll(".viewport-label-group");
|
|
284
|
+
existingGroups.forEach((group) => {
|
|
285
|
+
group.remove();
|
|
286
|
+
});
|
|
287
|
+
// Create/update labels for each viewport
|
|
288
|
+
viewports.forEach((viewport) => {
|
|
289
|
+
const viewportElement = viewport;
|
|
290
|
+
const viewportName = viewportElement.getAttribute("data-viewport-name");
|
|
291
|
+
if (!viewportName)
|
|
292
|
+
return;
|
|
293
|
+
const bounds = getScreenBounds(viewportElement);
|
|
294
|
+
// Create group for this viewport label
|
|
295
|
+
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
296
|
+
group.classList.add("viewport-label-group");
|
|
297
|
+
group.setAttribute("data-viewport-name", viewportName);
|
|
298
|
+
group.setAttribute("transform", `translate(${bounds.left}, ${bounds.top})`);
|
|
299
|
+
// Create text element
|
|
300
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
301
|
+
text.classList.add("viewport-label-text");
|
|
302
|
+
text.setAttribute("x", "0");
|
|
303
|
+
text.setAttribute("y", "-8");
|
|
304
|
+
text.setAttribute("vector-effect", "non-scaling-stroke");
|
|
305
|
+
text.setAttribute("pointer-events", "auto");
|
|
306
|
+
text.textContent = viewportName;
|
|
307
|
+
group.appendChild(text);
|
|
308
|
+
overlay.appendChild(group);
|
|
309
|
+
// Setup drag functionality for this label
|
|
310
|
+
const cleanup = setupViewportLabelDrag(text, viewportElement, viewportName);
|
|
311
|
+
dragCleanupFunctions.set(viewportName, cleanup);
|
|
312
|
+
});
|
|
313
|
+
};
|
|
314
|
+
|
|
12
315
|
const getCanvasWindowValue = (path, canvasName = "canvas") => {
|
|
13
|
-
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
14
316
|
const canvas = window[canvasName];
|
|
15
317
|
return path.reduce((obj, prop) => obj?.[prop], canvas);
|
|
16
318
|
};
|
|
@@ -33,11 +335,12 @@
|
|
|
33
335
|
const observer = new MutationObserver(() => {
|
|
34
336
|
applyCanvasState(canvasName);
|
|
35
337
|
// Refresh highlight frame (throttled via withRAFThrottle)
|
|
36
|
-
|
|
37
|
-
const nodeTools = window.nodeTools;
|
|
338
|
+
const nodeTools = getNodeTools();
|
|
38
339
|
if (nodeTools?.refreshHighlightFrame) {
|
|
39
340
|
nodeTools.refreshHighlightFrame();
|
|
40
341
|
}
|
|
342
|
+
// Refresh viewport labels
|
|
343
|
+
refreshViewportLabels();
|
|
41
344
|
});
|
|
42
345
|
observer.observe(transformLayer, {
|
|
43
346
|
attributes: true,
|
|
@@ -45,8 +348,16 @@
|
|
|
45
348
|
subtree: true,
|
|
46
349
|
childList: true,
|
|
47
350
|
});
|
|
351
|
+
// Handle window resize for viewport labels
|
|
352
|
+
const handleResize = () => {
|
|
353
|
+
refreshViewportLabels();
|
|
354
|
+
};
|
|
355
|
+
window.addEventListener("resize", handleResize);
|
|
356
|
+
// Initial refresh of viewport labels
|
|
357
|
+
refreshViewportLabels();
|
|
48
358
|
function disconnect() {
|
|
49
359
|
observer.disconnect();
|
|
360
|
+
window.removeEventListener("resize", handleResize);
|
|
50
361
|
}
|
|
51
362
|
return {
|
|
52
363
|
disconnect,
|
|
@@ -61,18 +372,8 @@
|
|
|
61
372
|
return resizeObserver;
|
|
62
373
|
};
|
|
63
374
|
|
|
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
375
|
const bindToWindow = (key, value) => {
|
|
74
376
|
if (typeof window !== "undefined") {
|
|
75
|
-
// biome-ignore lint/suspicious/noExplicitAny: global window extension requires flexibility
|
|
76
377
|
window[key] = value;
|
|
77
378
|
}
|
|
78
379
|
};
|
|
@@ -97,13 +398,8 @@
|
|
|
97
398
|
}
|
|
98
399
|
};
|
|
99
400
|
|
|
100
|
-
const getCanvasContainer = () => {
|
|
101
|
-
return document.querySelector(".canvas-container");
|
|
102
|
-
};
|
|
103
|
-
|
|
104
401
|
const clearHighlightFrame = () => {
|
|
105
|
-
const
|
|
106
|
-
const container = canvasContainer || document.body;
|
|
402
|
+
const container = getCanvasContainerOrBody();
|
|
107
403
|
const frame = container.querySelector(".highlight-frame-overlay");
|
|
108
404
|
if (frame) {
|
|
109
405
|
frame.remove();
|
|
@@ -152,6 +448,21 @@
|
|
|
152
448
|
return false;
|
|
153
449
|
};
|
|
154
450
|
|
|
451
|
+
const isInsideViewport = (element) => {
|
|
452
|
+
let current = element;
|
|
453
|
+
while (current) {
|
|
454
|
+
if (current.classList.contains("viewport")) {
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
// Stop at node-provider to avoid checking beyond the editable area
|
|
458
|
+
if (current.getAttribute("data-role") === "node-provider") {
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
461
|
+
current = current.parentElement;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
};
|
|
465
|
+
|
|
155
466
|
const targetSameCandidates = (cache, current) => cache.length === current.length && cache.every((el, i) => el === current[i]);
|
|
156
467
|
|
|
157
468
|
let candidateCache = [];
|
|
@@ -164,7 +475,16 @@
|
|
|
164
475
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
165
476
|
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) &&
|
|
166
477
|
!element.classList.contains("select-none") &&
|
|
167
|
-
!
|
|
478
|
+
!element.classList.contains("content-layer") &&
|
|
479
|
+
!element.classList.contains("resize-handle") &&
|
|
480
|
+
!element.classList.contains("resize-presets") &&
|
|
481
|
+
!isInsideComponent(element) &&
|
|
482
|
+
isInsideViewport(element));
|
|
483
|
+
console.log("candidates", candidates);
|
|
484
|
+
if (candidates.length === 0) {
|
|
485
|
+
lastSelectedNode = null;
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
168
488
|
const editableNode = text.getEditableNode();
|
|
169
489
|
if (editableNode && candidates.includes(editableNode)) {
|
|
170
490
|
selectedNode = editableNode;
|
|
@@ -208,7 +528,7 @@
|
|
|
208
528
|
onNodeSelected(selectedNode);
|
|
209
529
|
};
|
|
210
530
|
|
|
211
|
-
const setupEventListener
|
|
531
|
+
const setupEventListener = (nodeProvider, onNodeSelected, onEscapePressed, text) => {
|
|
212
532
|
const messageHandler = (event) => {
|
|
213
533
|
processPostMessage(event, onNodeSelected);
|
|
214
534
|
};
|
|
@@ -232,6 +552,17 @@
|
|
|
232
552
|
};
|
|
233
553
|
};
|
|
234
554
|
|
|
555
|
+
const toggleClass = (element, className, condition) => {
|
|
556
|
+
if (!element)
|
|
557
|
+
return;
|
|
558
|
+
if (condition) {
|
|
559
|
+
element.classList.add(className);
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
element.classList.remove(className);
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
|
|
235
566
|
const isComponentInstance = (element) => {
|
|
236
567
|
return element.getAttribute("data-instance") === "true";
|
|
237
568
|
};
|
|
@@ -269,16 +600,6 @@
|
|
|
269
600
|
createCornerHandle(group, 0, height, "handle-bottom-left", isInstance, isTextEdit);
|
|
270
601
|
};
|
|
271
602
|
|
|
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
603
|
const getComponentColor$1 = () => {
|
|
283
604
|
return getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim() || "oklch(65.6% 0.241 354.308)";
|
|
284
605
|
};
|
|
@@ -307,8 +628,7 @@
|
|
|
307
628
|
svg.style.height = "100vh";
|
|
308
629
|
svg.style.pointerEvents = "none";
|
|
309
630
|
svg.style.zIndex = "500";
|
|
310
|
-
const viewportWidth
|
|
311
|
-
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
631
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
312
632
|
svg.setAttribute("width", viewportWidth.toString());
|
|
313
633
|
svg.setAttribute("height", viewportHeight.toString());
|
|
314
634
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
@@ -331,13 +651,8 @@
|
|
|
331
651
|
group.appendChild(rect);
|
|
332
652
|
createCornerHandles(group, minWidth, height, isInstance, isTextEdit);
|
|
333
653
|
svg.appendChild(group);
|
|
334
|
-
const
|
|
335
|
-
|
|
336
|
-
canvasContainer.appendChild(svg);
|
|
337
|
-
}
|
|
338
|
-
else {
|
|
339
|
-
document.body.appendChild(svg);
|
|
340
|
-
}
|
|
654
|
+
const container = getCanvasContainerOrBody();
|
|
655
|
+
container.appendChild(svg);
|
|
341
656
|
return svg;
|
|
342
657
|
};
|
|
343
658
|
|
|
@@ -374,19 +689,14 @@
|
|
|
374
689
|
const createToolsContainer = (node, highlightFrame, isInstance = false, isTextEdit = false) => {
|
|
375
690
|
const nodeTools = document.createElement("div");
|
|
376
691
|
nodeTools.className = "node-tools";
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
if (isTextEdit) {
|
|
381
|
-
nodeTools.classList.add("is-text-edit");
|
|
382
|
-
}
|
|
692
|
+
toggleClass(nodeTools, "is-instance", isInstance);
|
|
693
|
+
toggleClass(nodeTools, "is-text-edit", isTextEdit);
|
|
383
694
|
highlightFrame.appendChild(nodeTools);
|
|
384
695
|
createTagLabel(node, nodeTools);
|
|
385
696
|
};
|
|
386
697
|
|
|
387
698
|
function getHighlightFrameElement() {
|
|
388
|
-
const
|
|
389
|
-
const container = canvasContainer || document.body;
|
|
699
|
+
const container = getCanvasContainerOrBody();
|
|
390
700
|
return container.querySelector(".highlight-frame-overlay");
|
|
391
701
|
}
|
|
392
702
|
|
|
@@ -394,8 +704,8 @@
|
|
|
394
704
|
if (!node)
|
|
395
705
|
return;
|
|
396
706
|
const existingHighlightFrame = getHighlightFrameElement();
|
|
397
|
-
const
|
|
398
|
-
const existingToolsWrapper =
|
|
707
|
+
const container = getCanvasContainerOrBody();
|
|
708
|
+
const existingToolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
399
709
|
if (existingHighlightFrame) {
|
|
400
710
|
existingHighlightFrame.remove();
|
|
401
711
|
}
|
|
@@ -414,24 +724,15 @@
|
|
|
414
724
|
// Create tools wrapper using CSS transform (GPU-accelerated)
|
|
415
725
|
const toolsWrapper = document.createElement("div");
|
|
416
726
|
toolsWrapper.classList.add("highlight-frame-tools-wrapper");
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
}
|
|
420
|
-
if (isTextEdit) {
|
|
421
|
-
toolsWrapper.classList.add("is-text-edit");
|
|
422
|
-
}
|
|
727
|
+
toggleClass(toolsWrapper, "is-instance", isInstance);
|
|
728
|
+
toggleClass(toolsWrapper, "is-text-edit", isTextEdit);
|
|
423
729
|
toolsWrapper.style.position = "absolute";
|
|
424
730
|
toolsWrapper.style.transform = `translate(${left}px, ${bottomY}px)`;
|
|
425
731
|
toolsWrapper.style.transformOrigin = "left center";
|
|
426
732
|
toolsWrapper.style.pointerEvents = "none";
|
|
427
733
|
toolsWrapper.style.zIndex = "500";
|
|
428
734
|
createToolsContainer(node, toolsWrapper, isInstance, isTextEdit);
|
|
429
|
-
|
|
430
|
-
canvasContainer.appendChild(toolsWrapper);
|
|
431
|
-
}
|
|
432
|
-
else {
|
|
433
|
-
document.body.appendChild(toolsWrapper);
|
|
434
|
-
}
|
|
735
|
+
container.appendChild(toolsWrapper);
|
|
435
736
|
};
|
|
436
737
|
|
|
437
738
|
const getComponentColor = () => {
|
|
@@ -449,24 +750,13 @@
|
|
|
449
750
|
const isTextEdit = node.contentEditable === "true";
|
|
450
751
|
// Update SVG dimensions to match current viewport (handles window resize and ensures coordinate system is correct)
|
|
451
752
|
// Use clientWidth/Height to match getBoundingClientRect() coordinate system (excludes scrollbars)
|
|
452
|
-
const viewportWidth
|
|
453
|
-
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
753
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
454
754
|
frame.setAttribute("width", viewportWidth.toString());
|
|
455
755
|
frame.setAttribute("height", viewportHeight.toString());
|
|
456
756
|
// Update instance class
|
|
457
|
-
|
|
458
|
-
frame.classList.add("is-instance");
|
|
459
|
-
}
|
|
460
|
-
else {
|
|
461
|
-
frame.classList.remove("is-instance");
|
|
462
|
-
}
|
|
757
|
+
toggleClass(frame, "is-instance", isInstance);
|
|
463
758
|
// Update text edit class
|
|
464
|
-
|
|
465
|
-
frame.classList.add("is-text-edit");
|
|
466
|
-
}
|
|
467
|
-
else {
|
|
468
|
-
frame.classList.remove("is-text-edit");
|
|
469
|
-
}
|
|
759
|
+
toggleClass(frame, "is-text-edit", isTextEdit);
|
|
470
760
|
const group = frame.querySelector(".highlight-frame-group");
|
|
471
761
|
if (!group)
|
|
472
762
|
return;
|
|
@@ -483,8 +773,7 @@
|
|
|
483
773
|
else {
|
|
484
774
|
rect.removeAttribute("stroke"); // Use CSS default
|
|
485
775
|
}
|
|
486
|
-
const
|
|
487
|
-
const container = canvasContainer || document.body;
|
|
776
|
+
const container = getCanvasContainerOrBody();
|
|
488
777
|
const toolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
489
778
|
const nodeTools = toolsWrapper?.querySelector(".node-tools");
|
|
490
779
|
const zoom = getCanvasWindowValue(["zoom", "current"], canvasName) ?? 1;
|
|
@@ -495,36 +784,10 @@
|
|
|
495
784
|
const minWidth = Math.max(width, 3);
|
|
496
785
|
const bottomY = top + height;
|
|
497
786
|
// Update instance classes on tools wrapper and node tools
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
else {
|
|
503
|
-
toolsWrapper.classList.remove("is-instance");
|
|
504
|
-
}
|
|
505
|
-
// Update text edit class
|
|
506
|
-
if (isTextEdit) {
|
|
507
|
-
toolsWrapper.classList.add("is-text-edit");
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
toolsWrapper.classList.remove("is-text-edit");
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
if (nodeTools) {
|
|
514
|
-
if (isInstance) {
|
|
515
|
-
nodeTools.classList.add("is-instance");
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
nodeTools.classList.remove("is-instance");
|
|
519
|
-
}
|
|
520
|
-
// Update text edit class
|
|
521
|
-
if (isTextEdit) {
|
|
522
|
-
nodeTools.classList.add("is-text-edit");
|
|
523
|
-
}
|
|
524
|
-
else {
|
|
525
|
-
nodeTools.classList.remove("is-text-edit");
|
|
526
|
-
}
|
|
527
|
-
}
|
|
787
|
+
toggleClass(toolsWrapper, "is-instance", isInstance);
|
|
788
|
+
toggleClass(toolsWrapper, "is-text-edit", isTextEdit);
|
|
789
|
+
toggleClass(nodeTools, "is-instance", isInstance);
|
|
790
|
+
toggleClass(nodeTools, "is-text-edit", isTextEdit);
|
|
528
791
|
// Batch all DOM writes (single paint pass)
|
|
529
792
|
// Update group transform to move entire group (rect + handles) at once
|
|
530
793
|
group.setAttribute("transform", `translate(${left}, ${top})`);
|
|
@@ -590,8 +853,7 @@
|
|
|
590
853
|
const displayValue = hasHiddenClass ? "none" : "";
|
|
591
854
|
// Batch DOM writes
|
|
592
855
|
frame.style.display = displayValue;
|
|
593
|
-
const
|
|
594
|
-
const container = canvasContainer || document.body;
|
|
856
|
+
const container = getCanvasContainerOrBody();
|
|
595
857
|
const toolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
596
858
|
if (toolsWrapper) {
|
|
597
859
|
toolsWrapper.style.display = displayValue;
|
|
@@ -711,6 +973,7 @@
|
|
|
711
973
|
// Accumulate mutations instead of replacing
|
|
712
974
|
pendingMutations.push(...mutations);
|
|
713
975
|
scheduleProcess();
|
|
976
|
+
console.log("refreshHighlightFrame in mutationObserver");
|
|
714
977
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
715
978
|
});
|
|
716
979
|
return () => {
|
|
@@ -848,14 +1111,15 @@
|
|
|
848
1111
|
checkNodeExists();
|
|
849
1112
|
if (!document.contains(node))
|
|
850
1113
|
return;
|
|
1114
|
+
console.log("refreshHighlightFrame in mutationObserver 2");
|
|
851
1115
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
852
1116
|
updateHighlightFrameVisibility(node);
|
|
853
1117
|
});
|
|
854
1118
|
mutationObserver.observe(node, {
|
|
855
1119
|
attributes: true,
|
|
856
1120
|
characterData: true,
|
|
857
|
-
childList: true,
|
|
858
|
-
subtree: true,
|
|
1121
|
+
//childList: true,
|
|
1122
|
+
//subtree: true,
|
|
859
1123
|
});
|
|
860
1124
|
// Also observe parent node to catch when this node is removed
|
|
861
1125
|
const parentNode = node.parentElement;
|
|
@@ -884,19 +1148,25 @@
|
|
|
884
1148
|
if (!document.contains(node))
|
|
885
1149
|
return; // Exit early if node was removed
|
|
886
1150
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
1151
|
+
console.log("refreshHighlightFrame in resizeObserver");
|
|
887
1152
|
updateHighlightFrameVisibility(node);
|
|
888
1153
|
});
|
|
889
1154
|
}
|
|
890
1155
|
selectedNode = node;
|
|
891
1156
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
892
1157
|
highlightNode(node) ?? null;
|
|
893
|
-
if (node
|
|
894
|
-
|
|
895
|
-
|
|
1158
|
+
if (node) {
|
|
1159
|
+
highlightNode(node);
|
|
1160
|
+
if (nodeProvider) {
|
|
1161
|
+
updateHighlightFrameVisibility(node);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
else {
|
|
1165
|
+
clearHighlightFrame();
|
|
896
1166
|
}
|
|
897
1167
|
};
|
|
898
1168
|
// Setup event listener
|
|
899
|
-
const removeListeners = setupEventListener
|
|
1169
|
+
const removeListeners = setupEventListener(nodeProvider, selectNode, handleEscape, text);
|
|
900
1170
|
const cleanup = () => {
|
|
901
1171
|
removeListeners();
|
|
902
1172
|
resizeObserver?.disconnect();
|
|
@@ -931,10 +1201,14 @@
|
|
|
931
1201
|
return nodeTools;
|
|
932
1202
|
};
|
|
933
1203
|
|
|
1204
|
+
const getNodeProvider = () => {
|
|
1205
|
+
return document.querySelector('[data-role="node-provider"]');
|
|
1206
|
+
};
|
|
1207
|
+
|
|
934
1208
|
const DEFAULT_WIDTH = 400;
|
|
935
1209
|
const RESIZE_CONFIG = {
|
|
936
|
-
minWidth:
|
|
937
|
-
maxWidth:
|
|
1210
|
+
minWidth: 4,
|
|
1211
|
+
maxWidth: 2560,
|
|
938
1212
|
};
|
|
939
1213
|
const RESIZE_PRESETS = [
|
|
940
1214
|
{
|
|
@@ -964,27 +1238,6 @@
|
|
|
964
1238
|
},
|
|
965
1239
|
];
|
|
966
1240
|
|
|
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
|
-
};
|
|
974
|
-
resizeHandle.addEventListener("mousedown", startResize);
|
|
975
|
-
document.addEventListener("mousemove", handleResize);
|
|
976
|
-
document.addEventListener("mouseup", stopResize);
|
|
977
|
-
document.addEventListener("mouseleave", handleMouseLeave);
|
|
978
|
-
window.addEventListener("blur", blurResize);
|
|
979
|
-
return () => {
|
|
980
|
-
resizeHandle.removeEventListener("mousedown", startResize);
|
|
981
|
-
document.removeEventListener("mousemove", handleResize);
|
|
982
|
-
document.removeEventListener("mouseup", stopResize);
|
|
983
|
-
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
984
|
-
window.removeEventListener("blur", blurResize);
|
|
985
|
-
};
|
|
986
|
-
};
|
|
987
|
-
|
|
988
1241
|
const createResizeHandle = (container) => {
|
|
989
1242
|
const handle = document.createElement("div");
|
|
990
1243
|
handle.className = "resize-handle";
|
|
@@ -1051,48 +1304,60 @@
|
|
|
1051
1304
|
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1052
1305
|
container.style.setProperty("--container-width", `${width}px`);
|
|
1053
1306
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1054
|
-
|
|
1307
|
+
// Track initial values for resize calculation
|
|
1055
1308
|
let startX = 0;
|
|
1056
1309
|
let startWidth = 0;
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
const handleResize = (event) => {
|
|
1065
|
-
if (!isDragging)
|
|
1066
|
-
return;
|
|
1067
|
-
if (canvas) {
|
|
1068
|
-
canvas.style.cursor = "ew-resize";
|
|
1069
|
-
}
|
|
1070
|
-
const width = calcWidth(event, startX, startWidth);
|
|
1071
|
-
updateWidth(container, width);
|
|
1072
|
-
};
|
|
1073
|
-
const stopResize = (event) => {
|
|
1074
|
-
event.preventDefault();
|
|
1075
|
-
event.stopPropagation();
|
|
1076
|
-
if (canvas) {
|
|
1077
|
-
canvas.style.cursor = "default";
|
|
1078
|
-
}
|
|
1079
|
-
isDragging = false;
|
|
1080
|
-
};
|
|
1081
|
-
const blurResize = () => {
|
|
1082
|
-
if (canvas) {
|
|
1083
|
-
canvas.style.cursor = "default";
|
|
1310
|
+
// Handle mouse leave for resize (specific to resize use case)
|
|
1311
|
+
const handleMouseLeave = (event) => {
|
|
1312
|
+
// Check if mouse is leaving the window/document
|
|
1313
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
1314
|
+
if (canvas) {
|
|
1315
|
+
canvas.style.cursor = "default";
|
|
1316
|
+
}
|
|
1084
1317
|
}
|
|
1085
|
-
isDragging = false;
|
|
1086
1318
|
};
|
|
1087
|
-
const
|
|
1319
|
+
const removeDragListeners = createDragHandler(resizeHandle, {
|
|
1320
|
+
onStart: (_event, { startX: dragStartX }) => {
|
|
1321
|
+
startX = dragStartX;
|
|
1322
|
+
startWidth = container.offsetWidth;
|
|
1323
|
+
},
|
|
1324
|
+
onDrag: (event) => {
|
|
1325
|
+
if (canvas) {
|
|
1326
|
+
canvas.style.cursor = "ew-resize";
|
|
1327
|
+
}
|
|
1328
|
+
const width = calcWidth(event, startX, startWidth);
|
|
1329
|
+
updateWidth(container, width);
|
|
1330
|
+
},
|
|
1331
|
+
onStop: () => {
|
|
1332
|
+
if (canvas) {
|
|
1333
|
+
canvas.style.cursor = "default";
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
onCancel: () => {
|
|
1337
|
+
if (canvas) {
|
|
1338
|
+
canvas.style.cursor = "default";
|
|
1339
|
+
}
|
|
1340
|
+
},
|
|
1341
|
+
onPreventClick: () => { },
|
|
1342
|
+
});
|
|
1343
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
1344
|
+
refreshViewportLabels();
|
|
1088
1345
|
const cleanup = () => {
|
|
1089
|
-
|
|
1090
|
-
|
|
1346
|
+
removeDragListeners();
|
|
1347
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
1091
1348
|
resizeHandle.remove();
|
|
1349
|
+
refreshViewportLabels();
|
|
1092
1350
|
};
|
|
1093
1351
|
return {
|
|
1094
1352
|
setWidth: (width) => {
|
|
1095
1353
|
updateWidth(container, width);
|
|
1354
|
+
refreshViewportLabels();
|
|
1355
|
+
const nodeTools = getNodeTools();
|
|
1356
|
+
const selectedNode = nodeTools?.getSelectedNode?.();
|
|
1357
|
+
const nodeProvider = getNodeProvider();
|
|
1358
|
+
if (selectedNode && nodeProvider) {
|
|
1359
|
+
refreshHighlightFrame(selectedNode, nodeProvider);
|
|
1360
|
+
}
|
|
1096
1361
|
},
|
|
1097
1362
|
cleanup,
|
|
1098
1363
|
};
|