mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-23 18:14:53 +02:00
[eric] dashboard: app cards glow + tether while an AppAgent drives them, same treatment as browser cards (glow keys app:<output_id>, set on the child's status event, fades on parent terminal)
This commit is contained in:
@@ -132,6 +132,9 @@ const DashboardViewCard: React.FC<Props> = ({
|
||||
const scrollOverlayRef = useOverlayScrollPassthrough(isSelected);
|
||||
const previewRef = useRef<ViewPreviewHandle>(null);
|
||||
const activeViewCardId = useAppSelector((s) => s.dashboardLayout.activeViewCardId);
|
||||
// Agent-driving glow, same treatment as browser cards: an AppAgent session carries browser_id "app:<output_id>", which keys glowingBrowserCards.
|
||||
const appGlow = useAppSelector((s) => s.dashboardLayout.glowingBrowserCards[`app:${cardKeyProp ?? output.id}`]);
|
||||
const showAgentGlow = !!appGlow && !appGlow.fading;
|
||||
const interactive = activeViewCardId === cardKey;
|
||||
|
||||
// Deselecting the card exits interact mode (click anywhere else on canvas).
|
||||
@@ -445,22 +448,26 @@ const DashboardViewCard: React.FC<Props> = ({
|
||||
borderRadius: `${c.radius.lg}px`,
|
||||
border: isHighlighted
|
||||
? `2px solid ${c.accent.primary}`
|
||||
: interactive
|
||||
: showAgentGlow
|
||||
? `2px solid ${c.accent.primary}`
|
||||
: isSelected ? '2px solid #3b82f6' : `1px solid ${c.border.medium}`,
|
||||
: interactive
|
||||
? `2px solid ${c.accent.primary}`
|
||||
: isSelected ? '2px solid #3b82f6' : `1px solid ${c.border.medium}`,
|
||||
bgcolor: c.bg.surface,
|
||||
boxShadow: isHighlighted
|
||||
? `0 0 0 3px ${c.accent.primary}50, 0 0 20px ${c.accent.primary}35, 0 0 40px ${c.accent.primary}15`
|
||||
: isDragging || isResizing
|
||||
? c.shadow.lg
|
||||
: isSelected
|
||||
? `0 0 0 1px #3b82f6, ${c.shadow.md}`
|
||||
: c.shadow.md,
|
||||
: showAgentGlow
|
||||
? `0 0 0 2px ${c.accent.primary}40, 0 0 18px ${c.accent.primary}30, 0 0 40px ${c.accent.primary}15, inset 0 0 30px ${c.accent.primary}25`
|
||||
: isDragging || isResizing
|
||||
? c.shadow.lg
|
||||
: isSelected
|
||||
? `0 0 0 1px #3b82f6, ${c.shadow.md}`
|
||||
: c.shadow.md,
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
zIndex: (isDragging || isResizing) ? 999999 : cardZOrder,
|
||||
transition: noTransition ? 'none' : 'box-shadow 0.2s',
|
||||
transition: noTransition ? 'none' : 'box-shadow 0.4s ease, border 0.3s ease',
|
||||
'&:hover .resize-handle': { opacity: 1 },
|
||||
...(isHighlighted && {
|
||||
animation: 'card-highlight-pulse 2s ease-out forwards',
|
||||
|
||||
@@ -258,9 +258,11 @@ export function useTethers({
|
||||
}
|
||||
|
||||
const glowTethers = new Map<string, ReturnType<typeof cardTether>>();
|
||||
// An "app:<output_id>" glow key targets a VIEW card (AppAgent driving an app); everything else is a browser card.
|
||||
const glowTarget = (id: string) => (id.startsWith('app:') ? viewCards[id.slice(4)] : browserCards[id]);
|
||||
for (const [browserId, { sourceId, fading, label }] of Object.entries(glowingBrowserCards)) {
|
||||
const t = cardTether(
|
||||
browserCards[browserId],
|
||||
glowTarget(browserId),
|
||||
browserId,
|
||||
sourceId,
|
||||
`browser-${browserId}`,
|
||||
@@ -278,7 +280,7 @@ export function useTethers({
|
||||
// A browser docked below the hub keeps a "Browser" pointer so the link reads at a glance; the right-docked agent/run cases stay label-free (their glow already said it on spawn).
|
||||
const parent = sessionById.get(s.parent_session_id);
|
||||
const t = cardTether(
|
||||
browserCards[s.browser_id],
|
||||
glowTarget(s.browser_id),
|
||||
s.browser_id,
|
||||
s.parent_session_id,
|
||||
`browser-${s.browser_id}`,
|
||||
|
||||
@@ -384,6 +384,17 @@ class WebSocketManager {
|
||||
store.dispatch(trackAgentNotification(session_id));
|
||||
}
|
||||
|
||||
// An AppAgent driving an app card announces itself only via this status event (no card_added like browsers), so light the app card here. Keyed by the parent chat like browser glows, so the same terminal fade below clears it.
|
||||
const p_sess = data.session;
|
||||
if (p_sess && p_sess.mode === 'browser-agent' && typeof p_sess.browser_id === 'string' && p_sess.browser_id.startsWith('app:')
|
||||
&& (p_sess.status === 'running' || p_sess.status === 'waiting_approval')) {
|
||||
store.dispatch(setGlowingBrowserCards({
|
||||
browserIds: [p_sess.browser_id],
|
||||
sessionId: p_sess.parent_session_id || p_sess.id,
|
||||
label: 'Use App',
|
||||
}));
|
||||
}
|
||||
|
||||
// Fade this session's browser glows on the terminal transition HERE, not only in AgentChat's effect: a collapsed chat is unmounted at finish, and a never-faded glow pins the browser's renderer (exempt from suspend + the webview cap) forever.
|
||||
const newStatus = data.status ?? data.session?.status;
|
||||
const wasWorking = prevStatus === 'running' || prevStatus === 'waiting_approval';
|
||||
|
||||
Reference in New Issue
Block a user