From ea316dc495ff3de52e8945e06c1fcc15dd267e5d Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Fri, 5 Jun 2026 20:07:19 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(desktop):=20=E9=87=8D=E7=BB=84=20shell?= =?UTF-8?q?=20=E4=B8=BA=20IM=20=E5=B7=A5=E4=BD=9C=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/desktop/src/App.module.css | 70 ++++- app/desktop/src/App.tsx | 364 ++++++++++++---------- app/desktop/src/__tests__/uiStore.test.ts | 40 +++ app/desktop/src/stores/uiStore.ts | 9 +- 4 files changed, 314 insertions(+), 169 deletions(-) diff --git a/app/desktop/src/App.module.css b/app/desktop/src/App.module.css index fad98839..ceff3b36 100644 --- a/app/desktop/src/App.module.css +++ b/app/desktop/src/App.module.css @@ -274,7 +274,7 @@ display: flex; flex-direction: column; align-items: center; - gap: 6px; + gap: 4px; padding: 8px 5px; border: 1px solid var(--glass-border-subtle); border-radius: 14px; @@ -283,8 +283,9 @@ -webkit-backdrop-filter: blur(16px); } -.rightRail { - justify-content: flex-start; +.railSpacer { + flex: 1; + min-height: 8px; } .railStatusDot { @@ -712,6 +713,56 @@ flex-shrink: 0; } +/* ── Workspace content: transcript + inspector ─ */ +.workspaceContent { + flex: 1; + display: flex; + min-height: 0; +} + +.transcriptArea { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + min-width: 0; +} + +/* ── Inline Inspector Panel ──────────────── */ +.inspectorPanel { + width: var(--right-panel-width, 320px); + flex-shrink: 0; + display: flex; + flex-direction: column; + border-left: 1px solid var(--glass-border-subtle); + background: var(--sidebar-bg); + overflow: hidden; + transition: opacity 0.18s ease; +} + +[data-theme='dark'] .inspectorPanel { + background: var(--glass-panel); + border-left-color: var(--glass-border); +} + +.inspectorPanelBody { + flex: 1; + overflow-y: auto; + padding: var(--space-sm); + font-size: var(--font-size-xs); + color: var(--foreground); +} + +.inspectorPanelOpen { + opacity: 1; + pointer-events: auto; +} + +.inspectorPanelClosing { + opacity: 0; + pointer-events: none; +} + /* ═══════════════════════════════════════════════ Right panel — slim, refined ═══════════════════════════════════════════════ */ @@ -1289,20 +1340,21 @@ Responsive ═══════════════════════════════════════════════ */ +@media (max-width: 1279px) { + .sidebar { width: min(var(--left-sidebar-width, 320px), 28vw); } + .inspectorPanel { width: min(var(--right-panel-width, 320px), 35vw); } +} + @media (max-width: 1023px) { .sidebar { width: min(var(--left-sidebar-width, 320px), 34vw); } - .rightPanel { - right: 12px; - width: min(var(--right-panel-width, 320px), 40vw); - max-height: calc(100% - 60px); - } + .inspectorPanel { width: min(var(--right-panel-width, 280px), 40vw); } } @media (max-width: 767px) { .body { flex-direction: column; padding: 0; gap: 0; } .sidebar { display: none; } .leftRail, .rightRail, .resizeHandle { display: none; } - .rightPanel { display: none; } + .inspectorPanel { display: none; } .workspace { border-radius: 0; } .nav { display: flex; } .mobileToolbar { display: flex; } diff --git a/app/desktop/src/App.tsx b/app/desktop/src/App.tsx index 12c3f06d..30b1fc92 100644 --- a/app/desktop/src/App.tsx +++ b/app/desktop/src/App.tsx @@ -68,6 +68,7 @@ const HomeDashboard = lazy(() => import('@/components/HomeDashboard')); const SettingsPage = lazy(() => import('@/components/SettingsPage')); import { AlertTriangle, + Bot, ChevronLeft, ChevronRight, ClipboardList, @@ -206,7 +207,6 @@ export default function App() { : t('settings.agentScheduling'); const [userMessages, setUserMessages] = useState([]); const { hiddenMessageIds, hideMessage } = useHiddenMessages(activeThreadId); - const [viewMode, setViewMode] = useState<'agent' | 'im'>('agent'); const [shortcutHelpOpen, setShortcutHelpOpen] = useState(false); const [workspaceExpanded, setWorkspaceExpanded] = useState(false); const [settingsOpen, setSettingsOpen] = useState(false); @@ -216,19 +216,21 @@ export default function App() { leftSidebarCollapsed, rightPanelOpen, leftSidebarWidth, - leftSidebarView, + activeRailView, setLeftSidebarCollapsed, setRightPanelOpen, setLeftSidebarView, + setActiveRailView, } = useUIStore( useShallow((s) => ({ leftSidebarCollapsed: s.leftSidebarCollapsed, rightPanelOpen: s.rightPanelOpen, leftSidebarWidth: s.sidebarWidth, - leftSidebarView: s.leftSidebarView, + activeRailView: s.activeRailView, setLeftSidebarCollapsed: s.setLeftSidebarCollapsed, setRightPanelOpen: s.setRightPanelOpen, setLeftSidebarView: s.setLeftSidebarView, + setActiveRailView: s.setActiveRailView, })), ); const { handleStartResize, handleResizeKeyDown } = useSidebarResize(); @@ -293,6 +295,9 @@ export default function App() { const runCardConstrained = workspaceWidth > 0 && workspaceWidth < RUN_CARD_MIN_WORKSPACE_WIDTH; const effectiveRightPanelOpen = rightPanelOpen && !runCardConstrained; const showRunCardSpace = !!displayedRun && effectiveRightPanelOpen && !isMobile && !workspaceExpanded; + const effectiveSidebarCollapsed = leftSidebarCollapsed + || activeRailView === 'messages' + || activeRailView === 'team'; const composerLocked = runStartPending || runIsActive; const persistedMessages = useMemo( () => buildChatMessagesFromThreadItems(threadItemData?.items ?? []), @@ -382,6 +387,17 @@ export default function App() { t, }); + const setViewModeCompat = useCallback((mode: 'agent' | 'im') => { + setActiveRailView(mode === 'im' ? 'messages' : 'agents'); + }, [setActiveRailView]); + + const setLeftSidebarViewCompat = useCallback((v: 'home' | 'thread') => { + setLeftSidebarView(v); + if (v === 'thread' && activeRailView === 'home') { + setActiveRailView('agents'); + } + }, [activeRailView, setActiveRailView, setLeftSidebarView]); + const { handleSelectThread, handleThreadTitleEdited, @@ -401,8 +417,8 @@ export default function App() { selectedThreadTitle: selectedThread?.title, selectThread, selectAgentThread, - setLeftSidebarView, - setViewMode, + setLeftSidebarView: setLeftSidebarViewCompat, + setViewMode: setViewModeCompat, setPendingComposerDraft, addThreadToCache, emptyCreatedThreadIdsRef, @@ -466,8 +482,9 @@ export default function App() { return; } setLeftSidebarView('thread'); + setActiveRailView('agents'); if (displayedRun) setRightPanelOpen(true); - }, [displayedRun, openSettings, permissionRequests.length, setLeftSidebarView, setRightPanelOpen]); + }, [displayedRun, openSettings, permissionRequests.length, setActiveRailView, setLeftSidebarView, setRightPanelOpen]); const handleDecidePermission = useCallback(async (requestId: string, decision: 'allow' | 'deny', reason?: string) => { const request = permissionRequests.find((item) => item.requestId === requestId); @@ -489,7 +506,7 @@ export default function App() { }, [hideMessage]); useEffect(() => { - if (!pendingComposerDraft || leftSidebarView === 'home' || viewMode !== 'agent') return undefined; + if (!pendingComposerDraft || activeRailView !== 'agents') return undefined; let attempts = 0; let timer: number | undefined; const tryApplyDraft = () => { @@ -507,7 +524,7 @@ export default function App() { return () => { if (timer !== undefined) window.clearTimeout(timer); }; - }, [leftSidebarView, pendingComposerDraft, viewMode]); + }, [activeRailView, pendingComposerDraft]); const handleShareWorkspace = useCallback(async () => { const title = selectedThread?.title ?? selectedAgent?.name ?? 'AgentHub'; @@ -701,22 +718,86 @@ export default function App() { )}
- {/* Single sidebar — agents + threads grouped */} - {!isMobile && !workspaceExpanded && !leftSidebarCollapsed && ( + {/* Global Rail — icon-only navigation */} + {!isMobile && !workspaceExpanded && ( +
+ setActiveRailView('home')} + label={t('nav.home')} + tooltipSide="right" + aria-pressed={activeRailView === 'home'} + > + + + setActiveRailView('messages')} + label={t('im.groupChat')} + tooltipSide="right" + aria-pressed={activeRailView === 'messages'} + > + + + setActiveRailView('agents')} + label={t('nav.agent')} + tooltipSide="right" + aria-pressed={activeRailView === 'agents'} + > + + + setActiveRailView('team')} + label={teamRunButtonLabel} + tooltipSide="right" + aria-pressed={activeRailView === 'team'} + > + + + {teamRunBadgeCount > 0 ? ( + + {teamRunBadgeCount > 9 ? '9+' : teamRunBadgeCount} + + ) : null} + + +
+ openSettings('general')} + label={t('nav.settings')} + tooltipSide="right" + > + + + + {hubAuthenticated ? : } + + + {theme === 'dark' ? : } + +
+ )} + + {/* Conversation Sidebar — agents + threads */} + {!isMobile && !workspaceExpanded && !effectiveSidebarCollapsed && ( <>
- {/* Home nav */} -
- setLeftSidebarView('home')} - label={t('nav.home')} - tooltipSide="right" - > - - -
- {/* Global search */}
- - {/* Sidebar footer */} -
- openSettings('general')} label={t('nav.settings')} tooltipSide="top"> - - - - {hubAuthenticated ? : } - - - {theme === 'dark' ? : } - -
- setViewMode((mode) => (mode === 'agent' ? 'im' : 'agent'))} - label={viewMode === 'agent' ? t('im.groupChat') : t('nav.agent')} - tooltipSide="bottom" - aria-pressed={viewMode === 'im'} - > - - openSettings('tasks')} @@ -813,21 +866,6 @@ export default function App() { > - openSettings('agentScheduling')} - label={teamRunButtonLabel} - tooltipSide="bottom" - > - - - {teamRunBadgeCount > 0 ? ( - - {teamRunBadgeCount > 9 ? '9+' : teamRunBadgeCount} - - ) : null} - - {displayedRun && !effectiveRightPanelOpen && (
- {/* Chat area */} -
- {leftSidebarView === 'home' ? ( - - { - try { - const thread = await createThread(); - addThreadToCache(thread, { empty: true }); - handleSelectThread(thread.threadId); - } catch { - // continue - } - }} - onSelectThread={handleSelectThread} - onQuickStart={async (prompt) => { - const title = buildAutomaticThreadTitle(prompt); - try { - const thread = await createThread(title ?? undefined); - addThreadToCache(thread); - handleSelectThread(thread.threadId); - await handleSend(prompt, selectedAgentId ?? undefined, { - threadId: thread.threadId, - threadInfo: thread, - createdEmptyThread: true, - }); - } catch { - addToast({ type: 'error', message: t('toast.error') }); - } - }} - onViewRuns={() => openSettings('tasks')} - onReviewApprovals={handleReviewApprovalsFromHome} - onViewAllThreads={() => setLeftSidebarView('thread')} - onOpenTeamRuns={() => openSettings('agentScheduling')} - onOpenHubAccount={handleOpenHubAccount} - permissionCount={permissionRequests.length} - agentTeamOverview={agentTeamsQuery.data} - agentTeamsLoading={agentTeamsQuery.isLoading || agentTeamsQuery.isFetching} - agentTeamsSignedIn={hubInventoryEnabled} - agents={agents} - selectedAgentId={selectedAgentId ?? undefined} - onSelectAgent={handleSelectAgent} - onStartLocalOrchestration={handleStartLocalOrchestration} - /> - - ) : viewMode === 'im' ? ( - - ) : ( - - )} -
- - {/* Input area */} - {leftSidebarView !== 'home' && viewMode === 'agent' && ( -
- -
- )} - - {!isMobile && !workspaceExpanded && displayedRun && rightPanelMounted && ( -
-
- -
}> - + {/* Content: transcript + inline inspector */} +
+
+ {/* Transcript */} +
+ {activeRailView === 'home' ? ( + + { + try { + const thread = await createThread(); + addThreadToCache(thread, { empty: true }); + handleSelectThread(thread.threadId); + } catch { + // continue + } + }} + onSelectThread={handleSelectThread} + onQuickStart={async (prompt) => { + const title = buildAutomaticThreadTitle(prompt); + try { + const thread = await createThread(title ?? undefined); + addThreadToCache(thread); + handleSelectThread(thread.threadId); + await handleSend(prompt, selectedAgentId ?? undefined, { + threadId: thread.threadId, + threadInfo: thread, + createdEmptyThread: true, + }); + } catch { + addToast({ type: 'error', message: t('toast.error') }); + } + }} + onViewRuns={() => openSettings('tasks')} + onReviewApprovals={handleReviewApprovalsFromHome} + onViewAllThreads={() => setActiveRailView('agents')} + onOpenTeamRuns={() => setActiveRailView('team')} + onOpenHubAccount={handleOpenHubAccount} + permissionCount={permissionRequests.length} + agentTeamOverview={agentTeamsQuery.data} + agentTeamsLoading={agentTeamsQuery.isLoading || agentTeamsQuery.isFetching} + agentTeamsSignedIn={hubInventoryEnabled} + agents={agents} + selectedAgentId={selectedAgentId ?? undefined} + onSelectAgent={handleSelectAgent} + onStartLocalOrchestration={handleStartLocalOrchestration} + /> - + ) : activeRailView === 'messages' ? ( + + ) : activeRailView === 'team' ? ( + + ) : ( + + )}
+ + {/* Input area — only for agent chat */} + {activeRailView === 'agents' && ( +
+ +
+ )}
- )} + + {/* Inline Inspector Panel */} + {!isMobile && !workspaceExpanded && displayedRun && rightPanelMounted && ( +
+
+ +
}> + + + +
+
+ )} +
diff --git a/app/desktop/src/__tests__/uiStore.test.ts b/app/desktop/src/__tests__/uiStore.test.ts index bd70607c..f887a4d3 100644 --- a/app/desktop/src/__tests__/uiStore.test.ts +++ b/app/desktop/src/__tests__/uiStore.test.ts @@ -10,6 +10,7 @@ describe('uiStore shell layout state', () => { leftSidebarCollapsed: false, rightPanelOpen: false, leftSidebarView: 'home', + activeRailView: 'home', mobileSidebarOpen: false, mobileRightPanelOpen: false, }); @@ -31,11 +32,25 @@ describe('uiStore shell layout state', () => { }); }); + it('tracks active rail view', () => { + const store = useUIStore.getState(); + + store.setActiveRailView('messages'); + expect(useUIStore.getState().activeRailView).toBe('messages'); + + store.setActiveRailView('agents'); + expect(useUIStore.getState().activeRailView).toBe('agents'); + + store.setActiveRailView('home'); + expect(useUIStore.getState().activeRailView).toBe('home'); + }); + it('persists only desktop shell layout fields', () => { const store = useUIStore.getState(); store.setLeftSidebarCollapsed(true); store.setRightPanelOpen(true); + store.setActiveRailView('messages'); store.setMobileSidebarOpen(true); const persisted = JSON.parse(localStorage.getItem('agenthub-ui-shell') ?? '{}'); @@ -46,6 +61,31 @@ describe('uiStore shell layout state', () => { leftSidebarCollapsed: true, rightPanelOpen: true, leftSidebarView: 'home', + activeRailView: 'messages', + }); + }); + + it('migrates persisted state without activeRailView to default home', async () => { + localStorage.setItem('agenthub-ui-shell', JSON.stringify({ + version: 2, + state: { + sidebarWidth: 320, + rightPanelWidth: 360, + leftSidebarCollapsed: false, + rightPanelOpen: false, + leftSidebarView: 'thread', + }, + })); + + await useUIStore.persist.rehydrate(); + + expect(useUIStore.getState()).toMatchObject({ + sidebarWidth: 320, + rightPanelWidth: 360, + leftSidebarCollapsed: false, + rightPanelOpen: false, + leftSidebarView: 'thread', + activeRailView: 'home', }); }); }); diff --git a/app/desktop/src/stores/uiStore.ts b/app/desktop/src/stores/uiStore.ts index 6f99f9e9..cc24ddaa 100644 --- a/app/desktop/src/stores/uiStore.ts +++ b/app/desktop/src/stores/uiStore.ts @@ -19,6 +19,7 @@ interface UIState { leftSidebarCollapsed: boolean; rightPanelOpen: boolean; leftSidebarView: 'home' | 'thread'; + activeRailView: 'home' | 'messages' | 'agents' | 'team'; // Mobile toggles mobileSidebarOpen: boolean; mobileRightPanelOpen: boolean; @@ -27,6 +28,7 @@ interface UIState { setLeftSidebarCollapsed: (v: boolean) => void; setRightPanelOpen: (v: boolean) => void; setLeftSidebarView: (v: 'home' | 'thread') => void; + setActiveRailView: (v: 'home' | 'messages' | 'agents' | 'team') => void; setMobileSidebarOpen: (v: boolean) => void; setMobileRightPanelOpen: (v: boolean) => void; toggleLeftSidebar: () => void; @@ -44,6 +46,7 @@ export const useUIStore = create()( leftSidebarCollapsed: false, rightPanelOpen: false, leftSidebarView: 'home', + activeRailView: 'home', mobileSidebarOpen: false, mobileRightPanelOpen: false, @@ -52,6 +55,7 @@ export const useUIStore = create()( setLeftSidebarCollapsed: (v) => set({ leftSidebarCollapsed: v }), setRightPanelOpen: (v) => set({ rightPanelOpen: v }), setLeftSidebarView: (v) => set({ leftSidebarView: v }), + setActiveRailView: (v) => set({ activeRailView: v }), setMobileSidebarOpen: (v) => set({ mobileSidebarOpen: v }), setMobileRightPanelOpen: (v) => set({ mobileRightPanelOpen: v }), toggleLeftSidebar: () => set((s) => ({ leftSidebarCollapsed: !s.leftSidebarCollapsed })), @@ -61,7 +65,7 @@ export const useUIStore = create()( }), { name: 'agenthub-ui-shell', - version: 2, + version: 3, migrate: (persisted) => { const state = (persisted && typeof persisted === 'object' && 'state' in persisted) ? (persisted as { state?: Partial }).state @@ -73,6 +77,8 @@ export const useUIStore = create()( leftSidebarCollapsed: Boolean(state?.leftSidebarCollapsed), rightPanelOpen: Boolean(state?.rightPanelOpen), leftSidebarView: (state?.leftSidebarView === 'thread' ? 'thread' : 'home') as 'home' | 'thread', + activeRailView: (['home', 'messages', 'agents', 'team'].includes(state?.activeRailView as string) + ? state?.activeRailView : 'home') as UIState['activeRailView'], mobileSidebarOpen: false, mobileRightPanelOpen: false, }; @@ -83,6 +89,7 @@ export const useUIStore = create()( leftSidebarCollapsed: s.leftSidebarCollapsed, rightPanelOpen: s.rightPanelOpen, leftSidebarView: s.leftSidebarView, + activeRailView: s.activeRailView, }), }, ), From ec41bb0f58be85caeffbc31ab6ab9cf725e7ccc6 Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Fri, 5 Jun 2026 22:05:32 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(desktop):=20=E4=BF=AE=E5=A4=8D=20R2=20s?= =?UTF-8?q?hell=20=E8=A7=86=E8=A7=89=20QA=20=E9=98=BB=E5=A1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../skills/ui-screenshot/scripts/capture.ts | 8 +++++++- app/desktop/src/App.module.css | 18 ++++++++++++++++++ app/desktop/src/hooks/useSidebarResize.ts | 11 +++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.agents/skills/ui-screenshot/scripts/capture.ts b/.agents/skills/ui-screenshot/scripts/capture.ts index ca1aa100..63316d1e 100644 --- a/.agents/skills/ui-screenshot/scripts/capture.ts +++ b/.agents/skills/ui-screenshot/scripts/capture.ts @@ -66,6 +66,7 @@ async function main() { const [vw, vh] = (viewport as string).split(',').map(Number); const waitMs = Number(wait); const useMock = mock !== 'false'; + const captureTheme = theme === 'light' ? 'light' : 'dark'; const timestamp = new Date().toISOString().replace(/[:T]/g, '-').slice(0, 19); const outputPath = resolve(out ?? `screenshots/capture-${timestamp}.png`); @@ -78,12 +79,17 @@ async function main() { const context = await browser.newContext({ viewport: { width: vw, height: vh }, - colorScheme: theme as 'dark' | 'light', + colorScheme: captureTheme, deviceScaleFactor: 1, }); const page = await context.newPage(); + await page.addInitScript((selectedTheme) => { + window.localStorage.setItem('agenthub-theme', selectedTheme); + document.documentElement.setAttribute('data-theme', selectedTheme); + }, captureTheme); + // Inject mock data before navigation if (useMock) { await page.addInitScript((payload) => { diff --git a/app/desktop/src/App.module.css b/app/desktop/src/App.module.css index ceff3b36..6daee16a 100644 --- a/app/desktop/src/App.module.css +++ b/app/desktop/src/App.module.css @@ -1351,6 +1351,24 @@ } @media (max-width: 767px) { + .topBar { + height: 28px; + justify-content: flex-end; + padding-inline: 8px; + } + + .topBarLeft { + flex: 1 1 auto; + min-width: 0; + } + + .topBarNavCluster, + .appMenu, + .statusBadge, + .topBarDim { + display: none; + } + .body { flex-direction: column; padding: 0; gap: 0; } .sidebar { display: none; } .leftRail, .rightRail, .resizeHandle { display: none; } diff --git a/app/desktop/src/hooks/useSidebarResize.ts b/app/desktop/src/hooks/useSidebarResize.ts index c1f5c4ff..3d10cb18 100644 --- a/app/desktop/src/hooks/useSidebarResize.ts +++ b/app/desktop/src/hooks/useSidebarResize.ts @@ -2,16 +2,19 @@ import { useCallback } from 'react'; import type { KeyboardEvent as ReactKeyboardEvent } from 'react'; import { clamp } from '@/utils/appUtils'; import { useUIStore } from '@/stores/uiStore'; +import { useShallow } from 'zustand/shallow'; import styles from '@/App.module.css'; const LEFT_SIDEBAR_MIN = 248; const LEFT_SIDEBAR_MAX = 420; export function useSidebarResize() { - const { leftSidebarWidth, setLeftSidebarWidth } = useUIStore((s) => ({ - leftSidebarWidth: s.sidebarWidth, - setLeftSidebarWidth: s.setSidebarWidth, - })); + const { leftSidebarWidth, setLeftSidebarWidth } = useUIStore( + useShallow((s) => ({ + leftSidebarWidth: s.sidebarWidth, + setLeftSidebarWidth: s.setSidebarWidth, + })), + ); const handleStartResize = useCallback( (side: 'left' | 'right') => (event: React.PointerEvent) => { From 0b8e67018f4040cb555f4eeccea0c18c31710722 Mon Sep 17 00:00:00 2001 From: Delicious233 Date: Fri, 5 Jun 2026 22:24:16 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(desktop):=20=E6=B8=85=E7=90=86=20R2=20A?= =?UTF-8?q?pp=20lint=20=E5=99=AA=E5=A3=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/desktop/src/App.tsx | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/app/desktop/src/App.tsx b/app/desktop/src/App.tsx index 30b1fc92..5a6f196c 100644 --- a/app/desktop/src/App.tsx +++ b/app/desktop/src/App.tsx @@ -33,12 +33,10 @@ import { useAuth } from '@/hooks/useAuth'; import useFocusSourceTracking from '@/hooks/useFocusSourceTracking'; import useShellShortcuts from '@/hooks/useShellShortcuts'; import { useDesktopCommands } from '@/hooks/useDesktopCommands'; -import type { ThreadInfo } from '@shared/types'; import type { ChatMessage } from '@/components/ChatView.types'; import { useConnectionStore } from '@/stores/connectionStore'; import { useThreadStore } from '@/stores/threadStore'; import { useUIStore } from '@/stores/uiStore'; -import { useTaskBridgeStore } from '@/stores/taskBridgeStore'; import { clamp, isRunActiveStatus, @@ -105,7 +103,6 @@ import { mergeChatMessages, } from '@/utils/chatMessages'; import { resolveThreadSelectionId, type ThreadSelectionInput } from '@/utils/threadSelection'; -import { buildTeamLocalExecutions, normalizeTeamTasks } from '@/utils/teamLocalExecution'; import { buildAutomaticThreadTitle } from '@/utils/threadTitle'; import ShellIconButton from '@/components/ShellIconButton'; import { getCurrentWindow } from '@tauri-apps/api/window'; @@ -182,23 +179,12 @@ export default function App() { const { messages, isConnected, currentRun, permissionRequests, decidePermission } = useChatMessages(online, activeThreadId); const { data: agentData } = useAgentList(online); const agents = agentData?.items ?? []; - const bridgedTasks = useTaskBridgeStore((s) => s.tasks); const modelCatalogQuery = useModelCatalog(online); const modelsDevDisplayNamesQuery = useModelsDevDisplayNames(true); const agentTeamSummary = useMemo( () => summarizeAgentTeamOverview(agentTeamsQuery.data), [agentTeamsQuery.data], ); - const teamLocalExecutions = useMemo(() => { - const overview = agentTeamsQuery.data; - return buildTeamLocalExecutions({ - selectedRunId: overview?.selectedRun?.id, - bridgeTasks: bridgedTasks, - tasks: normalizeTeamTasks(overview?.state, overview?.tasks ?? []), - assignments: overview?.state?.assignments ?? [], - events: overview?.state?.run_events ?? [], - }); - }, [agentTeamsQuery.data, bridgedTasks]); const teamRunBadgeCount = agentTeamSummary.blockingCount || agentTeamSummary.activeRuns; const teamRunButtonLabel = agentTeamSummary.blockingCount > 0 ? t('workspace.teamRunsWithBlocks', { count: agentTeamSummary.blockingCount }) @@ -245,13 +231,11 @@ export default function App() { // Sync health → connection store const prevOnlineRef = useRef(null); - const healthRef = useRef(health); - healthRef.current = health; useEffect(() => { if (prevOnlineRef.current === online) return; prevOnlineRef.current = online; - setOnline(online, healthRef.current); - }, [online, setOnline]); + setOnline(online, health); + }, [health, online, setOnline]); // Sync isConnected → connection store useEffect(() => { @@ -590,8 +574,14 @@ export default function App() { if (target.closest('button, input, select, a')) return; try { const w = getCurrentWindow(); - (await w.isMaximized()) ? w.unmaximize() : w.maximize(); - } catch {} + if (await w.isMaximized()) { + await w.unmaximize(); + } else { + await w.maximize(); + } + } catch { + // Tauri window APIs are unavailable in browser-only test environments. + } }, []); // ── Render ───────────────────────────────── @@ -635,7 +625,11 @@ export default function App() { { const w = getCurrentWindow(); - (await w.isMaximized()) ? w.unmaximize() : w.maximize(); + if (await w.isMaximized()) { + await w.unmaximize(); + } else { + await w.maximize(); + } }} label={t('window.maximize')} tooltipSide="bottom">