mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[eric] browser: agent clicks stop hijacking last-interacted targeting (Ctrl+R, zoom, dictation fallback); .swarm app import refreshes the dock
This commit is contained in:
@@ -10,6 +10,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
import { fetchOutputs } from '@/shared/state/outputsSlice';
|
||||
import { fetchWorkflows } from '@/shared/state/workflowsSlice';
|
||||
|
||||
import ImportDigest, { DigestHandle } from './ImportDigest';
|
||||
@@ -62,6 +63,8 @@ const ImportEntryPoint: React.FC = () => {
|
||||
setToast({ msg, sev: 'success' });
|
||||
// A workflow has no route of its own, so nothing would pull it in: an open Workflows hub only fetches on mount and would keep showing a stale list. Import drops dashboard_id, and /list keeps unassigned workflows for every dashboard, so this surfaces it wherever the user is.
|
||||
if (rootType === 'workflow') dispatch(fetchWorkflows(dashboardId));
|
||||
// Same staleness for apps: the Apps dock reads the outputs slice, which nothing refetches on import.
|
||||
if (rootType === 'app') dispatch(fetchOutputs());
|
||||
const to = DEST[rootType]?.(rootId);
|
||||
if (to) navigate(to);
|
||||
},
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
} from '@/shared/browserRegistry';
|
||||
import { captureBrowserShot } from '@/shared/captureBrowserShot';
|
||||
import { setLastInteractedBrowser } from '@/shared/browserFocus';
|
||||
import { isAgentDrivenBrowser } from '@/shared/isAgentDrivenBrowser';
|
||||
import { registerCapsuleForRestore } from '@/shared/browserStateCapsule';
|
||||
import BrowserFindBar from './BrowserFindBar';
|
||||
import { openCardContextMenu, isNativeMenuTarget } from '../desktop/openCardContextMenu';
|
||||
@@ -562,8 +563,11 @@ const BrowserCard: React.FC<Props> = ({
|
||||
);
|
||||
} else if (e?.channel === 'app-clicked') {
|
||||
// In-guest mousedown: a page click never reaches the host document, so this IPC is how a webview-content click marks this browser as last-interacted (drives Ctrl+R/zoom/tab targeting) and selected (spawn-beside anchor).
|
||||
setLastInteractedBrowser(browserId);
|
||||
window.dispatchEvent(new CustomEvent('openswarm:browser-guest-select', { detail: { browserId } }));
|
||||
// The guest fires this for the AGENT's clicks too; those must not hijack targeting (or dictation's fallback types into the agent's page).
|
||||
if (!isAgentDrivenBrowser(browserId)) {
|
||||
setLastInteractedBrowser(browserId);
|
||||
window.dispatchEvent(new CustomEvent('openswarm:browser-guest-select', { detail: { browserId } }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useRef, type Dispatch, type SetStateActi
|
||||
import { report } from '@/shared/serviceClient';
|
||||
import { useAppDispatch } from '@/shared/hooks';
|
||||
import { store } from '@/shared/state/store';
|
||||
import { isAgentDrivenBrowser } from '@/shared/isAgentDrivenBrowser';
|
||||
import { expandSession } from '@/shared/state/agentsSlice';
|
||||
import { bringToFront } from '@/shared/state/dashboardLayoutSlice';
|
||||
import { setScrollFocusedCard } from '@/shared/cardScrollFocus';
|
||||
@@ -145,13 +146,7 @@ export function useDashboardInteractions({
|
||||
// Mid-drag/marquee a selection change joins the card to the multi-drag (the browser visibly chased the cursor); the shield class is up for exactly that window.
|
||||
if (document.body.classList.contains('dashboard-marquee-active')) return;
|
||||
// The guest preload fires app-clicked for the AGENT's clicks too; a working agent driving its own page must not steal selection (it also re-anchored spawn-beside onto its browser).
|
||||
const st = store.getState();
|
||||
const working = (s?: { status?: string }) => !!s && (s.status === 'running' || s.status === 'waiting_approval');
|
||||
const glow = st.dashboardLayout.glowingBrowserCards[browserId];
|
||||
const agentDriven =
|
||||
Object.values(st.agents.sessions).some((s) => s.browser_id === browserId && working(s)) ||
|
||||
(!!glow && !glow.fading && working(st.agents.sessions[glow.sourceId]));
|
||||
if (agentDriven) return;
|
||||
if (isAgentDrivenBrowser(browserId)) return;
|
||||
selection.selectCard(browserId, 'browser', false);
|
||||
dispatch(bringToFront({ id: browserId, type: 'browser' }));
|
||||
// In-guest clicks never reach the host capture handler, so mark the browser focused here, mainly to UN-focus any chat so scroll over other cards behaves right (the browser's own page scroll/zoom is native regardless).
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { store } from '@/shared/state/store';
|
||||
|
||||
// A browser an agent is actively working: its guest clicks are the AGENT's, not the user's, so they
|
||||
// must not steal selection, last-interacted targeting (Ctrl+R, zoom, dictation fallback), or z-order.
|
||||
export function isAgentDrivenBrowser(browserId: string): boolean {
|
||||
const st = store.getState();
|
||||
const working = (s?: { status?: string }) => !!s && (s.status === 'running' || s.status === 'waiting_approval');
|
||||
const glow = st.dashboardLayout.glowingBrowserCards[browserId];
|
||||
return (
|
||||
Object.values(st.agents.sessions).some((s) => s.browser_id === browserId && working(s)) ||
|
||||
(!!glow && !glow.fading && working(st.agents.sessions[glow.sourceId]))
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user