diff --git a/src/renderer/components/common/ContextSwitchOverlay.tsx b/src/renderer/components/common/ContextSwitchOverlay.tsx new file mode 100644 index 00000000..00fbb7ea --- /dev/null +++ b/src/renderer/components/common/ContextSwitchOverlay.tsx @@ -0,0 +1,38 @@ +/** + * ContextSwitchOverlay - Full-screen loading overlay during context switches. + * + * Displayed when isContextSwitching is true, preventing stale data flash + * during workspace transitions. + */ + +import React from 'react'; + +import { useStore } from '@renderer/store'; + +export const ContextSwitchOverlay: React.FC = () => { + const isContextSwitching = useStore((state) => state.isContextSwitching); + const targetContextId = useStore((state) => state.targetContextId); + + if (!isContextSwitching) { + return null; + } + + // Format context label for display + const contextLabel = + targetContextId === 'local' ? 'Local' : (targetContextId?.replace(/^ssh-/, '') ?? 'Unknown'); + + return ( +
+
+ {/* Spinner */} +
+ + {/* Text */} +
+

Switching to {contextLabel}...

+

Loading workspace

+
+
+
+ ); +}; diff --git a/src/renderer/hooks/useContextSwitch.ts b/src/renderer/hooks/useContextSwitch.ts new file mode 100644 index 00000000..521870c0 --- /dev/null +++ b/src/renderer/hooks/useContextSwitch.ts @@ -0,0 +1,28 @@ +/** + * useContextSwitch - Hook for context switching actions. + * + * Thin wrapper exposing context switch functionality to components. + */ + +import { useCallback } from 'react'; + +import { useStore } from '../store'; + +export function useContextSwitch() { + const switchContext = useStore((state) => state.switchContext); + const isContextSwitching = useStore((state) => state.isContextSwitching); + const activeContextId = useStore((state) => state.activeContextId); + + const handleSwitch = useCallback( + async (targetContextId: string) => { + await switchContext(targetContextId); + }, + [switchContext] + ); + + return { + switchContext: handleSwitch, + isContextSwitching, + activeContextId, + }; +} diff --git a/src/renderer/store/index.ts b/src/renderer/store/index.ts index bb38330c..1b4dba73 100644 --- a/src/renderer/store/index.ts +++ b/src/renderer/store/index.ts @@ -6,6 +6,7 @@ import { create } from 'zustand'; import { createConfigSlice } from './slices/configSlice'; import { createConnectionSlice } from './slices/connectionSlice'; +import { createContextSlice } from './slices/contextSlice'; import { createConversationSlice } from './slices/conversationSlice'; import { createNotificationSlice } from './slices/notificationSlice'; import { createPaneSlice } from './slices/paneSlice'; @@ -41,6 +42,7 @@ export const useStore = create()((...args) => ({ ...createNotificationSlice(...args), ...createConfigSlice(...args), ...createConnectionSlice(...args), + ...createContextSlice(...args), ...createUpdateSlice(...args), })); diff --git a/src/renderer/store/types.ts b/src/renderer/store/types.ts index 22a33e9b..8ef28754 100644 --- a/src/renderer/store/types.ts +++ b/src/renderer/store/types.ts @@ -5,6 +5,7 @@ import type { ConfigSlice } from './slices/configSlice'; import type { ConnectionSlice } from './slices/connectionSlice'; +import type { ContextSlice } from './slices/contextSlice'; import type { ConversationSlice } from './slices/conversationSlice'; import type { NotificationSlice } from './slices/notificationSlice'; import type { PaneSlice } from './slices/paneSlice'; @@ -88,4 +89,5 @@ export type AppState = ProjectSlice & NotificationSlice & ConfigSlice & ConnectionSlice & + ContextSlice & UpdateSlice;