From c82f188fc8c43c547c1787dddbf92fcbc9f02b45 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Mon, 24 Aug 2026 16:36:41 +0300 Subject: [PATCH] refactor(surface): remove the main-area surface concept entirely MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit activeSurface was permanently 'chat' after the legacy mobile layout removal, so the whole concept is gone: the store field, surfaceGuard, setActiveSurface/setSurfaceGuard, the per-runtime surface memory in prepare/restoreForRuntimeSwitch, and WorkspaceSurface itself. All ~30 setActiveSurface('chat') call sites were no-ops and are deleted; always-true 'is the chat active' checks in keyboard shortcuts, Header and ChatContainer are unconditional now. FilesView's dirty-file guard kept its file-switch and close protection but drops the surface-switch branch nothing could trigger. TerminalView visibility comes only from its callers. The router keeps parsing legacy ?tab= links (they open the matching context-panel surface) via its own RouteTab type and no longer serializes a tab or diff file into URLs — desktop URLs never carried them anyway. --- packages/ui/src/App.tsx | 2 - packages/ui/src/apps/runtimeEndpointReset.ts | 3 -- .../ui/src/components/chat/ChatContainer.tsx | 3 +- packages/ui/src/components/layout/Header.tsx | 12 +---- .../ui/src/components/layout/MainLayout.tsx | 11 +--- .../session/DirectoryExplorerDialog.tsx | 4 +- .../session/ScheduledTasksDialog.tsx | 11 ++-- .../src/components/session/SessionSidebar.tsx | 17 ++---- .../session/SessionSwitcherDropdown.tsx | 4 +- .../project-context/useProjectTodoSend.ts | 4 +- .../session/sidebar/SessionGroupSection.tsx | 8 +-- .../session/sidebar/SidebarProjectsList.tsx | 4 -- .../hooks/useProjectSessionSelection.ts | 5 -- .../sidebar/hooks/useSessionActions.ts | 3 -- .../ui/src/components/ui/CommandPalette.tsx | 5 +- .../ui/src/components/views/ArchiveView.tsx | 4 +- .../ui/src/components/views/FilesView.tsx | 47 ++-------------- packages/ui/src/components/views/PlanView.tsx | 4 +- .../ui/src/components/views/TerminalView.tsx | 4 +- .../components/views/git/ConflictDialog.tsx | 4 -- .../views/git/IntegrateCommitsSection.tsx | 7 +-- .../views/git/PullRequestSection.tsx | 10 ++-- packages/ui/src/hooks/useKeyboardShortcuts.ts | 25 +++------ packages/ui/src/hooks/useMenuActions.ts | 11 ++-- packages/ui/src/hooks/useRouter.ts | 24 ++------- packages/ui/src/lib/addSelectionToChat.ts | 1 - packages/ui/src/lib/router/parseRoute.ts | 6 +-- .../ui/src/lib/router/serializeRoute.test.ts | 2 - packages/ui/src/lib/router/serializeRoute.ts | 17 ------ packages/ui/src/lib/router/types.ts | 9 ++-- packages/ui/src/stores/useUIStore.ts | 54 +------------------ 31 files changed, 49 insertions(+), 276 deletions(-) diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index c406536a..f213c11f 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -626,7 +626,6 @@ function App({ apis }: AppProps) { const directory = typeof detail?.directory === 'string' && detail.directory.trim().length > 0 ? detail.directory.trim() : null; - useUIStore.getState().setActiveSurface('chat'); void useSessionUIStore.getState().setCurrentSession(sessionId, directory); }; @@ -675,7 +674,6 @@ function App({ apis }: AppProps) { ? detail.projectId.trim() : null; const hasProjectTarget = Boolean(directory || projectId); - useUIStore.getState().setActiveSurface('chat'); useUIStore.getState().setSessionSwitcherOpen(false); useSessionUIStore.getState().openNewSessionDraft({ target: hasProjectTarget ? 'project' : 'chat', diff --git a/packages/ui/src/apps/runtimeEndpointReset.ts b/packages/ui/src/apps/runtimeEndpointReset.ts index 39c325fd..add84eb3 100644 --- a/packages/ui/src/apps/runtimeEndpointReset.ts +++ b/packages/ui/src/apps/runtimeEndpointReset.ts @@ -6,7 +6,6 @@ import { useProjectsStore } from '@/stores/useProjectsStore'; import { useProjectContextStore } from '@/stores/useProjectContextStore'; import { useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore'; import { useAutoReviewStore } from '@/stores/useAutoReviewStore'; -import { useUIStore } from '@/stores/useUIStore'; import { usePermissionStore } from '@/stores/permissionStore'; import { useFileSearchStore } from '@/stores/useFileSearchStore'; import { useGitStore } from '@/stores/useGitStore'; @@ -37,7 +36,6 @@ export const reconnectAppForTransportSwitch = (): void => { export const resetAppForRuntimeEndpointChange = (detail: RuntimeEndpointChangedDetail): void => { useSessionUIStore.getState().prepareForRuntimeSwitch(detail.previousRuntimeKey); - useUIStore.getState().prepareForRuntimeSwitch(detail.previousRuntimeKey); if (detail.previousRuntimeKey) { useAutoReviewStore.getState().stopRunningRunsForRuntime(detail.previousRuntimeKey); } @@ -71,7 +69,6 @@ export const resetAppForRuntimeEndpointChange = (detail: RuntimeEndpointChangedD useSessionFoldersStore.getState().resetForRuntimeSwitch(detail.runtimeKey); useFilesViewTabsStore.getState().resetForRuntimeSwitch(detail.runtimeKey); useSessionUIStore.getState().restoreForRuntimeSwitch(detail.runtimeKey); - useUIStore.getState().restoreForRuntimeSwitch(detail.runtimeKey); resetStreamingState(); queueMicrotask(() => void syncDesktopSettings()); }; diff --git a/packages/ui/src/components/chat/ChatContainer.tsx b/packages/ui/src/components/chat/ChatContainer.tsx index e8c46c4b..a6384531 100644 --- a/packages/ui/src/components/chat/ChatContainer.tsx +++ b/packages/ui/src/components/chat/ChatContainer.tsx @@ -1006,8 +1006,7 @@ export const ChatContainer: React.FC = ({ return; } - const { activeSurface } = useUIStore.getState(); - if (activeSurface !== 'chat' || hasBlockingChatOverlay()) { + if (hasBlockingChatOverlay()) { return; } diff --git a/packages/ui/src/components/layout/Header.tsx b/packages/ui/src/components/layout/Header.tsx index 067bcd38..3d99604b 100644 --- a/packages/ui/src/components/layout/Header.tsx +++ b/packages/ui/src/components/layout/Header.tsx @@ -434,7 +434,6 @@ export const Header: React.FC = () => { const openContextOverview = useUIStore((state) => state.openContextOverview); const openContextPlan = useUIStore((state) => state.openContextPlan); const closeContextPanel = useUIStore((state) => state.closeContextPanel); - const activeSurface = useUIStore((state) => state.activeSurface); const shortcutOverrides = useUIStore((state) => state.shortcutOverrides); const getCurrentModel = useConfigStore((state) => state.getCurrentModel); @@ -612,7 +611,6 @@ export const Header: React.FC = () => { }, [setWorkStatusOverlayOpen, setWorkStatusPanelEnabled, workStatusOverlayOpen, workStatusPanelEnabled, workStatusPanelFits]); const showDesktopHeaderContextUsage = !isVSCode && !workStatusPanelVisible - && activeSurface === 'chat' && !!stableDesktopContextUsage && stableDesktopContextUsage.totalTokens > 0; const desktopHeaderDisplayPercentage = stableDesktopContextUsage && stableDesktopContextUsage.contextLimit > 0 @@ -1153,9 +1151,6 @@ export const Header: React.FC = () => { // Reset plan tab availability when session changes React.useEffect(() => { if (!planModeEnabled) { - if (useUIStore.getState().activeSurface === 'plan') { - useUIStore.getState().setActiveSurface('chat'); - } return; } @@ -1165,11 +1160,6 @@ export const Header: React.FC = () => { if (lastPlanSessionKeyRef.current !== sessionKey) { lastPlanSessionKeyRef.current = sessionKey; } - - // If plan is not available but user is on plan tab, switch them back to chat - if (!planTabAvailable && useUIStore.getState().activeSurface === 'plan') { - useUIStore.getState().setActiveSurface('chat'); - } }, [ planModeEnabled, planTabAvailable, @@ -1759,7 +1749,7 @@ export const Header: React.FC = () => { className={cn(desktopHeaderIconButtonClass, 'mr-1')} Icon={'picture-in-picture-2'} /> - {activeSurface === 'chat' && !isVSCode ? ( + {!isVSCode ? (