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 CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from 'vite';
2
- import { V as VisualSelectorPluginOptions } from './runtime-CpZH1D8y.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, T as ToggleInlineEditModeMessage, f as ToggleVisualEditModeMessage, U as UnselectElementMessage, g as UpdateAttributeMessage, h as UpdateClassesMessage, i as UpdateContentMessage, j as UpdateThemeVariablesMessage, k as VisualEditAgentInstance, l as VisualEditAgentOptions, m as VisualSelectorControlMessage, s as setupVisualEditAgent } from './runtime-CpZH1D8y.js';
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
- state.selectionOverlays.forEach((o) => o.remove());
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) {
@@ -510,6 +356,11 @@ function handleMessage(state, e, callbacks) {
510
356
  case "request-element-position":
511
357
  reportPositionUpdate(state);
512
358
  break;
359
+ case "select-element-by-id":
360
+ if (msg.data?.visualSelectorId) {
361
+ callbacks.selectElementById(msg.data.visualSelectorId);
362
+ }
363
+ break;
513
364
  case CONTROL_MESSAGE_TYPE: {
514
365
  const controlMsg = msg;
515
366
  if (typeof controlMsg.active === "boolean") {
@@ -560,6 +411,27 @@ function createOverlay(opts) {
560
411
  }
561
412
  return overlay;
562
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
+ }
563
435
  function positionOverlay(overlay, target, tagMode = "none", callbacks) {
564
436
  const rect = target.getBoundingClientRect();
565
437
  overlay.style.top = `${rect.top + window.scrollY}px`;
@@ -582,8 +454,10 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
582
454
  textAlign: "center",
583
455
  display: "flex",
584
456
  alignItems: "center",
457
+ justifyContent: "center",
585
458
  gap: "3px",
586
- lineHeight: "1.4"
459
+ lineHeight: "1.4",
460
+ whiteSpace: "nowrap"
587
461
  });
588
462
  if (tagMode === "selected") {
589
463
  tag.style.backgroundColor = "#FC0";
@@ -592,12 +466,23 @@ function positionOverlay(overlay, target, tagMode = "none", callbacks) {
592
466
  tag.style.pointerEvents = "auto";
593
467
  const textSpan = document.createElement("span");
594
468
  textSpan.textContent = target.tagName.toLowerCase();
469
+ textSpan.style.display = "inline-flex";
470
+ textSpan.style.alignItems = "center";
471
+ textSpan.style.lineHeight = "1";
595
472
  tag.appendChild(textSpan);
596
473
  const arrow = document.createElement("span");
597
474
  arrow.setAttribute("data-tag-arrow", "");
598
- arrow.textContent = "\u2304";
599
- arrow.style.fontSize = "10px";
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";
600
483
  arrow.style.lineHeight = "1";
484
+ arrow.appendChild(createTagArrowSvg());
485
+ setTagArrowExpanded(arrow, false);
601
486
  tag.appendChild(arrow);
602
487
  if (callbacks?.onTagClick) {
603
488
  const onTagClick = callbacks.onTagClick;
@@ -720,6 +605,171 @@ function unfreezeAnimations() {
720
605
  if (overflowStyle) overflowStyle.remove();
721
606
  }
722
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
+
723
773
  // src/runtime/state.ts
724
774
  function createAgentState(options = {}) {
725
775
  return {
@@ -733,13 +783,36 @@ function createAgentState(options = {}) {
733
783
  layerDropdown: null,
734
784
  mutationObserver: null,
735
785
  attributeName: options.attributeName ?? DEFAULT_ATTRIBUTE_NAME,
736
- targetOrigin: options.targetOrigin ?? "*"
786
+ targetOrigin: options.targetOrigin ?? "*",
787
+ runtimeIdCounter: 0
737
788
  };
738
789
  }
739
790
 
740
791
  // src/runtime/index.ts
741
792
  function setupVisualEditAgent(options = {}) {
742
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
+ }
743
816
  function updateAllOverlayPositions() {
744
817
  if (state.selectedId && state.selectedElement?.isConnected) {
745
818
  const siblings = findAllElementsById(state.selectedId);
@@ -756,7 +829,7 @@ function setupVisualEditAgent(options = {}) {
756
829
  function onTagClick(target, tag) {
757
830
  const arrowEl = tag.querySelector("[data-tag-arrow]");
758
831
  if (arrowEl) {
759
- arrowEl.textContent = state.layerDropdown ? "\u2304" : "\u2303";
832
+ setTagArrowExpanded(arrowEl, !state.layerDropdown);
760
833
  }
761
834
  toggleLayerDropdown(state, target, tag, selectElement);
762
835
  }
@@ -767,7 +840,10 @@ function setupVisualEditAgent(options = {}) {
767
840
  if (freezeStyle) freezeStyle.disabled = false;
768
841
  if (!element) return null;
769
842
  if (element.closest(`[${AGENT_ATTR}]`)) return null;
770
- return element.closest(`[${state.attributeName}], [data-visual-selector-id]`) ?? null;
843
+ const trackedElement = element.closest(trackedSelector);
844
+ if (trackedElement) return trackedElement;
845
+ ensureElementTracked(element);
846
+ return element.hasAttribute("data-visual-selector-id") ? element : null;
771
847
  }
772
848
  function findHoverTarget(x, y, excludeId) {
773
849
  const element = findElementAtPoint(x, y);
@@ -815,6 +891,7 @@ function setupVisualEditAgent(options = {}) {
815
891
  selectElement(element);
816
892
  }
817
893
  function selectElement(element) {
894
+ ensureElementTracked(element);
818
895
  const id = getSourceId(element);
819
896
  if (!id) return;
820
897
  if (state.editingElement) {
@@ -850,6 +927,7 @@ function setupVisualEditAgent(options = {}) {
850
927
  if (enabled) {
851
928
  document.body.style.cursor = "crosshair";
852
929
  freezeAnimations();
930
+ trackCurrentDom();
853
931
  document.addEventListener("mousemove", onMouseMove);
854
932
  document.addEventListener("mouseleave", onMouseLeave);
855
933
  document.addEventListener("click", onClick, true);
@@ -876,6 +954,13 @@ function setupVisualEditAgent(options = {}) {
876
954
  function startMutationObserver() {
877
955
  if (state.mutationObserver) return;
878
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
+ });
879
964
  const hasRelevantChange = mutations.some((m) => {
880
965
  if (m.type === "attributes" && ["style", "class", "width", "height"].includes(m.attributeName ?? "") && containsTrackedElement(m.target))
881
966
  return true;
@@ -901,8 +986,8 @@ function setupVisualEditAgent(options = {}) {
901
986
  }
902
987
  function containsTrackedElement(node) {
903
988
  if (!(node instanceof Element)) return false;
904
- if (node.hasAttribute(AGENT_ATTR)) return false;
905
- return hasSourceLocation(node) || !!node.querySelector(`[${state.attributeName}]`);
989
+ if (node.closest(`[${AGENT_ATTR}]`)) return false;
990
+ return node.hasAttribute(state.attributeName) || node.hasAttribute("data-visual-selector-id") || !!node.querySelector(trackedSelector);
906
991
  }
907
992
  function setupSandboxMountObserver() {
908
993
  if (window.self === window.top) return;
@@ -911,7 +996,9 @@ function setupVisualEditAgent(options = {}) {
911
996
  (m) => m.addedNodes.length > 0 || m.removedNodes.length > 0
912
997
  );
913
998
  if (!hasChanges) return;
914
- const hasTrackedElements = document.body.querySelectorAll("[data-source-location], [data-dynamic-content]").length > 0;
999
+ const hasTrackedElements = document.body.querySelectorAll(
1000
+ `[${state.attributeName}], [data-dynamic-content], [data-visual-selector-id]`
1001
+ ).length > 0;
915
1002
  try {
916
1003
  window.parent.postMessage(
917
1004
  {
@@ -930,12 +1017,25 @@ function setupVisualEditAgent(options = {}) {
930
1017
  });
931
1018
  }
932
1019
  }
1020
+ function selectElementById(id) {
1021
+ const element = document.querySelector(
1022
+ `[${state.attributeName}="${id}"], [data-visual-selector-id="${id}"]`
1023
+ );
1024
+ if (element) {
1025
+ selectElement(element);
1026
+ }
1027
+ }
1028
+ function clearSelectionState() {
1029
+ clearAllOverlays(state, () => removeLayerDropdown(state));
1030
+ }
933
1031
  const onMessage = (e) => {
934
1032
  handleMessage(state, e, {
935
1033
  enableEditMode,
936
1034
  updateAllOverlayPositions,
937
1035
  stopInlineEditing: () => stopInlineEditing(state, updateAllOverlayPositions),
938
- handleInlineEdit: (data) => handleInlineEdit(state, data, updateAllOverlayPositions)
1036
+ handleInlineEdit: (data) => handleInlineEdit(state, data, updateAllOverlayPositions),
1037
+ selectElementById,
1038
+ clearSelectionState
939
1039
  });
940
1040
  };
941
1041
  window.addEventListener("message", onMessage);
@@ -954,7 +1054,7 @@ function setupVisualEditAgent(options = {}) {
954
1054
  enableEditMode,
955
1055
  selectElement,
956
1056
  clearSelection() {
957
- clearAllOverlays(state, () => removeLayerDropdown(state));
1057
+ clearSelectionState();
958
1058
  },
959
1059
  getSelectedId() {
960
1060
  return state.selectedId;