PluginProbe
Extendify / trunk
Extendify vtrunk
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
← All changes | src/Agent/components/DOMHighlighter.jsx +35 -19 3.1.4 → trunk View file →
@@ -1,5 +1,7 @@
1 1 import { usePortal } from '@agent/hooks/usePortal';
2 +import { blockCodeQueryArgs } from '@agent/lib/block-code';
3 +import { findBlockEl, scopeOf } from '@agent/lib/block-el';
2 4 import { useWorkflowStore } from '@agent/state/workflows';
3 5 import { whenAnimationsSettle } from '@quick-edit/lib/after-animations';
4 6 import { MEDIA_RING, needsContrastRing } from '@quick-edit/lib/over-media';
5 7 import { useQuickEditStore } from '@quick-edit/state/store';
@@ -42,14 +44,13 @@
42 44
43 45 useEffect(() => {
44 46 if (!block?.id) return;
45 47 const ac = new AbortController();
46 - const postId = window.extAgentData?.context?.postId;
47 - if (!postId) return;
48 - const queryArgs = {
49 - postId: String(postId),
50 - blockId: String(block.id),
51 - };
48 + const queryArgs = blockCodeQueryArgs(
49 + block,
50 + window.extAgentData?.context?.postId,
51 + );
52 + if (!queryArgs) return;
52 53
53 54 const isAlive = { current: true };
54 55 (async () => {
55 56 const res = await apiFetch({
@@ -68,12 +69,9 @@
68 69 // Re-syncs the rect for programmatic block changes (e.g. Ask AI)
69 70 // and after the wp-site-blocks open/close transform settles.
70 71 useEffect(() => {
71 72 if (!block?.id) return;
72 - const attr = block.target || 'data-extendify-agent-block-id';
73 - const match = document.querySelector(
74 - `[${attr}="${CSS.escape(String(block.id))}"]`,
75 - );
73 + const match = findBlockEl(block.id, document, scopeOf(block));
76 74 if (!match) return;
77 75 el.current = match;
78 76 setRingNeeded(needsContrastRing(match));
79 77
@@ -122,8 +120,30 @@
122 120 handle,
123 121 );
124 122 }, []);
125 123
124 + useEffect(() => {
125 + if (!block?.id) return;
126 + const handle = () => {
127 + const match = findBlockEl(block.id, document, scopeOf(block));
128 + // Clearing only the rect leaves the chip counting a gone node.
129 + if (!match) {
130 + clearBlock();
131 + return;
132 + }
133 + el.current = match;
134 + const r = match.getBoundingClientRect();
135 + if (r.width <= 0 || r.height <= 0) return;
136 + setRect({ top: r.top, left: r.left, width: r.width, height: r.height });
137 + };
138 + window.addEventListener('extendify-agent:refresh-block-highlight', handle);
139 + return () =>
140 + window.removeEventListener(
141 + 'extendify-agent:refresh-block-highlight',
142 + handle,
143 + );
144 + }, [block, clearBlock]);
145 +
126 146 // Use capture phase for `scroll` so we hear it on any scrollable
127 147 // ancestor (e.g. wp-site-blocks when something repositions it as
128 148 // the page scroll container). Bubble-phase `scroll` doesn't
129 149 // propagate, so a window-only listener misses those.
@@ -152,8 +172,10 @@
152 172
153 173 const resizeObserver = new ResizeObserver(() => {
154 174 if (!el.current) return;
155 175 const { top, left, width, height } = el.current.getBoundingClientRect();
176 + // A detached node reports 0x0, which draws as a corner dot.
177 + if (width <= 0 || height <= 0) return;
156 178 setRect({ top, left, width, height });
157 179 });
158 180
159 181 resizeObserver.observe(el.current);
@@ -162,20 +184,14 @@
162 184 resizeObserver.disconnect();
163 185 };
164 186 }, [el.current]);
165 187
166 - // Workflows can mutate the page while the outline is up: a tool that
167 - // re-renders the block produces a new DOM node with the same
168 - // data-extendify-agent-block-id, and ancestor reflows can shift the
169 - // element without changing its own size (ResizeObserver misses both).
170 - // Re-query and re-measure on any wp-site-blocks subtree mutation,
171 - // rAF-debounced so a burst of mutations costs one measurement.
188 + // ResizeObserver misses a replaced node and an ancestor reflow, so the
189 + // outline stays on the old box.
172 190 useEffect(() => {
173 191 if (!block?.id) return;
174 192 const root = document.querySelector('.wp-site-blocks');
175 193 if (!root) return;
176 - const attr = block.target || 'data-extendify-agent-block-id';
177 - const sel = `[${attr}="${CSS.escape(String(block.id))}"]`;
178 194
179 195 let rafId = 0;
180 196 const observer = new MutationObserver(() => {
181 197 if (rafId) return;
@@ -180,9 +196,9 @@
180 196 const observer = new MutationObserver(() => {
181 197 if (rafId) return;
182 198 rafId = window.requestAnimationFrame(() => {
183 199 rafId = 0;
184 - const match = document.querySelector(sel);
200 + const match = findBlockEl(block.id, document, scopeOf(block));
185 201 if (!match) return;
186 202 el.current = match;
187 203 const r = match.getBoundingClientRect();
188 204 if (r.width <= 0 || r.height <= 0) return;