vite-plugin-visual-selector 0.1.3 → 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 +267 -179
- package/dist/index.js.map +1 -1
- package/dist/{runtime-DpE5pSIp.d.ts → runtime-D695qvel.d.ts} +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +267 -179
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
|
-
import { V as VisualSelectorPluginOptions } from './runtime-
|
|
3
|
-
export { A as AgentReadyMessage, C as ContentEditingMessage, D as DropdownStateMessage, E as ElementPosition, a as ElementPositionUpdateMessage, b as ElementSelectedMessage, c as ElementUpdateMessage, I as InjectFontImportMessage, d as InlineEditMessage, L as LayerItem, P as PopoverDragStateMessage, R as RefreshPageMessage, e as RequestElementPositionMessage, S as SandboxMountMessage, f as SelectElementByIdMessage, T as ToggleInlineEditModeMessage, g as ToggleVisualEditModeMessage, U as UnselectElementMessage, h as UpdateAttributeMessage, i as UpdateClassesMessage, j as UpdateContentMessage, k as UpdateThemeVariablesMessage, l as VisualEditAgentInstance, m as VisualEditAgentOptions, n as VisualSelectorControlMessage, s as setupVisualEditAgent } from './runtime-
|
|
2
|
+
import { V as VisualSelectorPluginOptions } from './runtime-D695qvel.js';
|
|
3
|
+
export { A as AgentReadyMessage, C as ContentEditingMessage, D as DropdownStateMessage, E as ElementPosition, a as ElementPositionUpdateMessage, b as ElementSelectedMessage, c as ElementUpdateMessage, I as InjectFontImportMessage, d as InlineEditMessage, L as LayerItem, P as PopoverDragStateMessage, R as RefreshPageMessage, e as RequestElementPositionMessage, S as SandboxMountMessage, f as SelectElementByIdMessage, T as ToggleInlineEditModeMessage, g as ToggleVisualEditModeMessage, U as UnselectElementMessage, h as UpdateAttributeMessage, i as UpdateClassesMessage, j as UpdateContentMessage, k as UpdateThemeVariablesMessage, l as VisualEditAgentInstance, m as VisualEditAgentOptions, n as VisualSelectorControlMessage, s as setupVisualEditAgent } from './runtime-D695qvel.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* 创建 Vite 插件:在编译阶段(transform hook)向每个原生 JSX 元素注入
|
package/dist/index.js
CHANGED
|
@@ -218,169 +218,6 @@ function handleInlineEdit(state, data, updatePositions) {
|
|
|
218
218
|
}
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
// src/runtime/layer-navigation.ts
|
|
222
|
-
function buildLayerTree(element) {
|
|
223
|
-
const tree = [];
|
|
224
|
-
const ancestors = [];
|
|
225
|
-
let parent = element.parentElement;
|
|
226
|
-
while (parent && parent !== document.body) {
|
|
227
|
-
if (hasSourceLocation(parent)) {
|
|
228
|
-
ancestors.push(parent);
|
|
229
|
-
}
|
|
230
|
-
parent = parent.parentElement;
|
|
231
|
-
}
|
|
232
|
-
ancestors.reverse();
|
|
233
|
-
ancestors.forEach((el) => {
|
|
234
|
-
tree.push({ element: el, tagName: el.tagName.toLowerCase(), depth: 0 });
|
|
235
|
-
});
|
|
236
|
-
tree.push({ element, tagName: element.tagName.toLowerCase(), depth: 0 });
|
|
237
|
-
for (let i = 0; i < element.children.length; i++) {
|
|
238
|
-
const child = element.children[i];
|
|
239
|
-
if (hasSourceLocation(child)) {
|
|
240
|
-
tree.push({ element: child, tagName: child.tagName.toLowerCase(), depth: 0 });
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
return tree;
|
|
244
|
-
}
|
|
245
|
-
function removeLayerDropdown(state) {
|
|
246
|
-
if (state.layerDropdown) {
|
|
247
|
-
const parentOverlay = state.layerDropdown.parentElement;
|
|
248
|
-
if (parentOverlay) {
|
|
249
|
-
const arrowEl = parentOverlay.querySelector("[data-tag-arrow]");
|
|
250
|
-
if (arrowEl) arrowEl.textContent = "\u2304";
|
|
251
|
-
}
|
|
252
|
-
state.layerDropdown.remove();
|
|
253
|
-
state.layerDropdown = null;
|
|
254
|
-
document.removeEventListener("keydown", handleLayerKeyboard);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
function toggleLayerDropdown(state, element, anchor, onSelectElement) {
|
|
258
|
-
if (state.layerDropdown) {
|
|
259
|
-
removeLayerDropdown(state);
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const layers = buildLayerTree(element);
|
|
263
|
-
renderLayerDropdown(state, anchor, layers, element, onSelectElement);
|
|
264
|
-
}
|
|
265
|
-
function renderLayerDropdown(state, anchor, layers, currentElement, onSelectElement) {
|
|
266
|
-
const dropdown = document.createElement("div");
|
|
267
|
-
dropdown.setAttribute("data-layer-dropdown", "true");
|
|
268
|
-
dropdown.setAttribute(AGENT_ATTR, "");
|
|
269
|
-
Object.assign(dropdown.style, {
|
|
270
|
-
position: "absolute",
|
|
271
|
-
backgroundColor: "#ffffff",
|
|
272
|
-
border: "1px solid #e2e8f0",
|
|
273
|
-
borderRadius: "6px",
|
|
274
|
-
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
275
|
-
fontSize: "12px",
|
|
276
|
-
zIndex: "99999",
|
|
277
|
-
pointerEvents: "auto",
|
|
278
|
-
padding: "4px 0",
|
|
279
|
-
minWidth: "120px",
|
|
280
|
-
maxHeight: "320px",
|
|
281
|
-
overflowY: "auto"
|
|
282
|
-
});
|
|
283
|
-
let focusedIndex = -1;
|
|
284
|
-
const items = [];
|
|
285
|
-
layers.forEach((layer, idx) => {
|
|
286
|
-
const item = document.createElement("div");
|
|
287
|
-
item.setAttribute(AGENT_ATTR, "");
|
|
288
|
-
item.textContent = layer.tagName;
|
|
289
|
-
item.style.padding = "6px 16px";
|
|
290
|
-
item.style.cursor = "pointer";
|
|
291
|
-
item.style.whiteSpace = "nowrap";
|
|
292
|
-
if (layer.element === currentElement) {
|
|
293
|
-
item.style.color = "#000000";
|
|
294
|
-
item.style.backgroundColor = "rgba(255, 204, 0, 0.2)";
|
|
295
|
-
item.style.fontWeight = "600";
|
|
296
|
-
focusedIndex = idx;
|
|
297
|
-
} else {
|
|
298
|
-
item.style.color = "#64748b";
|
|
299
|
-
}
|
|
300
|
-
item.addEventListener("click", (e) => {
|
|
301
|
-
e.stopPropagation();
|
|
302
|
-
removeLayerDropdown(state);
|
|
303
|
-
onSelectElement(layer.element);
|
|
304
|
-
});
|
|
305
|
-
item.addEventListener("mouseenter", () => {
|
|
306
|
-
if (layer.element !== currentElement) {
|
|
307
|
-
item.style.backgroundColor = "#f1f5f9";
|
|
308
|
-
item.style.color = "#334155";
|
|
309
|
-
}
|
|
310
|
-
});
|
|
311
|
-
item.addEventListener("mouseleave", () => {
|
|
312
|
-
if (layer.element !== currentElement) {
|
|
313
|
-
item.style.backgroundColor = "transparent";
|
|
314
|
-
item.style.color = "#64748b";
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
items.push(item);
|
|
318
|
-
dropdown.appendChild(item);
|
|
319
|
-
});
|
|
320
|
-
document.body.appendChild(dropdown);
|
|
321
|
-
const anchorRect = anchor.getBoundingClientRect();
|
|
322
|
-
dropdown.style.top = `${anchorRect.bottom + window.scrollY + 2}px`;
|
|
323
|
-
dropdown.style.left = `${anchorRect.left + window.scrollX}px`;
|
|
324
|
-
requestAnimationFrame(() => {
|
|
325
|
-
const ddRect = dropdown.getBoundingClientRect();
|
|
326
|
-
if (ddRect.right > window.innerWidth - 4) {
|
|
327
|
-
dropdown.style.left = `${window.innerWidth - ddRect.width - 4 + window.scrollX}px`;
|
|
328
|
-
}
|
|
329
|
-
if (ddRect.left < 4) {
|
|
330
|
-
dropdown.style.left = `${4 + window.scrollX}px`;
|
|
331
|
-
}
|
|
332
|
-
if (ddRect.bottom > window.innerHeight) {
|
|
333
|
-
dropdown.style.top = `${anchorRect.top + window.scrollY - ddRect.height - 2}px`;
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
state.layerDropdown = dropdown;
|
|
337
|
-
const handleKeydown = (e) => {
|
|
338
|
-
if (!state.layerDropdown) return;
|
|
339
|
-
switch (e.key) {
|
|
340
|
-
case "ArrowDown":
|
|
341
|
-
e.preventDefault();
|
|
342
|
-
focusedIndex = Math.min(focusedIndex + 1, items.length - 1);
|
|
343
|
-
highlightItem(items, focusedIndex, layers, currentElement);
|
|
344
|
-
break;
|
|
345
|
-
case "ArrowUp":
|
|
346
|
-
e.preventDefault();
|
|
347
|
-
focusedIndex = Math.max(focusedIndex - 1, 0);
|
|
348
|
-
highlightItem(items, focusedIndex, layers, currentElement);
|
|
349
|
-
break;
|
|
350
|
-
case "Enter":
|
|
351
|
-
e.preventDefault();
|
|
352
|
-
if (focusedIndex >= 0 && focusedIndex < layers.length) {
|
|
353
|
-
removeLayerDropdown(state);
|
|
354
|
-
onSelectElement(layers[focusedIndex].element);
|
|
355
|
-
}
|
|
356
|
-
break;
|
|
357
|
-
case "Escape":
|
|
358
|
-
e.preventDefault();
|
|
359
|
-
removeLayerDropdown(state);
|
|
360
|
-
break;
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
handleLayerKeyboard.current = handleKeydown;
|
|
364
|
-
document.addEventListener("keydown", handleLayerKeyboard);
|
|
365
|
-
}
|
|
366
|
-
function handleLayerKeyboard(e) {
|
|
367
|
-
const current = handleLayerKeyboard.current;
|
|
368
|
-
if (current) current(e);
|
|
369
|
-
}
|
|
370
|
-
function highlightItem(items, index, layers, currentElement) {
|
|
371
|
-
items.forEach((item, i) => {
|
|
372
|
-
if (layers[i].element === currentElement) {
|
|
373
|
-
item.style.color = "#000000";
|
|
374
|
-
item.style.backgroundColor = i === index ? "rgba(255, 204, 0, 0.35)" : "rgba(255, 204, 0, 0.2)";
|
|
375
|
-
item.style.fontWeight = "600";
|
|
376
|
-
} else {
|
|
377
|
-
item.style.backgroundColor = i === index ? "#f1f5f9" : "transparent";
|
|
378
|
-
item.style.color = i === index ? "#334155" : "#64748b";
|
|
379
|
-
item.style.fontWeight = "normal";
|
|
380
|
-
}
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
|
|
384
221
|
// src/runtime/messages.ts
|
|
385
222
|
function reportElementSelected(state, element) {
|
|
386
223
|
const el = element;
|
|
@@ -439,6 +276,18 @@ function reportPositionUpdate(state) {
|
|
|
439
276
|
} catch {
|
|
440
277
|
}
|
|
441
278
|
}
|
|
279
|
+
function reportDropdownState(state, isOpen) {
|
|
280
|
+
try {
|
|
281
|
+
window.parent.postMessage(
|
|
282
|
+
{
|
|
283
|
+
type: "dropdown-state",
|
|
284
|
+
data: { isOpen }
|
|
285
|
+
},
|
|
286
|
+
state.targetOrigin
|
|
287
|
+
);
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
442
291
|
function handleMessage(state, e, callbacks) {
|
|
443
292
|
if (state.targetOrigin !== "*" && e.origin !== state.targetOrigin) return;
|
|
444
293
|
const msg = e.data;
|
|
@@ -472,10 +321,7 @@ function handleMessage(state, e, callbacks) {
|
|
|
472
321
|
break;
|
|
473
322
|
case "unselect-element":
|
|
474
323
|
if (state.editingElement) callbacks.stopInlineEditing();
|
|
475
|
-
|
|
476
|
-
state.selectionOverlays = [];
|
|
477
|
-
state.selectedId = null;
|
|
478
|
-
state.selectedElement = null;
|
|
324
|
+
callbacks.clearSelectionState();
|
|
479
325
|
break;
|
|
480
326
|
case "update-theme-variables":
|
|
481
327
|
if (msg.data?.variables) {
|
|
@@ -565,6 +411,27 @@ function createOverlay(opts) {
|
|
|
565
411
|
}
|
|
566
412
|
return overlay;
|
|
567
413
|
}
|
|
414
|
+
function setTagArrowExpanded(arrow, expanded) {
|
|
415
|
+
arrow.style.transform = expanded ? "rotate(0deg)" : "rotate(180deg)";
|
|
416
|
+
}
|
|
417
|
+
function createTagArrowSvg() {
|
|
418
|
+
const svgNs = "http://www.w3.org/2000/svg";
|
|
419
|
+
const svg = document.createElementNS(svgNs, "svg");
|
|
420
|
+
svg.setAttribute("xmlns", svgNs);
|
|
421
|
+
svg.setAttribute("width", "12");
|
|
422
|
+
svg.setAttribute("height", "12");
|
|
423
|
+
svg.setAttribute("viewBox", "0 0 12 12");
|
|
424
|
+
svg.setAttribute("fill", "none");
|
|
425
|
+
svg.style.display = "block";
|
|
426
|
+
const path2 = document.createElementNS(svgNs, "path");
|
|
427
|
+
path2.setAttribute("stroke", "rgba(51, 51, 51, 1)");
|
|
428
|
+
path2.setAttribute("stroke-width", "0.8325");
|
|
429
|
+
path2.setAttribute("stroke-linejoin", "round");
|
|
430
|
+
path2.setAttribute("stroke-linecap", "round");
|
|
431
|
+
path2.setAttribute("d", "M3.24994 7.5L6.24994 4.5L9.24994 7.5");
|
|
432
|
+
svg.appendChild(path2);
|
|
433
|
+
return svg;
|
|
434
|
+
}
|
|
568
435
|
function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
569
436
|
const rect = target.getBoundingClientRect();
|
|
570
437
|
overlay.style.top = `${rect.top + window.scrollY}px`;
|
|
@@ -587,8 +454,10 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
|
587
454
|
textAlign: "center",
|
|
588
455
|
display: "flex",
|
|
589
456
|
alignItems: "center",
|
|
457
|
+
justifyContent: "center",
|
|
590
458
|
gap: "3px",
|
|
591
|
-
lineHeight: "1.4"
|
|
459
|
+
lineHeight: "1.4",
|
|
460
|
+
whiteSpace: "nowrap"
|
|
592
461
|
});
|
|
593
462
|
if (tagMode === "selected") {
|
|
594
463
|
tag.style.backgroundColor = "#FC0";
|
|
@@ -597,12 +466,23 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
|
|
|
597
466
|
tag.style.pointerEvents = "auto";
|
|
598
467
|
const textSpan = document.createElement("span");
|
|
599
468
|
textSpan.textContent = target.tagName.toLowerCase();
|
|
469
|
+
textSpan.style.display = "inline-flex";
|
|
470
|
+
textSpan.style.alignItems = "center";
|
|
471
|
+
textSpan.style.lineHeight = "1";
|
|
600
472
|
tag.appendChild(textSpan);
|
|
601
473
|
const arrow = document.createElement("span");
|
|
602
474
|
arrow.setAttribute("data-tag-arrow", "");
|
|
603
|
-
arrow.textContent = "
|
|
604
|
-
arrow.style.
|
|
475
|
+
arrow.textContent = "";
|
|
476
|
+
arrow.style.display = "inline-flex";
|
|
477
|
+
arrow.style.alignItems = "center";
|
|
478
|
+
arrow.style.justifyContent = "center";
|
|
479
|
+
arrow.style.width = "12px";
|
|
480
|
+
arrow.style.height = "12px";
|
|
481
|
+
arrow.style.flex = "0 0 auto";
|
|
482
|
+
arrow.style.transformOrigin = "center";
|
|
605
483
|
arrow.style.lineHeight = "1";
|
|
484
|
+
arrow.appendChild(createTagArrowSvg());
|
|
485
|
+
setTagArrowExpanded(arrow, false);
|
|
606
486
|
tag.appendChild(arrow);
|
|
607
487
|
if (callbacks?.onTagClick) {
|
|
608
488
|
const onTagClick = callbacks.onTagClick;
|
|
@@ -725,6 +605,171 @@ function unfreezeAnimations() {
|
|
|
725
605
|
if (overflowStyle) overflowStyle.remove();
|
|
726
606
|
}
|
|
727
607
|
|
|
608
|
+
// src/runtime/layer-navigation.ts
|
|
609
|
+
function buildLayerTree(element) {
|
|
610
|
+
const tree = [];
|
|
611
|
+
const ancestors = [];
|
|
612
|
+
let parent = element.parentElement;
|
|
613
|
+
while (parent && parent !== document.body) {
|
|
614
|
+
if (hasSourceLocation(parent)) {
|
|
615
|
+
ancestors.push(parent);
|
|
616
|
+
}
|
|
617
|
+
parent = parent.parentElement;
|
|
618
|
+
}
|
|
619
|
+
ancestors.reverse();
|
|
620
|
+
ancestors.forEach((el) => {
|
|
621
|
+
tree.push({ element: el, tagName: el.tagName.toLowerCase(), depth: 0 });
|
|
622
|
+
});
|
|
623
|
+
tree.push({ element, tagName: element.tagName.toLowerCase(), depth: 0 });
|
|
624
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
625
|
+
const child = element.children[i];
|
|
626
|
+
if (hasSourceLocation(child)) {
|
|
627
|
+
tree.push({ element: child, tagName: child.tagName.toLowerCase(), depth: 0 });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return tree;
|
|
631
|
+
}
|
|
632
|
+
function removeLayerDropdown(state) {
|
|
633
|
+
if (state.layerDropdown) {
|
|
634
|
+
const parentOverlay = state.layerDropdown.parentElement;
|
|
635
|
+
if (parentOverlay) {
|
|
636
|
+
const arrowEl = parentOverlay.querySelector("[data-tag-arrow]");
|
|
637
|
+
if (arrowEl) setTagArrowExpanded(arrowEl, false);
|
|
638
|
+
}
|
|
639
|
+
state.layerDropdown.remove();
|
|
640
|
+
state.layerDropdown = null;
|
|
641
|
+
reportDropdownState(state, false);
|
|
642
|
+
document.removeEventListener("keydown", handleLayerKeyboard);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
function toggleLayerDropdown(state, element, anchor, onSelectElement) {
|
|
646
|
+
if (state.layerDropdown) {
|
|
647
|
+
removeLayerDropdown(state);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const layers = buildLayerTree(element);
|
|
651
|
+
renderLayerDropdown(state, anchor, layers, element, onSelectElement);
|
|
652
|
+
}
|
|
653
|
+
function renderLayerDropdown(state, anchor, layers, currentElement, onSelectElement) {
|
|
654
|
+
const dropdown = document.createElement("div");
|
|
655
|
+
dropdown.setAttribute("data-layer-dropdown", "true");
|
|
656
|
+
dropdown.setAttribute(AGENT_ATTR, "");
|
|
657
|
+
Object.assign(dropdown.style, {
|
|
658
|
+
position: "absolute",
|
|
659
|
+
backgroundColor: "#ffffff",
|
|
660
|
+
border: "1px solid #e2e8f0",
|
|
661
|
+
borderRadius: "6px",
|
|
662
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
|
|
663
|
+
fontSize: "12px",
|
|
664
|
+
zIndex: "2147483647",
|
|
665
|
+
pointerEvents: "auto",
|
|
666
|
+
padding: "4px 0",
|
|
667
|
+
minWidth: "120px",
|
|
668
|
+
maxHeight: "320px",
|
|
669
|
+
overflowY: "auto"
|
|
670
|
+
});
|
|
671
|
+
let focusedIndex = -1;
|
|
672
|
+
const items = [];
|
|
673
|
+
layers.forEach((layer, idx) => {
|
|
674
|
+
const item = document.createElement("div");
|
|
675
|
+
item.setAttribute(AGENT_ATTR, "");
|
|
676
|
+
item.textContent = layer.tagName;
|
|
677
|
+
item.style.padding = "6px 16px";
|
|
678
|
+
item.style.cursor = "pointer";
|
|
679
|
+
item.style.whiteSpace = "nowrap";
|
|
680
|
+
if (layer.element === currentElement) {
|
|
681
|
+
item.style.color = "#000000";
|
|
682
|
+
item.style.backgroundColor = "rgba(255, 204, 0, 0.2)";
|
|
683
|
+
item.style.fontWeight = "600";
|
|
684
|
+
focusedIndex = idx;
|
|
685
|
+
} else {
|
|
686
|
+
item.style.color = "#64748b";
|
|
687
|
+
}
|
|
688
|
+
item.addEventListener("click", (e) => {
|
|
689
|
+
e.stopPropagation();
|
|
690
|
+
removeLayerDropdown(state);
|
|
691
|
+
onSelectElement(layer.element);
|
|
692
|
+
});
|
|
693
|
+
item.addEventListener("mouseenter", () => {
|
|
694
|
+
if (layer.element !== currentElement) {
|
|
695
|
+
item.style.backgroundColor = "#f1f5f9";
|
|
696
|
+
item.style.color = "#334155";
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
item.addEventListener("mouseleave", () => {
|
|
700
|
+
if (layer.element !== currentElement) {
|
|
701
|
+
item.style.backgroundColor = "transparent";
|
|
702
|
+
item.style.color = "#64748b";
|
|
703
|
+
}
|
|
704
|
+
});
|
|
705
|
+
items.push(item);
|
|
706
|
+
dropdown.appendChild(item);
|
|
707
|
+
});
|
|
708
|
+
document.body.appendChild(dropdown);
|
|
709
|
+
const anchorRect = anchor.getBoundingClientRect();
|
|
710
|
+
dropdown.style.top = `${anchorRect.bottom + window.scrollY + 2}px`;
|
|
711
|
+
dropdown.style.left = `${anchorRect.left + window.scrollX}px`;
|
|
712
|
+
requestAnimationFrame(() => {
|
|
713
|
+
const ddRect = dropdown.getBoundingClientRect();
|
|
714
|
+
if (ddRect.right > window.innerWidth - 4) {
|
|
715
|
+
dropdown.style.left = `${window.innerWidth - ddRect.width - 4 + window.scrollX}px`;
|
|
716
|
+
}
|
|
717
|
+
if (ddRect.left < 4) {
|
|
718
|
+
dropdown.style.left = `${4 + window.scrollX}px`;
|
|
719
|
+
}
|
|
720
|
+
if (ddRect.bottom > window.innerHeight) {
|
|
721
|
+
dropdown.style.top = `${anchorRect.top + window.scrollY - ddRect.height - 2}px`;
|
|
722
|
+
}
|
|
723
|
+
});
|
|
724
|
+
state.layerDropdown = dropdown;
|
|
725
|
+
reportDropdownState(state, true);
|
|
726
|
+
const handleKeydown = (e) => {
|
|
727
|
+
if (!state.layerDropdown) return;
|
|
728
|
+
switch (e.key) {
|
|
729
|
+
case "ArrowDown":
|
|
730
|
+
e.preventDefault();
|
|
731
|
+
focusedIndex = Math.min(focusedIndex + 1, items.length - 1);
|
|
732
|
+
highlightItem(items, focusedIndex, layers, currentElement);
|
|
733
|
+
break;
|
|
734
|
+
case "ArrowUp":
|
|
735
|
+
e.preventDefault();
|
|
736
|
+
focusedIndex = Math.max(focusedIndex - 1, 0);
|
|
737
|
+
highlightItem(items, focusedIndex, layers, currentElement);
|
|
738
|
+
break;
|
|
739
|
+
case "Enter":
|
|
740
|
+
e.preventDefault();
|
|
741
|
+
if (focusedIndex >= 0 && focusedIndex < layers.length) {
|
|
742
|
+
removeLayerDropdown(state);
|
|
743
|
+
onSelectElement(layers[focusedIndex].element);
|
|
744
|
+
}
|
|
745
|
+
break;
|
|
746
|
+
case "Escape":
|
|
747
|
+
e.preventDefault();
|
|
748
|
+
removeLayerDropdown(state);
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
handleLayerKeyboard.current = handleKeydown;
|
|
753
|
+
document.addEventListener("keydown", handleLayerKeyboard);
|
|
754
|
+
}
|
|
755
|
+
function handleLayerKeyboard(e) {
|
|
756
|
+
const current = handleLayerKeyboard.current;
|
|
757
|
+
if (current) current(e);
|
|
758
|
+
}
|
|
759
|
+
function highlightItem(items, index, layers, currentElement) {
|
|
760
|
+
items.forEach((item, i) => {
|
|
761
|
+
if (layers[i].element === currentElement) {
|
|
762
|
+
item.style.color = "#000000";
|
|
763
|
+
item.style.backgroundColor = i === index ? "rgba(255, 204, 0, 0.35)" : "rgba(255, 204, 0, 0.2)";
|
|
764
|
+
item.style.fontWeight = "600";
|
|
765
|
+
} else {
|
|
766
|
+
item.style.backgroundColor = i === index ? "#f1f5f9" : "transparent";
|
|
767
|
+
item.style.color = i === index ? "#334155" : "#64748b";
|
|
768
|
+
item.style.fontWeight = "normal";
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
|
|
728
773
|
// src/runtime/state.ts
|
|
729
774
|
function createAgentState(options = {}) {
|
|
730
775
|
return {
|
|
@@ -738,13 +783,36 @@ function createAgentState(options = {}) {
|
|
|
738
783
|
layerDropdown: null,
|
|
739
784
|
mutationObserver: null,
|
|
740
785
|
attributeName: options.attributeName ?? DEFAULT_ATTRIBUTE_NAME,
|
|
741
|
-
targetOrigin: options.targetOrigin ?? "*"
|
|
786
|
+
targetOrigin: options.targetOrigin ?? "*",
|
|
787
|
+
runtimeIdCounter: 0
|
|
742
788
|
};
|
|
743
789
|
}
|
|
744
790
|
|
|
745
791
|
// src/runtime/index.ts
|
|
746
792
|
function setupVisualEditAgent(options = {}) {
|
|
747
793
|
const state = createAgentState(options);
|
|
794
|
+
const trackedSelector = `[${state.attributeName}], [data-visual-selector-id]`;
|
|
795
|
+
function shouldAssignRuntimeId(element) {
|
|
796
|
+
return !element.closest(`[${AGENT_ATTR}]`) && !element.hasAttribute(state.attributeName) && !element.hasAttribute("data-visual-selector-id");
|
|
797
|
+
}
|
|
798
|
+
function assignRuntimeId(element) {
|
|
799
|
+
if (!shouldAssignRuntimeId(element)) return;
|
|
800
|
+
state.runtimeIdCounter += 1;
|
|
801
|
+
element.setAttribute("data-visual-selector-id", `runtime:${state.runtimeIdCounter}`);
|
|
802
|
+
}
|
|
803
|
+
function trackElementSubtree(root) {
|
|
804
|
+
assignRuntimeId(root);
|
|
805
|
+
root.querySelectorAll("*").forEach((element) => assignRuntimeId(element));
|
|
806
|
+
}
|
|
807
|
+
function trackCurrentDom() {
|
|
808
|
+
if (!document.body) return;
|
|
809
|
+
trackElementSubtree(document.body);
|
|
810
|
+
}
|
|
811
|
+
function ensureElementTracked(element) {
|
|
812
|
+
if (!element.closest(`[${AGENT_ATTR}]`)) {
|
|
813
|
+
assignRuntimeId(element);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
748
816
|
function updateAllOverlayPositions() {
|
|
749
817
|
if (state.selectedId && state.selectedElement?.isConnected) {
|
|
750
818
|
const siblings = findAllElementsById(state.selectedId);
|
|
@@ -761,7 +829,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
761
829
|
function onTagClick(target, tag) {
|
|
762
830
|
const arrowEl = tag.querySelector("[data-tag-arrow]");
|
|
763
831
|
if (arrowEl) {
|
|
764
|
-
arrowEl
|
|
832
|
+
setTagArrowExpanded(arrowEl, !state.layerDropdown);
|
|
765
833
|
}
|
|
766
834
|
toggleLayerDropdown(state, target, tag, selectElement);
|
|
767
835
|
}
|
|
@@ -772,7 +840,10 @@ function setupVisualEditAgent(options = {}) {
|
|
|
772
840
|
if (freezeStyle) freezeStyle.disabled = false;
|
|
773
841
|
if (!element) return null;
|
|
774
842
|
if (element.closest(`[${AGENT_ATTR}]`)) return null;
|
|
775
|
-
|
|
843
|
+
const trackedElement = element.closest(trackedSelector);
|
|
844
|
+
if (trackedElement) return trackedElement;
|
|
845
|
+
ensureElementTracked(element);
|
|
846
|
+
return element.hasAttribute("data-visual-selector-id") ? element : null;
|
|
776
847
|
}
|
|
777
848
|
function findHoverTarget(x, y, excludeId) {
|
|
778
849
|
const element = findElementAtPoint(x, y);
|
|
@@ -820,6 +891,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
820
891
|
selectElement(element);
|
|
821
892
|
}
|
|
822
893
|
function selectElement(element) {
|
|
894
|
+
ensureElementTracked(element);
|
|
823
895
|
const id = getSourceId(element);
|
|
824
896
|
if (!id) return;
|
|
825
897
|
if (state.editingElement) {
|
|
@@ -855,6 +927,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
855
927
|
if (enabled) {
|
|
856
928
|
document.body.style.cursor = "crosshair";
|
|
857
929
|
freezeAnimations();
|
|
930
|
+
trackCurrentDom();
|
|
858
931
|
document.addEventListener("mousemove", onMouseMove);
|
|
859
932
|
document.addEventListener("mouseleave", onMouseLeave);
|
|
860
933
|
document.addEventListener("click", onClick, true);
|
|
@@ -881,6 +954,13 @@ function setupVisualEditAgent(options = {}) {
|
|
|
881
954
|
function startMutationObserver() {
|
|
882
955
|
if (state.mutationObserver) return;
|
|
883
956
|
state.mutationObserver = new MutationObserver((mutations) => {
|
|
957
|
+
mutations.forEach((mutation) => {
|
|
958
|
+
mutation.addedNodes.forEach((node) => {
|
|
959
|
+
if (node instanceof Element) {
|
|
960
|
+
trackElementSubtree(node);
|
|
961
|
+
}
|
|
962
|
+
});
|
|
963
|
+
});
|
|
884
964
|
const hasRelevantChange = mutations.some((m) => {
|
|
885
965
|
if (m.type === "attributes" && ["style", "class", "width", "height"].includes(m.attributeName ?? "") && containsTrackedElement(m.target))
|
|
886
966
|
return true;
|
|
@@ -906,8 +986,8 @@ function setupVisualEditAgent(options = {}) {
|
|
|
906
986
|
}
|
|
907
987
|
function containsTrackedElement(node) {
|
|
908
988
|
if (!(node instanceof Element)) return false;
|
|
909
|
-
if (node.
|
|
910
|
-
return
|
|
989
|
+
if (node.closest(`[${AGENT_ATTR}]`)) return false;
|
|
990
|
+
return node.hasAttribute(state.attributeName) || node.hasAttribute("data-visual-selector-id") || !!node.querySelector(trackedSelector);
|
|
911
991
|
}
|
|
912
992
|
function setupSandboxMountObserver() {
|
|
913
993
|
if (window.self === window.top) return;
|
|
@@ -916,7 +996,9 @@ function setupVisualEditAgent(options = {}) {
|
|
|
916
996
|
(m) => m.addedNodes.length > 0 || m.removedNodes.length > 0
|
|
917
997
|
);
|
|
918
998
|
if (!hasChanges) return;
|
|
919
|
-
const hasTrackedElements = document.body.querySelectorAll(
|
|
999
|
+
const hasTrackedElements = document.body.querySelectorAll(
|
|
1000
|
+
`[${state.attributeName}], [data-dynamic-content], [data-visual-selector-id]`
|
|
1001
|
+
).length > 0;
|
|
920
1002
|
try {
|
|
921
1003
|
window.parent.postMessage(
|
|
922
1004
|
{
|
|
@@ -936,18 +1018,24 @@ function setupVisualEditAgent(options = {}) {
|
|
|
936
1018
|
}
|
|
937
1019
|
}
|
|
938
1020
|
function selectElementById(id) {
|
|
939
|
-
const element = document.querySelector(
|
|
1021
|
+
const element = document.querySelector(
|
|
1022
|
+
`[${state.attributeName}="${id}"], [data-visual-selector-id="${id}"]`
|
|
1023
|
+
);
|
|
940
1024
|
if (element) {
|
|
941
1025
|
selectElement(element);
|
|
942
1026
|
}
|
|
943
1027
|
}
|
|
1028
|
+
function clearSelectionState() {
|
|
1029
|
+
clearAllOverlays(state, () => removeLayerDropdown(state));
|
|
1030
|
+
}
|
|
944
1031
|
const onMessage = (e) => {
|
|
945
1032
|
handleMessage(state, e, {
|
|
946
1033
|
enableEditMode,
|
|
947
1034
|
updateAllOverlayPositions,
|
|
948
1035
|
stopInlineEditing: () => stopInlineEditing(state, updateAllOverlayPositions),
|
|
949
1036
|
handleInlineEdit: (data) => handleInlineEdit(state, data, updateAllOverlayPositions),
|
|
950
|
-
selectElementById
|
|
1037
|
+
selectElementById,
|
|
1038
|
+
clearSelectionState
|
|
951
1039
|
});
|
|
952
1040
|
};
|
|
953
1041
|
window.addEventListener("message", onMessage);
|
|
@@ -966,7 +1054,7 @@ function setupVisualEditAgent(options = {}) {
|
|
|
966
1054
|
enableEditMode,
|
|
967
1055
|
selectElement,
|
|
968
1056
|
clearSelection() {
|
|
969
|
-
|
|
1057
|
+
clearSelectionState();
|
|
970
1058
|
},
|
|
971
1059
|
getSelectedId() {
|
|
972
1060
|
return state.selectedId;
|