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
35 changes: 30 additions & 5 deletions src/renderer/components/AppKeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ import { useTaskManagementContext } from '../contexts/TaskManagementContext';
export interface AppKeyboardShortcutsProps {
showCommandPalette: boolean;
showSettings: boolean;
showBrowser: boolean;
showDiffViewer: boolean;
showEditor: boolean;
showKanban: boolean;
handleToggleCommandPalette: () => void;
handleOpenSettings: () => void;
handleCloseCommandPalette: () => void;
handleCloseSettings: () => void;
handleCloseBrowser: () => void;
handleCloseDiffViewer: () => void;
handleCloseEditor: () => void;
handleCloseKanban: () => void;
handleToggleKanban: () => void;
handleToggleEditor: () => void;
handleOpenInEditor: () => void;
Expand All @@ -21,10 +29,18 @@ export interface AppKeyboardShortcutsProps {
const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
showCommandPalette,
showSettings,
showBrowser,
showDiffViewer,
showEditor,
showKanban,
handleToggleCommandPalette,
handleOpenSettings,
handleCloseCommandPalette,
handleCloseSettings,
handleCloseBrowser,
handleCloseDiffViewer,
handleCloseEditor,
handleCloseKanban,
handleToggleKanban,
handleToggleEditor,
handleOpenInEditor,
Expand Down Expand Up @@ -55,13 +71,22 @@ const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
new CustomEvent('emdash:switch-agent', { detail: { direction: 'prev' } })
),
onOpenInEditor: handleOpenInEditor,
onCloseModal: showCommandPalette
? handleCloseCommandPalette
: showSettings
? handleCloseSettings
: undefined,
onCloseModal: (
[
[showCommandPalette, handleCloseCommandPalette],
[showSettings, handleCloseSettings],
[showBrowser, handleCloseBrowser],
[showDiffViewer, handleCloseDiffViewer],
[showEditor, handleCloseEditor],
[showKanban, handleCloseKanban],
] as const
).find(([open]) => open)?.[1],
isCommandPaletteOpen: showCommandPalette,
isSettingsOpen: showSettings,
isBrowserOpen: showBrowser,
isDiffViewerOpen: showDiffViewer,
isEditorOpen: showEditor,
isKanbanOpen: showKanban,
customKeyboardSettings: keyboardSettings ?? undefined,
});

Expand Down
13 changes: 9 additions & 4 deletions src/renderer/hooks/useKeyboardShortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -487,12 +487,17 @@ export function useKeyboardShortcuts(handlers: GlobalShortcutHandlers) {
// Command palette is blocking; settings behaves like a page and should
// not force-close for global shortcuts.
const isCommandPaletteOpen = Boolean(handlers.isCommandPaletteOpen);
const hasClosableOverlay = Boolean(
handlers.isCommandPaletteOpen || handlers.isSettingsOpen
const hasClosableView = Boolean(
handlers.isCommandPaletteOpen ||
handlers.isSettingsOpen ||
handlers.isBrowserOpen ||
handlers.isDiffViewerOpen ||
handlers.isEditorOpen ||
handlers.isKanbanOpen
);

// Modal-priority shortcuts (like Escape) only work when an overlay is open
if (shortcut.priority === 'modal' && !hasClosableOverlay) continue;
// Modal-priority shortcuts (like Escape) only work when a closable view is open
if (shortcut.priority === 'modal' && !hasClosableView) continue;

// Global shortcuts
if (shortcut.priority === 'global') {
Expand Down
4 changes: 4 additions & 0 deletions src/renderer/types/shortcuts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ export interface GlobalShortcutHandlers {
// State checks
isCommandPaletteOpen?: boolean;
isSettingsOpen?: boolean;
isBrowserOpen?: boolean;
isDiffViewerOpen?: boolean;
isEditorOpen?: boolean;
isKanbanOpen?: boolean;

// Custom keyboard settings
customKeyboardSettings?: KeyboardSettings;
Expand Down
39 changes: 31 additions & 8 deletions src/renderer/views/Workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { activityStore } from '@/lib/activityStore';
import { handleMenuUndo, handleMenuRedo } from '@/lib/menuUndoRedo';
import { rpc } from '@/lib/rpc';
import { soundPlayer } from '@/lib/soundPlayer';
import BrowserProvider from '@/providers/BrowserProvider';
import BrowserProvider, { useBrowser } from '@/providers/BrowserProvider';
import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
import { SettingsPageTab } from '@/components/SettingsPage';
const PANEL_RESIZE_DRAGGING_EVENT = 'emdash:panel-resize-dragging';
Expand All @@ -60,6 +60,20 @@ const RightSidebarBridge: React.FC<{
return null;
};

/** Bridge that reads BrowserProvider context and forwards it to AppKeyboardShortcuts */
const BrowserAwareShortcuts: React.FC<
Omit<React.ComponentProps<typeof AppKeyboardShortcuts>, 'showBrowser' | 'handleCloseBrowser'>
> = (props) => {
const browser = useBrowser();
return (
<AppKeyboardShortcuts
{...props}
showBrowser={browser.isOpen}
handleCloseBrowser={browser.close}
/>
);
};

export function Workspace() {
useTheme(); // Initialize theme on app startup
const { showModal } = useModalContext();
Expand Down Expand Up @@ -100,6 +114,11 @@ export function Workspace() {
const [showDiffViewer, setShowDiffViewer] = useState(false);
const [diffViewerInitialFile, setDiffViewerInitialFile] = useState<string | null>(null);
const [diffViewerTaskPath, setDiffViewerTaskPath] = useState<string | null>(null);
const handleCloseDiffViewer = useCallback(() => {
setShowDiffViewer(false);
setDiffViewerInitialFile(null);
setDiffViewerTaskPath(null);
}, []);
const panelHandleDraggingRef = useRef<Record<ResizeHandleId, boolean>>({
left: false,
right: false,
Expand Down Expand Up @@ -176,6 +195,8 @@ export function Workspace() {
setShowKanban(false);
setShowEditorMode((v) => !v);
}, [setShowKanban, setShowEditorMode]);
const handleCloseEditor = useCallback(() => setShowEditorMode(false), [setShowEditorMode]);
const handleCloseKanban = useCallback(() => setShowKanban(false), [setShowKanban]);

// --- Task management ---
const taskMgmt = useTaskManagementContext();
Expand Down Expand Up @@ -313,13 +334,19 @@ export function Workspace() {
<KeyboardSettingsProvider>
<SidebarProvider>
<RightSidebarProvider>
<AppKeyboardShortcuts
<BrowserAwareShortcuts
showCommandPalette={showCommandPalette}
showSettings={showSettingsPage}
showDiffViewer={showDiffViewer}
showEditor={showEditorMode && !!activeTask && !!selectedProject}
showKanban={!!projectMgmt.showKanban && !!selectedProject}
handleToggleCommandPalette={handleToggleCommandPalette}
handleOpenSettings={handleToggleSettingsPage}
handleCloseCommandPalette={handleCloseCommandPalette}
handleCloseSettings={handleCloseSettingsPage}
handleCloseDiffViewer={handleCloseDiffViewer}
handleCloseEditor={handleCloseEditor}
handleCloseKanban={handleCloseKanban}
handleToggleKanban={handleToggleKanban}
handleToggleEditor={handleToggleEditor}
handleOpenInEditor={handleOpenInEditor}
Expand Down Expand Up @@ -370,11 +397,7 @@ export function Workspace() {
<div className="flex h-full flex-col overflow-hidden bg-background text-foreground">
{showDiffViewer ? (
<DiffViewer
onClose={() => {
setShowDiffViewer(false);
setDiffViewerInitialFile(null);
setDiffViewerTaskPath(null);
}}
onClose={handleCloseDiffViewer}
taskId={activeTask?.id}
taskPath={diffViewerTaskPath || activeTask?.path}
initialFile={diffViewerInitialFile}
Expand Down Expand Up @@ -436,7 +459,7 @@ export function Workspace() {
taskPath={activeTask.path}
taskName={activeTask.name}
projectName={selectedProject.name}
onClose={() => setShowEditorMode(false)}
onClose={handleCloseEditor}
connectionId={derivedRemoteConnectionId}
remotePath={derivedRemotePath}
/>
Expand Down