mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 12:17:45 +02:00
[aidan] ux: when agent creates app always opens in dashboard
This commit is contained in:
@@ -38,6 +38,7 @@ interface UseAgentSpawnArgs {
|
||||
setToolbarOpen: Dispatch<SetStateAction<boolean>>;
|
||||
setAutoFocusSessionId: Dispatch<SetStateAction<string | null>>;
|
||||
setPendingSelectSessionId: Dispatch<SetStateAction<string | null>>;
|
||||
pendingViewBuilderSessionsRef: RefObject<Set<string>>;
|
||||
}
|
||||
|
||||
export function useAgentSpawn({
|
||||
@@ -54,6 +55,7 @@ export function useAgentSpawn({
|
||||
setToolbarOpen,
|
||||
setAutoFocusSessionId,
|
||||
setPendingSelectSessionId,
|
||||
pendingViewBuilderSessionsRef,
|
||||
}: UseAgentSpawnArgs) {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
@@ -155,6 +157,12 @@ export function useAgentSpawn({
|
||||
).then((action) => {
|
||||
if (launchAndSendFirstMessage.fulfilled.match(action)) {
|
||||
const realId = action.payload.session.id;
|
||||
// App Builder chats expect a view card to pop in next to the chat
|
||||
// once the backend seeds its Output row. Register the session id
|
||||
// so useDashboardLifecycle's output watcher can do the auto-open.
|
||||
if (mode === 'view-builder') {
|
||||
pendingViewBuilderSessionsRef.current?.add(realId);
|
||||
}
|
||||
dispatch(generateTitle({ sessionId: realId, prompt }));
|
||||
if (selectedBrowserIds?.length) {
|
||||
dispatch(setGlowingBrowserCards({ browserIds: selectedBrowserIds, sessionId: realId, label: 'Use Browser' }));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useRef, type MutableRefObject } from 'react';
|
||||
import { useEffect, useRef, type MutableRefObject, type RefObject } from 'react';
|
||||
import { report } from '@/shared/serviceClient';
|
||||
import { store } from '@/shared/state/store';
|
||||
import { useAppDispatch, useAppSelector } from '@/shared/hooks';
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
fetchLayout,
|
||||
reconcileSessions,
|
||||
addBrowserCard,
|
||||
addViewCard,
|
||||
resetLayout,
|
||||
removeViewCard,
|
||||
clearPendingFocusBrowserId,
|
||||
@@ -39,6 +40,7 @@ interface UseDashboardLifecycleArgs {
|
||||
handleHighlightCard: (cardId: string) => void;
|
||||
hasFittedRef: MutableRefObject<boolean>;
|
||||
restoredExpandedRef: MutableRefObject<boolean>;
|
||||
pendingViewBuilderSessionsRef: RefObject<Set<string>>;
|
||||
}
|
||||
|
||||
export function useDashboardLifecycle({
|
||||
@@ -55,6 +57,7 @@ export function useDashboardLifecycle({
|
||||
handleHighlightCard,
|
||||
hasFittedRef,
|
||||
restoredExpandedRef,
|
||||
pendingViewBuilderSessionsRef,
|
||||
}: UseDashboardLifecycleArgs) {
|
||||
const dispatch = useAppDispatch();
|
||||
const pendingBrowserUrl = useAppSelector((state) => state.tempState.pendingBrowserUrl);
|
||||
@@ -253,6 +256,40 @@ export function useDashboardLifecycle({
|
||||
}
|
||||
}, [layoutInitialized, outputsLoaded, viewCards, outputs, dispatch]);
|
||||
|
||||
// App Builder auto-open: when a view-builder session launched on this
|
||||
// dashboard, useAgentSpawn parks its id in pendingViewBuilderSessionsRef.
|
||||
// The backend then seeds an Output row and broadcasts agent:output_upserted;
|
||||
// when that lands in outputsSlice we drop a ViewCard next to the chat so the
|
||||
// user sees the app appear without a manual "open" step. Per-mount tracked
|
||||
// (not redux) so a manual close after auto-open stays closed.
|
||||
useEffect(() => {
|
||||
if (!layoutInitialized) return;
|
||||
const pending = pendingViewBuilderSessionsRef.current;
|
||||
if (!pending || pending.size === 0) return;
|
||||
for (const output of Object.values(outputs)) {
|
||||
const sid = output.session_id;
|
||||
if (!sid || !pending.has(sid)) continue;
|
||||
if (viewCards[output.id]) {
|
||||
pending.delete(sid);
|
||||
continue;
|
||||
}
|
||||
// Let addViewCard pick a collision-free grid cell; the auto-fit below
|
||||
// pans/zooms to show the chat + new view card together regardless.
|
||||
dispatch(addViewCard({ outputId: output.id, expandedSessionIds }));
|
||||
pending.delete(sid);
|
||||
const outputId = output.id;
|
||||
setTimeout(() => {
|
||||
const vc = store.getState().dashboardLayout.viewCards[outputId];
|
||||
if (!vc) return;
|
||||
const rects = [{ x: vc.x, y: vc.y, width: vc.width, height: vc.height }];
|
||||
const ac = store.getState().dashboardLayout.cards[sid];
|
||||
if (ac) rects.push({ x: ac.x, y: ac.y, width: ac.width, height: ac.height });
|
||||
canvasActions.fitToCards(rects, 1.15, true);
|
||||
handleHighlightCard(outputId);
|
||||
}, 200);
|
||||
}
|
||||
}, [layoutInitialized, outputs, viewCards, expandedSessionIds, dispatch, canvasActions, handleHighlightCard, pendingViewBuilderSessionsRef]);
|
||||
|
||||
const namedOnFirstMessageRef = useRef<string | null>(null);
|
||||
useEffect(() => {
|
||||
if (!dashboardId) return;
|
||||
|
||||
@@ -113,6 +113,13 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
|
||||
contentRef: canvas.contentRef,
|
||||
});
|
||||
|
||||
// Session ids that just launched in App Builder ('view-builder') mode and
|
||||
// are still awaiting their backend-seeded Output row. useAgentSpawn writes
|
||||
// here; useDashboardLifecycle consumes it to auto-open the view card the
|
||||
// moment the output upsert arrives. Per-mount ref (not redux) so a manual
|
||||
// close after auto-open stays closed.
|
||||
const pendingViewBuilderSessionsRef = useRef<Set<string>>(new Set());
|
||||
|
||||
useDashboardLifecycle({
|
||||
isActive,
|
||||
dashboardId,
|
||||
@@ -127,6 +134,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
|
||||
handleHighlightCard,
|
||||
hasFittedRef,
|
||||
restoredExpandedRef,
|
||||
pendingViewBuilderSessionsRef,
|
||||
});
|
||||
|
||||
// ---- Auto-reveal / collapse / unreveal sub-agent cards ----
|
||||
@@ -221,6 +229,7 @@ export function useDashboardController(dashboardId: string, isActive: boolean) {
|
||||
setToolbarOpen,
|
||||
setAutoFocusSessionId,
|
||||
setPendingSelectSessionId,
|
||||
pendingViewBuilderSessionsRef,
|
||||
});
|
||||
|
||||
const {
|
||||
|
||||
Reference in New Issue
Block a user