Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -2451,6 +2462,8 @@ const AppContent: React.FC = () => {
handleNextTask={handleNextTask}
handlePrevTask={handlePrevTask}
handleNewTask={handleNewTask}
handleNextAgent={handleNextAgent}
handlePrevAgent={handlePrevAgent}
/>
<RightSidebarBridge
onCollapsedChange={handleRightSidebarCollapsedChange}
Expand Down Expand Up @@ -2572,6 +2585,8 @@ const AppContent: React.FC = () => {
handleGoHome={handleGoHome}
handleOpenProject={handleOpenProject}
handleOpenSettings={handleOpenSettings}
handleNextAgent={handleNextAgent}
handlePrevAgent={handlePrevAgent}
/>
{showEditorMode && activeTask && selectedProject && (
<CodeEditor
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/components/AppKeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ export interface AppKeyboardShortcutsProps {
handleNextTask: () => void;
handlePrevTask: () => void;
handleNewTask: () => void;
handleNextAgent: () => void;
handlePrevAgent: () => void;
}

const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
Expand All @@ -31,6 +33,8 @@ const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
handleNextTask,
handlePrevTask,
handleNewTask,
handleNextAgent,
handlePrevAgent,
}) => {
const { toggle: toggleLeftSidebar } = useSidebar();
const { toggle: toggleRightSidebar } = useRightSidebar();
Expand All @@ -48,6 +52,8 @@ const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
onNextProject: handleNextTask,
onPrevProject: handlePrevTask,
onNewTask: handleNewTask,
onNextAgent: handleNextAgent,
onPrevAgent: handlePrevAgent,
onCloseModal: showCommandPalette
? handleCloseCommandPalette
: showSettings
Expand Down
35 changes: 35 additions & 0 deletions src/renderer/components/ChatInterface.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,41 @@ const ChatInterface: React.FC<Props> = ({
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;
Expand Down
46 changes: 46 additions & 0 deletions src/renderer/components/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -41,6 +42,8 @@ interface CommandPaletteProps {
onToggleTheme?: () => void;
onGoHome?: () => void;
onOpenProject?: () => void;
onNextAgent?: () => void;
onPrevAgent?: () => void;
}

type CommandItem = {
Expand Down Expand Up @@ -69,6 +72,8 @@ const CommandPalette: React.FC<CommandPaletteProps> = ({
onToggleTheme,
onGoHome,
onOpenProject,
onNextAgent,
onPrevAgent,
}) => {
const [search, setSearch] = useState('');
const shouldReduceMotion = useReducedMotion();
Expand Down Expand Up @@ -195,6 +200,39 @@ const CommandPalette: React.FC<CommandPaletteProps> = ({
});
}

// Agent switching commands
if (onNextAgent) {
items.push({
id: 'nav-next-agent',
label: 'Next Agent',
description: APP_SHORTCUTS.NEXT_AGENT.description,
icon: <ArrowRightLeft className="h-4 w-4" />,
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: <ArrowRightLeft className="h-4 w-4" />,
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) {
Expand Down Expand Up @@ -241,6 +279,8 @@ const CommandPalette: React.FC<CommandPaletteProps> = ({
onToggleLeftSidebar,
onToggleRightSidebar,
onToggleTheme,
onNextAgent,
onPrevAgent,
runCommand,
]);

Expand Down Expand Up @@ -335,6 +375,12 @@ const CommandPalette: React.FC<CommandPaletteProps> = ({
{item.shortcut.modifier === 'cmd' && (
<CommandIcon className="h-3 w-3" />
)}
{item.shortcut.modifier === 'cmd+shift' && (
<>
<CommandIcon className="h-3 w-3" />
<span className="font-medium">⇧</span>
</>
)}
{item.shortcut.modifier === 'ctrl' && (
<span className="font-medium">Ctrl</span>
)}
Expand Down
6 changes: 6 additions & 0 deletions src/renderer/components/CommandPaletteWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export interface CommandPaletteWrapperProps {
handleGoHome: () => void;
handleOpenProject: () => void;
handleOpenSettings: () => void;
handleNextAgent: () => void;
handlePrevAgent: () => void;
}

const CommandPaletteWrapper: React.FC<CommandPaletteWrapperProps> = ({
Expand All @@ -25,6 +27,8 @@ const CommandPaletteWrapper: React.FC<CommandPaletteWrapperProps> = ({
handleGoHome,
handleOpenProject,
handleOpenSettings,
handleNextAgent,
handlePrevAgent,
}) => {
const { toggle: toggleLeftSidebar } = useSidebar();
const { toggle: toggleRightSidebar } = useRightSidebar();
Expand Down Expand Up @@ -53,6 +57,8 @@ const CommandPaletteWrapper: React.FC<CommandPaletteWrapperProps> = ({
onToggleTheme={toggleTheme}
onGoHome={handleGoHome}
onOpenProject={handleOpenProject}
onNextAgent={handleNextAgent}
onPrevAgent={handlePrevAgent}
/>
);
};
Expand Down