diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 70068abc5..db6eca888 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -436,6 +436,17 @@ const AppContent: React.FC = () => { } }, [selectedProject]); + // Agent switching handlers - dispatch custom events for ChatInterface to handle + const handleNextAgent = useCallback(() => { + if (!activeTask) return; + window.dispatchEvent(new CustomEvent('emdash:switch-agent', { detail: { direction: 'next' } })); + }, [activeTask]); + + const handlePrevAgent = useCallback(() => { + if (!activeTask) return; + window.dispatchEvent(new CustomEvent('emdash:switch-agent', { detail: { direction: 'prev' } })); + }, [activeTask]); + const handleWelcomeGetStarted = useCallback(() => { setShowWelcomeScreen(false); setShowFirstLaunchModal(true); @@ -2451,6 +2462,8 @@ const AppContent: React.FC = () => { handleNextTask={handleNextTask} handlePrevTask={handlePrevTask} handleNewTask={handleNewTask} + handleNextAgent={handleNextAgent} + handlePrevAgent={handlePrevAgent} /> { handleGoHome={handleGoHome} handleOpenProject={handleOpenProject} handleOpenSettings={handleOpenSettings} + handleNextAgent={handleNextAgent} + handlePrevAgent={handlePrevAgent} /> {showEditorMode && activeTask && selectedProject && ( void; handlePrevTask: () => void; handleNewTask: () => void; + handleNextAgent: () => void; + handlePrevAgent: () => void; } const AppKeyboardShortcuts: React.FC = ({ @@ -31,6 +33,8 @@ const AppKeyboardShortcuts: React.FC = ({ handleNextTask, handlePrevTask, handleNewTask, + handleNextAgent, + handlePrevAgent, }) => { const { toggle: toggleLeftSidebar } = useSidebar(); const { toggle: toggleRightSidebar } = useRightSidebar(); @@ -48,6 +52,8 @@ const AppKeyboardShortcuts: React.FC = ({ onNextProject: handleNextTask, onPrevProject: handlePrevTask, onNewTask: handleNewTask, + onNextAgent: handleNextAgent, + onPrevAgent: handlePrevAgent, onCloseModal: showCommandPalette ? handleCloseCommandPalette : showSettings diff --git a/src/renderer/components/ChatInterface.tsx b/src/renderer/components/ChatInterface.tsx index 67c2b6ac9..f1beee13f 100644 --- a/src/renderer/components/ChatInterface.tsx +++ b/src/renderer/components/ChatInterface.tsx @@ -181,6 +181,41 @@ const ChatInterface: React.FC = ({ return () => clearTimeout(timer); }, [task.id]); + // Listen for agent switching events from keyboard shortcuts (Cmd+Shift+J/K) + useEffect(() => { + const handleAgentSwitch = async (event: Event) => { + const customEvent = event as CustomEvent<{ direction: 'next' | 'prev' }>; + if (conversations.length <= 1) return; + + const currentIndex = conversations.findIndex((c) => c.id === activeConversationId); + if (currentIndex === -1) return; + + let newIndex: number; + if (customEvent.detail.direction === 'next') { + newIndex = (currentIndex + 1) % conversations.length; + } else { + newIndex = currentIndex <= 0 ? conversations.length - 1 : currentIndex - 1; + } + + const newConversation = conversations[newIndex]; + if (newConversation) { + await window.electronAPI.setActiveConversation({ + taskId: task.id, + conversationId: newConversation.id, + }); + setActiveConversationId(newConversation.id); + if (newConversation.provider) { + setAgent(newConversation.provider as Agent); + } + } + }; + + window.addEventListener('emdash:switch-agent', handleAgentSwitch); + return () => { + window.removeEventListener('emdash:switch-agent', handleAgentSwitch); + }; + }, [conversations, activeConversationId, task.id]); + useEffect(() => { const meta = agentMeta[agent]; if (!meta?.terminalOnly || !meta.autoStartCommand) return; diff --git a/src/renderer/components/CommandPalette.tsx b/src/renderer/components/CommandPalette.tsx index 1ddfc3153..2cb0b10f0 100644 --- a/src/renderer/components/CommandPalette.tsx +++ b/src/renderer/components/CommandPalette.tsx @@ -16,6 +16,7 @@ import { Command as CommandIcon, Option, Palette, + ArrowRightLeft, } from 'lucide-react'; import { APP_SHORTCUTS } from '../hooks/useKeyboardShortcuts'; import type { ShortcutModifier } from '../types/shortcuts'; @@ -41,6 +42,8 @@ interface CommandPaletteProps { onToggleTheme?: () => void; onGoHome?: () => void; onOpenProject?: () => void; + onNextAgent?: () => void; + onPrevAgent?: () => void; } type CommandItem = { @@ -69,6 +72,8 @@ const CommandPalette: React.FC = ({ onToggleTheme, onGoHome, onOpenProject, + onNextAgent, + onPrevAgent, }) => { const [search, setSearch] = useState(''); const shouldReduceMotion = useReducedMotion(); @@ -195,6 +200,39 @@ const CommandPalette: React.FC = ({ }); } + // Agent switching commands + if (onNextAgent) { + items.push({ + id: 'nav-next-agent', + label: 'Next Agent', + description: APP_SHORTCUTS.NEXT_AGENT.description, + icon: , + group: 'Navigation', + keywords: ['agent', 'next', 'switch', 'conversation', 'chat'], + shortcut: { + key: APP_SHORTCUTS.NEXT_AGENT.key.toUpperCase(), + modifier: APP_SHORTCUTS.NEXT_AGENT.modifier, + }, + onSelect: () => runCommand(onNextAgent), + }); + } + + if (onPrevAgent) { + items.push({ + id: 'nav-prev-agent', + label: 'Previous Agent', + description: APP_SHORTCUTS.PREV_AGENT.description, + icon: , + group: 'Navigation', + keywords: ['agent', 'previous', 'switch', 'conversation', 'chat'], + shortcut: { + key: APP_SHORTCUTS.PREV_AGENT.key.toUpperCase(), + modifier: APP_SHORTCUTS.PREV_AGENT.modifier, + }, + onSelect: () => runCommand(onPrevAgent), + }); + } + // Project commands projects.forEach((project) => { if (onSelectProject) { @@ -241,6 +279,8 @@ const CommandPalette: React.FC = ({ onToggleLeftSidebar, onToggleRightSidebar, onToggleTheme, + onNextAgent, + onPrevAgent, runCommand, ]); @@ -335,6 +375,12 @@ const CommandPalette: React.FC = ({ {item.shortcut.modifier === 'cmd' && ( )} + {item.shortcut.modifier === 'cmd+shift' && ( + <> + + + + )} {item.shortcut.modifier === 'ctrl' && ( Ctrl )} diff --git a/src/renderer/components/CommandPaletteWrapper.tsx b/src/renderer/components/CommandPaletteWrapper.tsx index 5ddd1f6db..e5c2ec23d 100644 --- a/src/renderer/components/CommandPaletteWrapper.tsx +++ b/src/renderer/components/CommandPaletteWrapper.tsx @@ -14,6 +14,8 @@ export interface CommandPaletteWrapperProps { handleGoHome: () => void; handleOpenProject: () => void; handleOpenSettings: () => void; + handleNextAgent: () => void; + handlePrevAgent: () => void; } const CommandPaletteWrapper: React.FC = ({ @@ -25,6 +27,8 @@ const CommandPaletteWrapper: React.FC = ({ handleGoHome, handleOpenProject, handleOpenSettings, + handleNextAgent, + handlePrevAgent, }) => { const { toggle: toggleLeftSidebar } = useSidebar(); const { toggle: toggleRightSidebar } = useRightSidebar(); @@ -53,6 +57,8 @@ const CommandPaletteWrapper: React.FC = ({ onToggleTheme={toggleTheme} onGoHome={handleGoHome} onOpenProject={handleOpenProject} + onNextAgent={handleNextAgent} + onPrevAgent={handlePrevAgent} /> ); };