vite-plugin-visual-selector 0.1.2 → 0.1.4
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/index.d.ts +2 -2
- package/dist/index.js +278 -178
- package/dist/index.js.map +1 -1
- package/dist/{runtime-CpZH1D8y.d.ts → runtime-D695qvel.d.ts} +9 -3
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +278 -178
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/runtime.js
CHANGED
|
@@ -141,169 +141,6 @@ function handleInlineEdit(state, data, updatePositions) {
|
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
// src/runtime/layer-navigation.ts
|
|
145
|
-
function buildLayerTree(element) {
|
|
146
|
-
const tree = [];
|
|
147
|
-
const ancestors = [];
|
|
148
|
-
let parent = element.parentElement;
|
|
149
|
-
while (parent && parent !== document.body) {
|
|
150
|
-
if (hasSourceLocation(parent)) {
|
|
151
|
-
ancestors.push(parent);
|
|
152
|
-
}
|
|
153
|
-
parent = parent.parentElement;
|
|
154
|
-
}
|
|
155
|
-
ancestors.reverse();
|
|
156
|
-
ancestors.forEach((el) => {
|
|
157
|
-
tree.push({ element: el, tagName: el.tagName.toLowerCase(), depth: 0 });
|
|
158
|
-
});
|
|
159
|
-
tree.push({ element, tagName: element.tagName.toLowerCase(), depth: 0 });
|
|
160
|
-
for (let i = 0; i < element.children.length; i++) {
|
|
161
|
-
const child = element.children[i];
|
|
162
|
-
if (hasSourceLocation(child)) {
|
|
163
|
-
tree.push({ element: child, tagName: child.tagName.toLowerCase(), depth: 0 });
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
return tree;
|
|
167
|
-
}
|
|
168
|
-
function removeLayerDropdown(state) {
|
|
169
|
-
if (state.layerDropdown) {
|
|
170
|
-
const parentOverlay = state.layerDropdown.parentElement;
|
|
171
|
-
if (parentOverlay) {
|
|
172
|
-
const arrowEl = parentOverlay.querySelector("[data-tag-arrow]");
|
|
173
|
-
if (arrowEl) arrowEl.textContent = "\u2304";
|
|
174
|
-
}
|
|
175
|
-
state.layerDropdown.remove();
|
|
176
|
-
state.layerDropdown = null;
|
|
177
|
-
document.removeEventListener("keydown", handleLayerKeyboard);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
function toggleLayerDropdown(state, element, anchor, onSelectElement) {
|
|
181
|
-
if (state.layerDropdown) {
|
|
182
|
-
removeLayerDropdown(state);
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
const layers = buildLayerTree(element);
|
|
186
|
-
renderLayerDropdown(state, anchor, layers, element, onSelectElement);
|
|
187
|
-
}
|
|
188
|
-
function renderLayerDropdown(state, anchor, layers, currentElement, onSelectElement) {
|
|
189
|
-
const dropdown = document.createElement("div");
|
|
190
|
-
dropdown.setAttribute("data-layer-dropdown", "true");
|
|
191
|
-
dropdown.setAttribute(AGENT_ATTR, "");
|
|
192
|
-
Object.assign(dropdown.style, {
|
|
193
|
-
position: "absolute",
|
|
194
|
-
backgroundColor: "#ffffff",
|
|
195
|
-
border: "1px solid #e2e8f0",
|
|
196
|
-
borderRadius: "6px",
|
|
197
|
-
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
198
|
-
fontSize: "12px",
|
|
199
|
-
zIndex: "99999",
|
|
200
|
-
pointerEvents: "auto",
|
|
201
|
-
padding: "4px 0",
|
|
202
|
-
minWidth: "120px",
|
|
203
|
-
maxHeight: "320px",
|
|
204
|
-
overflowY: "auto"
|
|
205
|
-
});
|
|
206
|
-
let focusedIndex = -1;
|
|
207
|
-
const items = [];
|
|
208
|
-
layers.forEach((layer, idx) => {
|
|
209
|
-
const item = document.createElement("div");
|
|
210
|
-
item.setAttribute(AGENT_ATTR, "");
|
|
211
|
-
item.textContent = layer.tagName;
|
|
212
|
-
item.style.padding = "6px 16px";
|
|
213
|
-
item.style.cursor = "pointer";
|
|
214
|
-
item.style.whiteSpace = "nowrap";
|
|
215
|
-
if (layer.element === currentElement) {
|
|
216
|
-
item.style.color = "#000000";
|
|
217
|
-
item.style.backgroundColor = "rgba(255, 204, 0, 0.2)";
|
|
218
|
-
item.style.fontWeight = "600";
|
|
219
|
-
focusedIndex = idx;
|
|
220
|
-
} else {
|
|
221
|
-
item.style.color = "#64748b";
|
|
222
|
-
}
|
|
223
|
-
item.addEventListener("click", (e) => {
|
|
224
|
-
e.stopPropagation();
|
|
225
|
-
removeLayerDropdown(state);
|
|
226
|
-
onSelectElement(layer.element);
|
|
227
|
-
});
|
|
228
|
-
item.addEventListener("mouseenter", () => {
|
|
229
|
-
if (layer.element !== currentElement) {
|
|
230
|
-
item.style.backgroundColor = "#f1f5f9";
|
|
231
|
-
item.style.color = "#334155";
|
|
232
|
-
}
|
|
233
|
-
});
|
|
234
|
-
item.addEventListener("mouseleave", () => {
|
|
235
|
-
if (layer.element !== currentElement) {
|
|
236
|
-
item.style.backgroundColor = "transparent";
|
|
237
|
-
item.style.color = "#64748b";
|
|
238
|
-
}
|
|
239
|
-
});
|
|
240
|
-
items.push(item);
|
|
241
|
-
dropdown.appendChild(item);
|
|
242
|
-
});
|
|
243
|
-
document.body.appendChild(dropdown);
|
|
244
|
-
const anchorRect = anchor.getBoundingClientRect();
|
|
245
|
-
dropdown.style.top = `${anchorRect.bottom + window.scrollY + 2}px`;
|
|
246
|
-
dropdown.style.left = `${anchorRect.left + window.scrollX}px`;
|
|
247
|
-
requestAnimationFrame(() => {
|
|
248
|
-
const ddRect = dropdown.getBoundingClientRect();
|
|
249
|
-
if (ddRect.right > window.innerWidth - 4) {
|
|
250
|
-
dropdown.style.left = `${window.innerWidth - ddRect.width - 4 + window.scrollX}px`;
|
|
251
|
-
}
|
|
252
|
-
if (ddRect.left < 4) {
|
|
253
|
-
dropdown.style.left = `${4 + window.scrollX}px`;
|
|
254
|
-
}
|
|
255
|
-
if (ddRect.bottom > window.innerHeight) {
|
|
256
|
-
dropdown.style.top = `${anchorRect.top + window.scrollY - ddRect.height - 2}px`;
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
state.layerDropdown = dropdown;
|
|
260
|
-
const handleKeydown = (e) => {
|
|
261
|
-
if (!state.layerDropdown) return;
|
|
262
|
-
switch (e.key) {
|
|
263
|
-
case "ArrowDown":
|
|
264
|
-
e.preventDefault();
|
|
265
|
-
focusedIndex = Math.min(focusedIndex + 1, items.length - 1);
|
|
266
|
-
highlightItem(items, focusedIndex, layers, currentElement);
|
|
267
|
-
break;
|
|
268
|
-
case "ArrowUp":
|
|
269
|
-
e.preventDefault();
|
|
270
|
-
focusedIndex = Math.max(focusedIndex - 1, 0);
|
|
271
|
-
highlightItem(items, focusedIndex, layers, currentElement);
|
|
272
|
-
break;
|
|
273
|
-
case "Enter":
|
|
274
|
-
e.preventDefault();
|
|
275
|
-
if (focusedIndex >= 0 && focusedIndex < layers.length) {
|
|
276
|
-
removeLayerDropdown(state);
|
|
277
|
-
onSelectElement(layers[focusedIndex].element);
|
|
278
|
-
}
|
|
279
|
-
break;
|
|
280
|
-
case "Escape":
|
|
281
|
-
e.preventDefault();
|
|
282
|
-
removeLayerDropdown(state);
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
};
|
|
286
|
-
handleLayerKeyboard.current = handleKeydown;
|
|
287
|
-
document.addEventListener("keydown", handleLayerKeyboard);
|
|
288
|
-
}
|
|
289
|
-
function handleLayerKeyboard(e) {
|
|
290
|
-
const current = handleLayerKeyboard.current;
|
|
291
|
-
if (current) current(e);
|
|
292
|
-
}
|
|
293
|
-
function highlightItem(items, index, layers, currentElement) {
|
|
294
|
-
items.forEach((item, i) => {
|
|
295
|
-
if (layers[i].element === currentElement) {
|
|
296
|
-
item.style.color = "#000000";
|
|
297
|
-
item.style.backgroundColor = i === index ? "rgba(255, 204, 0, 0.35)" : "rgba(255, 204, 0, 0.2)";
|
|
298
|
-
item.style.fontWeight = "600";
|
|
299
|
-
} else {
|
|
300
|
-
item.style.backgroundColor = i === index ? "#f1f5f9" : "transparent";
|
|
301
|
-
item.style.color = i === index ? "#334155" : "#64748b";
|
|
302
|
-
item.style.fontWeight = "normal";
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
144
|
// src/runtime/messages.ts
|
|
308
145
|
function reportElementSelected(state, element) {
|
|
309
146
|
const el = element;
|
|
@@ -362,6 +199,18 @@ function reportPositionUpdate(state) {
|
|
|
362
199
|
} catch {
|
|
363
200
|
}
|
|
364
201
|
}
|
|
202
|
+
function reportDropdownState(state, isOpen) {
|
|
203
|
+
try {
|
|
204
|
+
window.parent.postMessage(
|
|
205
|
+
{
|
|
206
|
+
type: "dropdown-state",
|
|
207
|
+
data: { isOpen }
|
|
208
|
+
},
|
|
209
|
+
state.targetOrigin
|
|
210
|
+
);
|
|
211
|
+
} catch {
|
|
212
|
+
}
|
|
213
|
+
}
|
|
365
214
|
function handleMessage(state, e, callbacks) {
|
|
366
215
|
if (state.targetOrigin !== "*" && e.origin !== state.targetOrigin) return;
|
|
367
216
|
const msg = e.data;
|
|
@@ -395,10 +244,7 @@ function handleMessage(state, e, callbacks) {
|
|
|
395
244
|
break;
|
|
396
245
|
case "unselect-element":
|
|
397
246
|
if (state.editingElement) callbacks.stopInlineEditing();
|
|
398
|
-
|
|
399
|
-
state.selectionOverlays = [];
|
|
400
|
-
state.selectedId = null;
|
|
401
|
-
state.selectedElement = null;
|
|
247
|
+
callbacks.clearSelectionState();
|
|
402
248
|
break;
|
|
403
249
|
case "update-theme-variables":
|
|
404
250
|
if (msg.data?.variables) {
|
|
@@ -433,6 +279,11 @@ function handleMessage(state, e, callbacks) {
|
|
|
433
279
|
case "request-element-position":
|
|
434
280
|
reportPositionUpdate(state);
|
|
435
281
|
break;
|
|
282
|
+
case "select-element-by-id":
|
|
283
|
+
if (msg.data?.visualSelectorId) {
|
|
284
|
+
callbacks.selectElementById(msg.data.visualSelectorId);
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
436
287
|
case CONTROL_MESSAGE_TYPE: {
|
|
437
288
|
const controlMsg = msg;
|
|
438
289
|
if (typeof controlMsg.active === "boolean") {
|
|
@@ -483,6 +334,27 @@ function createOverlay(opts) {
|
|
|
483
334
|
}
|
|
484
335
|
return overlay;
|
|
485
336
|
}
|
|
337
|
+
function setTagArrowExpanded(arrow, expanded) {
|
|
338
|
+
arrow.style.transform = expanded ? "rotate(0deg)" : "rotate(180deg)";
|
|
339
|
+
}
|
|
340
|
+
function createTagArrowSvg() {
|
|
341
|
+
const svgNs = "http://www.w3.org/2000/svg";
|
|
342
|
+
const svg = document.createElementNS(svgNs, "svg");
|
|
343
|
+
svg.setAttribute("xmlns", svgNs);
|
|
344
|
+
svg.setAttribute("width", "12");
|
|
345
|
+
svg.setAttribute("height", "12");
|
|
346
|
+
svg.setAttribute("viewBox", "0 0 12 12");
|
|
347
|
+
svg.setAttribute("fill", "none");
|
|
348
|
+
svg.style.display = "block";
|
|
349
|
+
const path = document.createElementNS(svgNs, "path");
|
|
350
|
+
path.setAttribute("stroke", "rgba(51, 51, 51, 1)");
|
|
351
|
+
path.setAttribute("stroke-width", "0.8325");
|
|
352
|
+
path.setAttribute("stroke-linejoin", "round");
|
|
353
|
+
path.setAttribute("stroke-linecap", "round");
|
|
354
|
+
path.setAttribute("d", "M3.24994 7.5L6.24994 4.5L9.24994 7.5");
|
|
355
|
+
svg.appendChild(path);
|
|
356
|
+
return svg;
|
|
357
|
+
}
|
|
486
358
|
function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
487
359
|
const rect = target.getBoundingClientRect();
|
|
488
360
|
overlay.style.top = `${rect.top + window.scrollY}px`;
|
|
@@ -505,8 +377,10 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
|
505
377
|
textAlign: "center",
|
|
506
378
|
display: "flex",
|
|
507
379
|
alignItems: "center",
|
|
380
|
+
justifyContent: "center",
|
|
508
381
|
gap: "3px",
|
|
509
|
-
lineHeight: "1.4"
|
|
382
|
+
lineHeight: "1.4",
|
|
383
|
+
whiteSpace: "nowrap"
|
|
510
384
|
});
|
|
511
385
|
if (tagMode === "selected") {
|
|
512
386
|
tag.style.backgroundColor = "#FC0";
|
|
@@ -515,12 +389,23 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
|
515
389
|
tag.style.pointerEvents = "auto";
|
|
516
390
|
const textSpan = document.createElement("span");
|
|
517
391
|
textSpan.textContent = target.tagName.toLowerCase();
|
|
392
|
+
textSpan.style.display = "inline-flex";
|
|
393
|
+
textSpan.style.alignItems = "center";
|
|
394
|
+
textSpan.style.lineHeight = "1";
|
|
518
395
|
tag.appendChild(textSpan);
|
|
519
396
|
const arrow = document.createElement("span");
|
|
520
397
|
arrow.setAttribute("data-tag-arrow", "");
|
|
521
|
-
arrow.textContent = "
|
|
522
|
-
arrow.style.
|
|
398
|
+
arrow.textContent = "";
|
|
399
|
+
arrow.style.display = "inline-flex";
|
|
400
|
+
arrow.style.alignItems = "center";
|
|
401
|
+
arrow.style.justifyContent = "center";
|
|
402
|
+
arrow.style.width = "12px";
|
|
403
|
+
arrow.style.height = "12px";
|
|
404
|
+
arrow.style.flex = "0 0 auto";
|
|
405
|
+
arrow.style.transformOrigin = "center";
|
|
523
406
|
arrow.style.lineHeight = "1";
|
|
407
|
+
arrow.appendChild(createTagArrowSvg());
|
|
408
|
+
setTagArrowExpanded(arrow, false);
|
|
524
409
|
tag.appendChild(arrow);
|
|
525
410
|
if (callbacks?.onTagClick) {
|
|
526
411
|
const onTagClick = callbacks.onTagClick;
|
|
@@ -643,6 +528,171 @@ function unfreezeAnimations() {
|
|
|
643
528
|
if (overflowStyle) overflowStyle.remove();
|
|
644
529
|
}
|
|
645
530
|
|
|
531
|
+
// src/runtime/layer-navigation.ts
|
|
532
|
+
function buildLayerTree(element) {
|
|
533
|
+
const tree = [];
|
|
534
|
+
const ancestors = [];
|
|
535
|
+
let parent = element.parentElement;
|
|
536
|
+
while (parent && parent !== document.body) {
|
|
537
|
+
if (hasSourceLocation(parent)) {
|
|
538
|
+
ancestors.push(parent);
|
|
539
|
+
}
|
|
540
|
+
parent = parent.parentElement;
|
|
541
|
+
}
|
|
542
|
+
ancestors.reverse();
|
|
543
|
+
ancestors.forEach((el) => {
|
|
544
|
+
tree.push({ element: el, tagName: el.tagName.toLowerCase(), depth: 0 });
|
|
545
|
+
});
|
|
546
|
+
tree.push({ element, tagName: element.tagName.toLowerCase(), depth: 0 });
|
|
547
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
548
|
+
const child = element.children[i];
|
|
549
|
+
if (hasSourceLocation(child)) {
|
|
550
|
+
tree.push({ element: child, tagName: child.tagName.toLowerCase(), depth: 0 });
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return tree;
|
|
554
|
+
}
|
|
555
|
+
function removeLayerDropdown(state) {
|
|
556
|
+
if (state.layerDropdown) {
|
|
557
|
+
const parentOverlay = state.layerDropdown.parentElement;
|
|
558
|
+
if (parentOverlay) {
|
|
559
|
+
const arrowEl = parentOverlay.querySelector("[data-tag-arrow]");
|
|
560
|
+
if (arrowEl) setTagArrowExpanded(arrowEl, false);
|
|
561
|
+
}
|
|
562
|
+
state.layerDropdown.remove();
|
|
563
|
+
state.layerDropdown = null;
|
|
564
|
+
reportDropdownState(state, false);
|
|
565
|
+
document.removeEventListener("keydown", handleLayerKeyboard);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
function toggleLayerDropdown(state, element, anchor, onSelectElement) {
|
|
569
|
+
if (state.layerDropdown) {
|
|
570
|
+
removeLayerDropdown(state);
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
const layers = buildLayerTree(element);
|
|
574
|
+
renderLayerDropdown(state, anchor, layers, element, onSelectElement);
|
|
575
|
+
}
|
|
576
|
+
function renderLayerDropdown(state, anchor, layers, currentElement, onSelectElement) {
|
|
577
|
+
const dropdown = document.createElement("div");
|
|
578
|
+
dropdown.setAttribute("data-layer-dropdown", "true");
|
|
579
|
+
dropdown.setAttribute(AGENT_ATTR, "");
|
|
580
|
+
Object.assign(dropdown.style, {
|
|
581
|
+
position: "absolute",
|
|
582
|
+
backgroundColor: "#ffffff",
|
|
583
|
+
border: "1px solid #e2e8f0",
|
|
584
|
+
borderRadius: "6px",
|
|
585
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
586
|
+
fontSize: "12px",
|
|
587
|
+
zIndex: "2147483647",
|
|
588
|
+
pointerEvents: "auto",
|
|
589
|
+
padding: "4px 0",
|
|
590
|
+
minWidth: "120px",
|
|
591
|
+
maxHeight: "320px",
|
|
592
|
+
overflowY: "auto"
|
|
593
|
+
});
|
|
594
|
+
let focusedIndex = -1;
|
|
595
|
+
const items = [];
|
|
596
|
+
layers.forEach((layer, idx) => {
|
|
597
|
+
const item = document.createElement("div");
|
|
598
|
+
item.setAttribute(AGENT_ATTR, "");
|
|
599
|
+
item.textContent = layer.tagName;
|
|
600
|
+
item.style.padding = "6px 16px";
|
|
601
|
+
item.style.cursor = "pointer";
|
|
602
|
+
item.style.whiteSpace = "nowrap";
|
|
603
|
+
if (layer.element === currentElement) {
|
|
604
|
+
item.style.color = "#000000";
|
|
605
|
+
item.style.backgroundColor = "rgba(255, 204, 0, 0.2)";
|
|
606
|
+
item.style.fontWeight = "600";
|
|
607
|
+
focusedIndex = idx;
|
|
608
|
+
} else {
|
|
609
|
+
item.style.color = "#64748b";
|
|
610
|
+
}
|
|
611
|
+
item.addEventListener("click", (e) => {
|
|
612
|
+
e.stopPropagation();
|
|
613
|
+
removeLayerDropdown(state);
|
|
614
|
+
onSelectElement(layer.element);
|
|
615
|
+
});
|
|
616
|
+
item.addEventListener("mouseenter", () => {
|
|
617
|
+
if (layer.element !== currentElement) {
|
|
618
|
+
item.style.backgroundColor = "#f1f5f9";
|
|
619
|
+
item.style.color = "#334155";
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
item.addEventListener("mouseleave", () => {
|
|
623
|
+
if (layer.element !== currentElement) {
|
|
624
|
+
item.style.backgroundColor = "transparent";
|
|
625
|
+
item.style.color = "#64748b";
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
items.push(item);
|
|
629
|
+
dropdown.appendChild(item);
|
|
630
|
+
});
|
|
631
|
+
document.body.appendChild(dropdown);
|
|
632
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
633
|
+
dropdown.style.top = `${anchorRect.bottom + window.scrollY + 2}px`;
|
|
634
|
+
dropdown.style.left = `${anchorRect.left + window.scrollX}px`;
|
|
635
|
+
requestAnimationFrame(() => {
|
|
636
|
+
const ddRect = dropdown.getBoundingClientRect();
|
|
637
|
+
if (ddRect.right > window.innerWidth - 4) {
|
|
638
|
+
dropdown.style.left = `${window.innerWidth - ddRect.width - 4 + window.scrollX}px`;
|
|
639
|
+
}
|
|
640
|
+
if (ddRect.left < 4) {
|
|
641
|
+
dropdown.style.left = `${4 + window.scrollX}px`;
|
|
642
|
+
}
|
|
643
|
+
if (ddRect.bottom > window.innerHeight) {
|
|
644
|
+
dropdown.style.top = `${anchorRect.top + window.scrollY - ddRect.height - 2}px`;
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
state.layerDropdown = dropdown;
|
|
648
|
+
reportDropdownState(state, true);
|
|
649
|
+
const handleKeydown = (e) => {
|
|
650
|
+
if (!state.layerDropdown) return;
|
|
651
|
+
switch (e.key) {
|
|
652
|
+
case "ArrowDown":
|
|
653
|
+
e.preventDefault();
|
|
654
|
+
focusedIndex = Math.min(focusedIndex + 1, items.length - 1);
|
|
655
|
+
highlightItem(items, focusedIndex, layers, currentElement);
|
|
656
|
+
break;
|
|
657
|
+
case "ArrowUp":
|
|
658
|
+
e.preventDefault();
|
|
659
|
+
focusedIndex = Math.max(focusedIndex - 1, 0);
|
|
660
|
+
highlightItem(items, focusedIndex, layers, currentElement);
|
|
661
|
+
break;
|
|
662
|
+
case "Enter":
|
|
663
|
+
e.preventDefault();
|
|
664
|
+
if (focusedIndex >= 0 && focusedIndex < layers.length) {
|
|
665
|
+
removeLayerDropdown(state);
|
|
666
|
+
onSelectElement(layers[focusedIndex].element);
|
|
667
|
+
}
|
|
668
|
+
break;
|
|
669
|
+
case "Escape":
|
|
670
|
+
e.preventDefault();
|
|
671
|
+
removeLayerDropdown(state);
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
};
|
|
675
|
+
handleLayerKeyboard.current = handleKeydown;
|
|
676
|
+
document.addEventListener("keydown", handleLayerKeyboard);
|
|
677
|
+
}
|
|
678
|
+
function handleLayerKeyboard(e) {
|
|
679
|
+
const current = handleLayerKeyboard.current;
|
|
680
|
+
if (current) current(e);
|
|
681
|
+
}
|
|
682
|
+
function highlightItem(items, index, layers, currentElement) {
|
|
683
|
+
items.forEach((item, i) => {
|
|
684
|
+
if (layers[i].element === currentElement) {
|
|
685
|
+
item.style.color = "#000000";
|
|
686
|
+
item.style.backgroundColor = i === index ? "rgba(255, 204, 0, 0.35)" : "rgba(255, 204, 0, 0.2)";
|
|
687
|
+
item.style.fontWeight = "600";
|
|
688
|
+
} else {
|
|
689
|
+
item.style.backgroundColor = i === index ? "#f1f5f9" : "transparent";
|
|
690
|
+
item.style.color = i === index ? "#334155" : "#64748b";
|
|
691
|
+
item.style.fontWeight = "normal";
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
646
696
|
// src/runtime/state.ts
|
|
647
697
|
function createAgentState(options = {}) {
|
|
648
698
|
return {
|
|
@@ -656,13 +706,36 @@ function createAgentState(options = {}) {
|
|
|
656
706
|
layerDropdown: null,
|
|
657
707
|
mutationObserver: null,
|
|
658
708
|
attributeName: options.attributeName ?? DEFAULT_ATTRIBUTE_NAME,
|
|
659
|
-
targetOrigin: options.targetOrigin ?? "*"
|
|
709
|
+
targetOrigin: options.targetOrigin ?? "*",
|
|
710
|
+
runtimeIdCounter: 0
|
|
660
711
|
};
|
|
661
712
|
}
|
|
662
713
|
|
|
663
714
|
// src/runtime/index.ts
|
|
664
715
|
function setupVisualEditAgent(options = {}) {
|
|
665
716
|
const state = createAgentState(options);
|
|
717
|
+
const trackedSelector = `[${state.attributeName}], [data-visual-selector-id]`;
|
|
718
|
+
function shouldAssignRuntimeId(element) {
|
|
719
|
+
return !element.closest(`[${AGENT_ATTR}]`) && !element.hasAttribute(state.attributeName) && !element.hasAttribute("data-visual-selector-id");
|
|
720
|
+
}
|
|
721
|
+
function assignRuntimeId(element) {
|
|
722
|
+
if (!shouldAssignRuntimeId(element)) return;
|
|
723
|
+
state.runtimeIdCounter += 1;
|
|
724
|
+
element.setAttribute("data-visual-selector-id", `runtime:${state.runtimeIdCounter}`);
|
|
725
|
+
}
|
|
726
|
+
function trackElementSubtree(root) {
|
|
727
|
+
assignRuntimeId(root);
|
|
728
|
+
root.querySelectorAll("*").forEach((element) => assignRuntimeId(element));
|
|
729
|
+
}
|
|
730
|
+
function trackCurrentDom() {
|
|
731
|
+
if (!document.body) return;
|
|
732
|
+
trackElementSubtree(document.body);
|
|
733
|
+
}
|
|
734
|
+
function ensureElementTracked(element) {
|
|
735
|
+
if (!element.closest(`[${AGENT_ATTR}]`)) {
|
|
736
|
+
assignRuntimeId(element);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
666
739
|
function updateAllOverlayPositions() {
|
|
667
740
|
if (state.selectedId && state.selectedElement?.isConnected) {
|
|
668
741
|
const siblings = findAllElementsById(state.selectedId);
|
|
@@ -679,7 +752,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
679
752
|
function onTagClick(target, tag) {
|
|
680
753
|
const arrowEl = tag.querySelector("[data-tag-arrow]");
|
|
681
754
|
if (arrowEl) {
|
|
682
|
-
arrowEl
|
|
755
|
+
setTagArrowExpanded(arrowEl, !state.layerDropdown);
|
|
683
756
|
}
|
|
684
757
|
toggleLayerDropdown(state, target, tag, selectElement);
|
|
685
758
|
}
|
|
@@ -690,7 +763,10 @@ function setupVisualEditAgent(options = {}) {
|
|
|
690
763
|
if (freezeStyle) freezeStyle.disabled = false;
|
|
691
764
|
if (!element) return null;
|
|
692
765
|
if (element.closest(`[${AGENT_ATTR}]`)) return null;
|
|
693
|
-
|
|
766
|
+
const trackedElement = element.closest(trackedSelector);
|
|
767
|
+
if (trackedElement) return trackedElement;
|
|
768
|
+
ensureElementTracked(element);
|
|
769
|
+
return element.hasAttribute("data-visual-selector-id") ? element : null;
|
|
694
770
|
}
|
|
695
771
|
function findHoverTarget(x, y, excludeId) {
|
|
696
772
|
const element = findElementAtPoint(x, y);
|
|
@@ -738,6 +814,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
738
814
|
selectElement(element);
|
|
739
815
|
}
|
|
740
816
|
function selectElement(element) {
|
|
817
|
+
ensureElementTracked(element);
|
|
741
818
|
const id = getSourceId(element);
|
|
742
819
|
if (!id) return;
|
|
743
820
|
if (state.editingElement) {
|
|
@@ -773,6 +850,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
773
850
|
if (enabled) {
|
|
774
851
|
document.body.style.cursor = "crosshair";
|
|
775
852
|
freezeAnimations();
|
|
853
|
+
trackCurrentDom();
|
|
776
854
|
document.addEventListener("mousemove", onMouseMove);
|
|
777
855
|
document.addEventListener("mouseleave", onMouseLeave);
|
|
778
856
|
document.addEventListener("click", onClick, true);
|
|
@@ -799,6 +877,13 @@ function setupVisualEditAgent(options = {}) {
|
|
|
799
877
|
function startMutationObserver() {
|
|
800
878
|
if (state.mutationObserver) return;
|
|
801
879
|
state.mutationObserver = new MutationObserver((mutations) => {
|
|
880
|
+
mutations.forEach((mutation) => {
|
|
881
|
+
mutation.addedNodes.forEach((node) => {
|
|
882
|
+
if (node instanceof Element) {
|
|
883
|
+
trackElementSubtree(node);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
});
|
|
802
887
|
const hasRelevantChange = mutations.some((m) => {
|
|
803
888
|
if (m.type === "attributes" && ["style", "class", "width", "height"].includes(m.attributeName ?? "") && containsTrackedElement(m.target))
|
|
804
889
|
return true;
|
|
@@ -824,8 +909,8 @@ function setupVisualEditAgent(options = {}) {
|
|
|
824
909
|
}
|
|
825
910
|
function containsTrackedElement(node) {
|
|
826
911
|
if (!(node instanceof Element)) return false;
|
|
827
|
-
if (node.
|
|
828
|
-
return
|
|
912
|
+
if (node.closest(`[${AGENT_ATTR}]`)) return false;
|
|
913
|
+
return node.hasAttribute(state.attributeName) || node.hasAttribute("data-visual-selector-id") || !!node.querySelector(trackedSelector);
|
|
829
914
|
}
|
|
830
915
|
function setupSandboxMountObserver() {
|
|
831
916
|
if (window.self === window.top) return;
|
|
@@ -834,7 +919,9 @@ function setupVisualEditAgent(options = {}) {
|
|
|
834
919
|
(m) => m.addedNodes.length > 0 || m.removedNodes.length > 0
|
|
835
920
|
);
|
|
836
921
|
if (!hasChanges) return;
|
|
837
|
-
const hasTrackedElements = document.body.querySelectorAll(
|
|
922
|
+
const hasTrackedElements = document.body.querySelectorAll(
|
|
923
|
+
`[${state.attributeName}], [data-dynamic-content], [data-visual-selector-id]`
|
|
924
|
+
).length > 0;
|
|
838
925
|
try {
|
|
839
926
|
window.parent.postMessage(
|
|
840
927
|
{
|
|
@@ -853,12 +940,25 @@ function setupVisualEditAgent(options = {}) {
|
|
|
853
940
|
});
|
|
854
941
|
}
|
|
855
942
|
}
|
|
943
|
+
function selectElementById(id) {
|
|
944
|
+
const element = document.querySelector(
|
|
945
|
+
`[${state.attributeName}="${id}"], [data-visual-selector-id="${id}"]`
|
|
946
|
+
);
|
|
947
|
+
if (element) {
|
|
948
|
+
selectElement(element);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
function clearSelectionState() {
|
|
952
|
+
clearAllOverlays(state, () => removeLayerDropdown(state));
|
|
953
|
+
}
|
|
856
954
|
const onMessage = (e) => {
|
|
857
955
|
handleMessage(state, e, {
|
|
858
956
|
enableEditMode,
|
|
859
957
|
updateAllOverlayPositions,
|
|
860
958
|
stopInlineEditing: () => stopInlineEditing(state, updateAllOverlayPositions),
|
|
861
|
-
handleInlineEdit: (data) => handleInlineEdit(state, data, updateAllOverlayPositions)
|
|
959
|
+
handleInlineEdit: (data) => handleInlineEdit(state, data, updateAllOverlayPositions),
|
|
960
|
+
selectElementById,
|
|
961
|
+
clearSelectionState
|
|
862
962
|
});
|
|
863
963
|
};
|
|
864
964
|
window.addEventListener("message", onMessage);
|
|
@@ -877,7 +977,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
877
977
|
enableEditMode,
|
|
878
978
|
selectElement,
|
|
879
979
|
clearSelection() {
|
|
880
|
-
|
|
980
|
+
clearSelectionState();
|
|
881
981
|
},
|
|
882
982
|
getSelectedId() {
|
|
883
983
|
return state.selectedId;
|