diff --git a/frontend/src/shared/hooks/useLastDashboardId.ts b/frontend/src/shared/hooks/useLastDashboardId.ts index 6ee67f8d..da83eb63 100644 --- a/frontend/src/shared/hooks/useLastDashboardId.ts +++ b/frontend/src/shared/hooks/useLastDashboardId.ts @@ -1,8 +1,8 @@ import { useEffect, useState, useCallback } from 'react'; import { useLocation } from 'react-router-dom'; +import { setLastDashboardId } from '@/shared/lastDashboardId'; const STORAGE_KEY = 'openswarm_last_dashboard_id'; -const WINDOW_KEY = '__openswarm_last_dashboard_id'; /** Sticky last-visited dashboard id so Dashboard stays mounted across non-dashboard nav. */ export function useLastDashboardId(): [string | null, (id: string | null) => void] { @@ -23,7 +23,7 @@ export function useLastDashboardId(): [string | null, (id: string | null) => voi try { localStorage.setItem(STORAGE_KEY, match[1]); } catch {} - (window as any)[WINDOW_KEY] = match[1]; + setLastDashboardId(match[1]); } }, [location.pathname, lastId]); @@ -36,11 +36,7 @@ export function useLastDashboardId(): [string | null, (id: string | null) => voi localStorage.removeItem(STORAGE_KEY); } } catch {} - if (id) { - (window as any)[WINDOW_KEY] = id; - } else { - delete (window as any)[WINDOW_KEY]; - } + setLastDashboardId(id); }, []); return [lastId, setLastId]; diff --git a/frontend/src/shared/lastDashboardId.ts b/frontend/src/shared/lastDashboardId.ts new file mode 100644 index 00000000..59da2f46 --- /dev/null +++ b/frontend/src/shared/lastDashboardId.ts @@ -0,0 +1,13 @@ +// The dashboard the user is currently on, mirrored to a window global so low-level non-React code (the addBrowserCard reducer) can tag a new browser card with its home dashboard at birth, instead of the card leaking onto every dashboard until the first layout save. +const WINDOW_KEY = '__openswarm_last_dashboard_id'; + +export function setLastDashboardId(id: string | null): void { + if (typeof window === 'undefined') return; + if (id) (window as any)[WINDOW_KEY] = id; + else delete (window as any)[WINDOW_KEY]; +} + +export function getLastDashboardId(): string | null { + if (typeof window === 'undefined') return null; + return ((window as any)[WINDOW_KEY] as string) || null; +} diff --git a/frontend/src/shared/state/dashboardLayoutSlice.ts b/frontend/src/shared/state/dashboardLayoutSlice.ts index 32323bc8..77661a7b 100644 --- a/frontend/src/shared/state/dashboardLayoutSlice.ts +++ b/frontend/src/shared/state/dashboardLayoutSlice.ts @@ -1,6 +1,7 @@ import { createSlice, createAsyncThunk, PayloadAction, createAction } from '@reduxjs/toolkit'; import { launchAndSendFirstMessage } from './agentsSlice'; import { API_BASE } from '@/shared/config'; +import { getLastDashboardId } from '@/shared/lastDashboardId'; // fetchSession 404/410 strips the layout card to stop AgentChat remount-loop. Matched by string to avoid circular import. const fetchSessionRejectedAction = createAction< @@ -708,6 +709,8 @@ const dashboardLayoutSlice = createSlice({ width: DEFAULT_BROWSER_CARD_W, height: DEFAULT_BROWSER_CARD_H, zOrder: state.nextZOrder++, + // Born onto the current dashboard so it shows there and only there, never bleeding onto every dashboard while it waits for the first layout save to tag it. + dashboard_id: getLastDashboardId() ?? undefined, }; state.pendingFocusBrowserId = id; },