diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index 295be880..9537087c 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -78,11 +78,9 @@ if (typeof window !== 'undefined') { } import { report, getSessionTraceState, getRecentActions } from '@/shared/serviceClient'; import { useRouteTracker } from '@/shared/hooks/useRouteTracker'; -import { useKeyboardShortcuts } from '@/shared/hooks/useKeyboardShortcuts'; import { useDeepLink } from '@/shared/hooks/useDeepLink'; import { useWindowFocus } from '@/shared/hooks/useWindowFocus'; import { useInteractionHeartbeat } from '@/shared/hooks/useInteractionHeartbeat'; -import KeyboardShortcutsHelp from './components/overlays/KeyboardShortcutsHelp'; import { ThemeProvider, useThemeMode, useClaudeTokens } from '@/shared/styles/ThemeContext'; import { ClaudeTokens } from '@/shared/styles/claudeTokens'; @@ -207,11 +205,6 @@ function buildMuiTheme(c: ClaudeTokens, mode: 'light' | 'dark') { }); } -const ShortcutsProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - useKeyboardShortcuts(); - return <>{children}; -}; - const DeepLinkListener: React.FC<{ children: React.ReactNode }> = ({ children }) => { useDeepLink(); useWindowFocus(); @@ -469,8 +462,7 @@ const ThemedApp: React.FC = () => { - - + @@ -502,7 +494,6 @@ const ThemedApp: React.FC = () => { - ); diff --git a/frontend/src/app/components/overlays/KeyboardShortcutsHelp.tsx b/frontend/src/app/components/overlays/KeyboardShortcutsHelp.tsx deleted file mode 100644 index cd92daa5..00000000 --- a/frontend/src/app/components/overlays/KeyboardShortcutsHelp.tsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import Dialog from '@mui/material/Dialog'; -import DialogTitle from '@mui/material/DialogTitle'; -import DialogContent from '@mui/material/DialogContent'; -import Box from '@mui/material/Box'; -import Typography from '@mui/material/Typography'; -import { useClaudeTokens } from '@/shared/styles/ThemeContext'; - -const shortcuts = [ - { key: 'd', description: 'Go to Dashboard' }, - { key: '1-9', description: 'Open agent by position' }, - { key: '⌘M', description: 'Add App' }, - { key: '⌘N', description: 'New Browser' }, - { key: '⌘O', description: 'History' }, - { key: 'Shift+A', description: 'Approve all pending' }, - { key: 'Shift+D', description: 'Deny all pending' }, - { key: '?', description: 'Show this help' }, -]; - -const KeyboardShortcutsHelp: React.FC = () => { - const c = useClaudeTokens(); - const [open, setOpen] = useState(false); - - useEffect(() => { - const handler = (e: KeyboardEvent) => { - const target = e.target as HTMLElement; - if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable) return; - if (e.key === '?') { - setOpen((prev) => !prev); - } - }; - window.addEventListener('keydown', handler); - return () => window.removeEventListener('keydown', handler); - }, []); - - return ( - setOpen(false)} - PaperProps={{ - sx: { - bgcolor: c.bg.surface, - color: c.text.primary, - borderRadius: 4, - border: `1px solid ${c.border.subtle}`, - minWidth: 360, - boxShadow: c.shadow.lg, - }, - }} - > - - Keyboard Shortcuts - - - {shortcuts.map((s) => ( - - {s.description} - - - {s.key} - - - - ))} - - - ); -}; - -export default KeyboardShortcutsHelp; diff --git a/frontend/src/app/pages/Commands/Commands.tsx b/frontend/src/app/pages/Commands/Commands.tsx index d7df9b97..ea9451ac 100644 --- a/frontend/src/app/pages/Commands/Commands.tsx +++ b/frontend/src/app/pages/Commands/Commands.tsx @@ -5,7 +5,6 @@ import Chip from '@mui/material/Chip'; import PsychologyIcon from '@mui/icons-material/Psychology'; import SmartToyOutlinedIcon from '@mui/icons-material/SmartToyOutlined'; import AlternateEmailIcon from '@mui/icons-material/AlternateEmail'; -import KeyboardIcon from '@mui/icons-material/Keyboard'; import TerminalIcon from '@mui/icons-material/Terminal'; import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined'; import LanguageIcon from '@mui/icons-material/Language'; @@ -36,46 +35,6 @@ interface AtCommand { isChild?: boolean; } -interface Shortcut { - key: string; - description: string; - category: 'navigation' | 'action'; -} - -const SHORTCUTS: Shortcut[] = [ - { key: 'd', description: 'Go to Dashboard', category: 'navigation' }, - { key: '1-9', description: 'Open agent by position', category: 'navigation' }, - { key: 'Shift+A', description: 'Approve all pending', category: 'action' }, - { key: 'Shift+D', description: 'Deny all pending', category: 'action' }, - { key: '?', description: 'Show shortcuts dialog', category: 'navigation' }, -]; - -const KeyBadge: React.FC<{ keys: string; c: any }> = ({ keys, c }) => ( - - - {keys} - - -); - const SectionHeader: React.FC<{ icon: React.ReactNode; title: string; @@ -248,9 +207,6 @@ export const CommandsContent: React.FC = () => { return items; }, [builtinTools, customTools, outputItems]); - const navShortcuts = SHORTCUTS.filter((s) => s.category === 'navigation'); - const actionShortcuts = SHORTCUTS.filter((s) => s.category === 'action'); - return ( @@ -436,95 +392,6 @@ export const CommandsContent: React.FC = () => { )} - - - - } - title="Keyboard Shortcuts" - subtitle="Press ? anywhere to see the quick-reference dialog" - count={SHORTCUTS.length} - c={c} - /> - - - - - Navigation - - - {navShortcuts.map((s) => ( - - - {s.description} - - - - ))} - - - - - - Actions - - - {actionShortcuts.map((s) => ( - - - {s.description} - - - - ))} - - - - ); }; diff --git a/frontend/src/shared/hooks/useKeyboardShortcuts.ts b/frontend/src/shared/hooks/useKeyboardShortcuts.ts deleted file mode 100644 index 941b092e..00000000 --- a/frontend/src/shared/hooks/useKeyboardShortcuts.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { useEffect, useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useAppDispatch, useAppSelector } from '@/shared/hooks'; -import { handleApproval, setActiveSession } from '@/shared/state/agentsSlice'; - -export function useKeyboardShortcuts() { - const navigate = useNavigate(); - const dispatch = useAppDispatch(); - const sessions = useAppSelector((state) => state.agents.sessions); - - const handler = useCallback( - (e: KeyboardEvent) => { - const target = e.target as HTMLElement | null; - const active = document.activeElement as HTMLElement | null; - // Double-guard: e.target AND document.activeElement. A bare-letter - // shortcut would otherwise fire if focus is on a wrapper Box and the - // child input never received it, kicking the user out mid-type. - const isInputLike = (el: HTMLElement | null) => - !!el && ( - el.tagName === 'INPUT' || - el.tagName === 'TEXTAREA' || - el.isContentEditable || - !!el.closest('input, textarea, [contenteditable="true"]') - ); - if (isInputLike(target) || isInputLike(active)) return; - - // Mod-gated shortcuts only. Bare letters were footguns: typing the - // letter "d" anywhere outside a tagged input field used to navigate - // home, which surprised users typing workflow titles/descriptions. - if (e.key.toLowerCase() === 'd' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - e.preventDefault(); - navigate('/'); - return; - } - - if (e.key === 'A' && e.shiftKey && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - for (const session of Object.values(sessions)) { - for (const req of session.pending_approvals) { - dispatch(handleApproval({ requestId: req.id, behavior: 'allow' })); - } - } - return; - } - - if (e.key === 'D' && e.shiftKey && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - for (const session of Object.values(sessions)) { - for (const req of session.pending_approvals) { - dispatch(handleApproval({ requestId: req.id, behavior: 'deny' })); - } - } - return; - } - - if (e.key >= '1' && e.key <= '9' && (e.metaKey || e.ctrlKey) && !e.shiftKey) { - e.preventDefault(); - const idx = parseInt(e.key) - 1; - const sessionList = Object.values(sessions).sort( - (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() - ); - if (sessionList[idx]) { - navigate('/'); - dispatch(setActiveSession(sessionList[idx].id)); - } - return; - } - }, - [navigate, dispatch, sessions] - ); - - useEffect(() => { - window.addEventListener('keydown', handler); - return () => window.removeEventListener('keydown', handler); - }, [handler]); -}