[eric] browser: the docked mini re-measures when its slot announces a remount, retiring a 150ms forever-poll of layout reads

This commit is contained in:
ciregenz
2026-08-08 09:47:25 -07:00
parent 4d86b3377d
commit c0d33c0cfb
2 changed files with 14 additions and 5 deletions
@@ -292,6 +292,10 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
// The live overlay stamps data-mini-live; while it paints, the frozen-shot backdrop must not (the clamped overlay leaves margins where a misaligned second copy of the page peeked through).
'&[data-mini-live="1"] img': { opacity: 0 },
} as const;
// The slot announces its own (re)mount so the docked mini re-measures exactly then, instead of polling rects on a timer (the windowed transcript remounts it with no resize/pan event firing).
const announceBrowserSlot = useCallback((el: HTMLElement | null) => {
if (el) window.dispatchEvent(new CustomEvent('openswarm:browser-slot-mounted', { detail: { id } }));
}, [id]);
// A live webview cannot be clipped by the scroller, so the OVERLAY only shows while fully in view; this frozen shot is what scrolls and clips underneath it, ChatGPT-style.
const dockedShot = dockedSurfaceId ? getMinimizedShot(dockedSurfaceId) : undefined;
const browserSlotBody = dockedShot ? (
@@ -1937,7 +1941,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
return (
<React.Fragment key={`${item.id}-with-browser`}>
{rendered}
<Box data-browser-slot={id} sx={browserSlotSx}>{browserSlotBody}</Box>
<Box data-browser-slot={id} ref={announceBrowserSlot} sx={browserSlotSx}>{browserSlotBody}</Box>
</React.Fragment>
);
}
@@ -2090,7 +2094,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
)}
{/* Fallback dock slot for a browser that docked before any browser tool row exists (or whose row was compacted away); once a row appears the slot anchors at it instead (see browserAnchorItemId). The real card overlays this rect geometrically, so the webview never remounts; the mini hides itself when its slot scrolls mostly out of view, since a live webview can't be clipped by the scroller. */}
{hasDockedBrowser && !browserAnchorItemId && (
<Box data-browser-slot={id} sx={browserSlotSx}>{browserSlotBody}</Box>
<Box data-browser-slot={id} ref={announceBrowserSlot} sx={browserSlotSx}>{browserSlotBody}</Box>
)}
</Box>
</Box>
@@ -310,18 +310,23 @@ const BrowserCard: React.FC<Props> = ({
window.addEventListener('openswarm:canvas-pan-changed', measure);
document.addEventListener('visibilitychange', measure);
const timers = [60, 250, 700].map((ms) => window.setTimeout(measure, ms));
// Self-heal: the slot lives in the WINDOWED transcript and remounts without firing any of the events above, which left the mini scaled against a stale rect (visibly overflowing its frame) until the next pan. 150ms bounds how long any staleness can survive.
const heal = window.setInterval(measure, 150);
// The slot lives in the WINDOWED transcript and remounts without firing any of the events
// above; it announces itself on mount now, which retired a 150ms forever-poll that forced a
// layout read ~7x/sec per docked mini even while nothing moved (measure once per real change).
const onSlotMounted = (e: Event): void => {
if ((e as CustomEvent).detail?.id === dockedTo) measure();
};
window.addEventListener('openswarm:browser-slot-mounted', onSlotMounted);
return () => {
ro.disconnect();
mo.disconnect();
window.removeEventListener('resize', measure);
window.removeEventListener('openswarm:canvas-pan-changed', measure);
document.removeEventListener('visibilitychange', measure);
window.removeEventListener('openswarm:browser-slot-mounted', onSlotMounted);
scrollHost?.removeEventListener('scroll', onScroll);
if (scrollRaf) cancelAnimationFrame(scrollRaf);
timers.forEach((tm) => window.clearTimeout(tm));
window.clearInterval(heal);
};
// dockParentCard x/y/w/h are re-measure triggers: the slot's client rect moves with the chat card.
}, [dockedTo, dockParentExpanded, dockParentTiled, dockParentCard?.x, dockParentCard?.y, dockParentCard?.width, dockParentCard?.height, getCanvasState, dockParentCard]);