mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-02 14:28:59 +02:00
[eric] chat: the inline browser hugs the page's aspect and can never paint outside its chat card
This commit is contained in:
@@ -257,6 +257,21 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
|
||||
const hasDockedBrowser = useAppSelector((st) =>
|
||||
Object.values(st.dashboardLayout.browserCards).some((bc) => bc.docked_to === (sessionIdProp || routeId)) ||
|
||||
Object.values(st.dashboardLayout.viewCards).some((vc) => vc.docked_to === (sessionIdProp || routeId)));
|
||||
// The docked surface's aspect ratio, so the inline slot hugs the browser's shape instead of reserving a fixed letterbox band (primitive selectors so no fresh-object rerenders).
|
||||
const dockedSurfaceW = useAppSelector((st) =>
|
||||
Object.values(st.dashboardLayout.browserCards).find((bc) => bc.docked_to === (sessionIdProp || routeId))?.width ?? 0);
|
||||
const dockedSurfaceH = useAppSelector((st) =>
|
||||
Object.values(st.dashboardLayout.browserCards).find((bc) => bc.docked_to === (sessionIdProp || routeId))?.height ?? 0);
|
||||
// Shared by the anchor slot and the fallback slot so both read as the same framed block.
|
||||
const browserSlotSx = {
|
||||
width: '100%',
|
||||
aspectRatio: dockedSurfaceW > 0 && dockedSurfaceH > 0 ? `${dockedSurfaceW} / ${dockedSurfaceH}` : undefined,
|
||||
height: dockedSurfaceW > 0 && dockedSurfaceH > 0 ? 'auto' : 'min(360px, 38vh)',
|
||||
maxHeight: 'min(400px, 42vh)',
|
||||
minHeight: 140,
|
||||
mt: 1,
|
||||
mb: 0.5,
|
||||
} as const;
|
||||
// A card linked as a workflow sidecar (Test Agent, or a watched run) swaps its composer for a Force Stop button: continuing the chat is meaningless, but killing the run is the common need. Once a Test Agent finishes, the button flips to a green "close" (see workflow_test_state + ForceStopAgentBar).
|
||||
const linkedSidecar = useAppSelector((s) => {
|
||||
const found = Object.values(s.workflows.openCards).find(
|
||||
@@ -1861,7 +1876,7 @@ const AgentChat: React.FC<AgentChatProps> = ({ sessionId: sessionIdProp, onClose
|
||||
return (
|
||||
<React.Fragment key={`${item.id}-with-browser`}>
|
||||
{rendered}
|
||||
<Box data-browser-slot={id} sx={{ height: 'min(360px, 38vh)', minHeight: 180, mt: 1, mb: 0.5 }} />
|
||||
<Box data-browser-slot={id} sx={browserSlotSx} />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -2014,10 +2029,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={{ height: 'min(360px, 38vh)', minHeight: 180, mt: 1, mb: 0.5 }}
|
||||
/>
|
||||
<Box data-browser-slot={id} sx={browserSlotSx} />
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -274,7 +274,18 @@ const BrowserCard: React.FC<Props> = ({
|
||||
}
|
||||
const z = getCanvasState().zoom || 1;
|
||||
const lr = layer.getBoundingClientRect();
|
||||
const sr = slot.getBoundingClientRect();
|
||||
let sr: { left: number; top: number; right: number; bottom: number; width: number; height: number } = slot.getBoundingClientRect();
|
||||
// Hard containment: the mini must NEVER paint outside its chat card, whatever a mid-animation or mismeasured slot claims; clamp to the card's real bounds and hide when the overlap collapses.
|
||||
const parentEl = document.querySelector(`[data-select-id="${dockedTo}"]`);
|
||||
if (parentEl) {
|
||||
const pr = parentEl.getBoundingClientRect();
|
||||
const left = Math.max(sr.left, pr.left);
|
||||
const top = Math.max(sr.top, pr.top);
|
||||
const right = Math.min(sr.right, pr.right);
|
||||
const bottom = Math.min(sr.bottom, pr.bottom);
|
||||
if (right - left < 60 || bottom - top < 60) { setDockVisible(false); return; }
|
||||
sr = { left, top, right, bottom, width: right - left, height: bottom - top };
|
||||
}
|
||||
// Slot and card share the transformed layer, so layer-relative coords are camera-invariant.
|
||||
setDockRect({ x: (sr.left - lr.left) / z, y: (sr.top - lr.top) / z, w: sr.width / z, h: sr.height / z });
|
||||
if (scrollHost) {
|
||||
@@ -436,7 +447,8 @@ const BrowserCard: React.FC<Props> = ({
|
||||
})
|
||||
.catch(() => {});
|
||||
try {
|
||||
(wv as any).setVisualZoomLevelLimits?.(1, 1);
|
||||
// Chrome-parity pinch: locked at (1,1) Electron DROPS trackpad pinch entirely, so Figma/Miro/Maps never saw the ctrl+wheel their canvas zoom listens for. Pages that preventDefault it (Figma) own the zoom; plain pages get Chrome's pinch magnify.
|
||||
(wv as any).setVisualZoomLevelLimits?.(1, 3);
|
||||
(wv as any).setZoomFactor?.(1);
|
||||
} catch (_) {}
|
||||
};
|
||||
@@ -449,6 +461,11 @@ const BrowserCard: React.FC<Props> = ({
|
||||
};
|
||||
wv.addEventListener('dom-ready', onReady, { once: true });
|
||||
cleanups.push(() => wv.removeEventListener('dom-ready', onReady));
|
||||
// The preload re-runs on every full navigation, so re-tag the guest each dom-ready: browser surfaces keep ctrl/meta+wheel (pinch) IN the page instead of forwarding it to canvas zoom.
|
||||
const tagSurface = () => { try { (wv as any).send?.('openswarm:set-surface', { kind: 'browser' }); } catch (_) {} };
|
||||
tagSurface();
|
||||
wv.addEventListener('dom-ready', tagSurface);
|
||||
cleanups.push(() => wv.removeEventListener('dom-ready', tagSurface));
|
||||
}
|
||||
|
||||
// Every guest sits at about:blank before its real load (lazy tabs never leave it); mirroring
|
||||
|
||||
Reference in New Issue
Block a user