[aidan] ui/ux: removed keyboard shortcuts

This commit is contained in:
abccodes
2026-06-11 02:50:49 -07:00
parent b2d515566b
commit eadd6f71a2
4 changed files with 1 additions and 314 deletions
+1 -10
View File
@@ -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}<KeyboardShortcutsHelp /></>;
};
const DeepLinkListener: React.FC<{ children: React.ReactNode }> = ({ children }) => {
useDeepLink();
useWindowFocus();
@@ -469,8 +462,7 @@ const ThemedApp: React.FC = () => {
<CssBaseline />
<HashRouter>
<RouteTrackerMount />
<ShortcutsProvider>
<SettingsLoader>
<SettingsLoader>
<DefaultModelGuard>
<UpdateListener>
<CrashRecoveryChip />
@@ -502,7 +494,6 @@ const ThemedApp: React.FC = () => {
</UpdateListener>
</DefaultModelGuard>
</SettingsLoader>
</ShortcutsProvider>
</HashRouter>
</MuiThemeProvider>
);
@@ -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 (
<Dialog
open={open}
onClose={() => 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,
},
}}
>
<DialogTitle sx={{ color: c.text.primary, fontWeight: 600, fontSize: '1rem' }}>
Keyboard Shortcuts
</DialogTitle>
<DialogContent>
{shortcuts.map((s) => (
<Box
key={s.key}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
py: 0.75,
borderBottom: `0.5px solid ${c.border.medium}`,
'&:last-child': { borderBottom: 'none' },
}}
>
<Typography sx={{ color: c.text.muted, fontSize: '0.85rem' }}>{s.description}</Typography>
<Box
sx={{
bgcolor: c.bg.secondary,
border: `1px solid ${c.border.medium}`,
borderRadius: 1,
px: 1,
py: 0.25,
}}
>
<Typography
sx={{
color: c.accent.primary,
fontSize: '0.75rem',
fontFamily: c.font.mono,
fontWeight: 600,
}}
>
{s.key}
</Typography>
</Box>
</Box>
))}
</DialogContent>
</Dialog>
);
};
export default KeyboardShortcutsHelp;
@@ -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 }) => (
<Box
sx={{
bgcolor: c.bg.secondary,
border: `1px solid ${c.border.medium}`,
borderRadius: 1.5,
px: 1.25,
py: 0.4,
display: 'inline-flex',
alignItems: 'center',
}}
>
<Typography
sx={{
color: c.accent.primary,
fontSize: '0.75rem',
fontFamily: c.font.mono,
fontWeight: 600,
lineHeight: 1,
}}
>
{keys}
</Typography>
</Box>
);
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 (
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<Box>
@@ -436,95 +392,6 @@ export const CommandsContent: React.FC = () => {
)}
</Box>
<Box sx={{ my: 2, borderTop: `1px solid ${c.border.subtle}` }} />
<Box>
<SectionHeader
icon={<KeyboardIcon sx={{ fontSize: 22 }} />}
title="Keyboard Shortcuts"
subtitle="Press ? anywhere to see the quick-reference dialog"
count={SHORTCUTS.length}
c={c}
/>
<Box sx={{ display: 'flex', gap: 4 }}>
<Box sx={{ flex: 1 }}>
<Typography
sx={{
color: c.text.tertiary,
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: 1,
mb: 1.5,
px: 1,
}}
>
Navigation
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
{navShortcuts.map((s) => (
<Box
key={s.key}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
px: 1.5,
py: 1,
borderRadius: 2,
'&:hover': { bgcolor: `${c.accent.primary}06` },
transition: 'background-color 0.15s',
}}
>
<Typography sx={{ color: c.text.muted, fontSize: '0.84rem' }}>
{s.description}
</Typography>
<KeyBadge keys={s.key} c={c} />
</Box>
))}
</Box>
</Box>
<Box sx={{ flex: 1 }}>
<Typography
sx={{
color: c.text.tertiary,
fontSize: '0.7rem',
fontWeight: 600,
textTransform: 'uppercase',
letterSpacing: 1,
mb: 1.5,
px: 1,
}}
>
Actions
</Typography>
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
{actionShortcuts.map((s) => (
<Box
key={s.key}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
px: 1.5,
py: 1,
borderRadius: 2,
'&:hover': { bgcolor: `${c.accent.primary}06` },
transition: 'background-color 0.15s',
}}
>
<Typography sx={{ color: c.text.muted, fontSize: '0.84rem' }}>
{s.description}
</Typography>
<KeyBadge keys={s.key} c={c} />
</Box>
))}
</Box>
</Box>
</Box>
</Box>
</Box>
);
};
@@ -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]);
}