PluginProbe
Extendify / 3.2.2
Extendify v3.2.2
3.2.2 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 All 128 releases
← All changes | src/Agent/components/DOMHighlighter.jsx +34 -24 3.1.5 → 3.2.2 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';
@@ -22,9 +24,14 @@
22 24 // Render-only after the selector unification: `agentBlock` is set
23 25 // by Quick Edit's Ask AI flow (or future workflows), and this component
24 26 // draws the outline + X-close indicator. Hover-bar owns hover + click
25 27 // selection on the live page; DOMHighlighter no longer listens for either.
26 -export const DOMHighlighter = ({ busy = false, working = false }) => {
28 +export const DOMHighlighter = ({
29 + busy = false,
30 + working = false,
31 + waiting = false,
32 + taskActive = false,
33 +}) => {
27 34 const [rect, setRect] = useState(null);
28 35 const [ringNeeded, setRingNeeded] = useState(false);
29 36 const mountNode = usePortal('extendify-agent-dom-mount');
30 37 const el = useRef(null);
@@ -42,14 +49,13 @@
42 49
43 50 useEffect(() => {
44 51 if (!block?.id) return;
45 52 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 - };
53 + const queryArgs = blockCodeQueryArgs(
54 + block,
55 + window.extAgentData?.context?.postId,
56 + );
57 + if (!queryArgs) return;
52 58
53 59 const isAlive = { current: true };
54 60 (async () => {
55 61 const res = await apiFetch({
@@ -68,12 +74,9 @@
68 74 // Re-syncs the rect for programmatic block changes (e.g. Ask AI)
69 75 // and after the wp-site-blocks open/close transform settles.
70 76 useEffect(() => {
71 77 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 - );
78 + const match = findBlockEl(block.id, document, scopeOf(block));
76 79 if (!match) return;
77 80 el.current = match;
78 81 setRingNeeded(needsContrastRing(match));
79 82
@@ -124,13 +127,10 @@
124 127 }, []);
125 128
126 129 useEffect(() => {
127 130 if (!block?.id) return;
128 - const attr = block.target || 'data-extendify-agent-block-id';
129 131 const handle = () => {
130 - const match = document.querySelector(
131 - `[${attr}="${CSS.escape(String(block.id))}"]`,
132 - );
132 + const match = findBlockEl(block.id, document, scopeOf(block));
133 133 // Clearing only the rect leaves the chip counting a gone node.
134 134 if (!match) {
135 135 clearBlock();
136 136 return;
@@ -189,20 +189,14 @@
189 189 resizeObserver.disconnect();
190 190 };
191 191 }, [el.current]);
192 192
193 - // Workflows can mutate the page while the outline is up: a tool that
194 - // re-renders the block produces a new DOM node with the same
195 - // data-extendify-agent-block-id, and ancestor reflows can shift the
196 - // element without changing its own size (ResizeObserver misses both).
197 - // Re-query and re-measure on any wp-site-blocks subtree mutation,
198 - // rAF-debounced so a burst of mutations costs one measurement.
193 + // ResizeObserver misses a replaced node and an ancestor reflow, so the
194 + // outline stays on the old box.
199 195 useEffect(() => {
200 196 if (!block?.id) return;
201 197 const root = document.querySelector('.wp-site-blocks');
202 198 if (!root) return;
203 - const attr = block.target || 'data-extendify-agent-block-id';
204 - const sel = `[${attr}="${CSS.escape(String(block.id))}"]`;
205 199
206 200 let rafId = 0;
207 201 const observer = new MutationObserver(() => {
208 202 if (rafId) return;
@@ -207,9 +201,9 @@
207 201 const observer = new MutationObserver(() => {
208 202 if (rafId) return;
209 203 rafId = window.requestAnimationFrame(() => {
210 204 rafId = 0;
211 - const match = document.querySelector(sel);
205 + const match = findBlockEl(block.id, document, scopeOf(block));
212 206 if (!match) return;
213 207 el.current = match;
214 208 const r = match.getBoundingClientRect();
215 209 if (r.width <= 0 || r.height <= 0) return;
@@ -246,8 +240,24 @@
246 240 if (!root) return;
247 241 root.classList.add('extendify-agent-busy');
248 242 return () => root.classList.remove('extendify-agent-busy');
249 243 }, [busy]);
244 +
245 + useEffect(() => {
246 + if (!taskActive) return;
247 + const root = document.querySelector('.wp-site-blocks');
248 + if (!root) return;
249 + root.classList.add('extendify-agent-task');
250 + return () => root.classList.remove('extendify-agent-task');
251 + }, [taskActive]);
252 +
253 + useEffect(() => {
254 + if (!waiting) return;
255 + const root = document.querySelector('.wp-site-blocks');
256 + if (!root) return;
257 + root.classList.add('extendify-agent-waiting');
258 + return () => root.classList.remove('extendify-agent-waiting');
259 + }, [waiting]);
250 260
251 261 useEffect(() => {
252 262 if (!working) return;
253 263 const root = document.querySelector('.wp-site-blocks');