8d650fefae
CI / build-and-push (push) Successful in 28s
Address 18 issues across high/medium/low impact tiers identified in a full interface review. Key changes: Models page decomposed into tabs, confirmation dialogs for irreversible actions (deploy/open-source/acquire), chart Y-axes made visible, hash router extended for Market tab persistence, collapsible sidebar, keyboard navigation shortcuts (g+key chords), notification bulk actions, achievement progress bars, and ARIA label improvements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
71 lines
1.7 KiB
TypeScript
71 lines
1.7 KiB
TypeScript
import { useEffect } from 'react';
|
|
import { useGameStore, type ActivePage } from '@/store';
|
|
import type { GameSpeed } from '@ai-tycoon/shared';
|
|
|
|
const PAGE_SHORTCUTS: Record<string, ActivePage> = {
|
|
d: 'dashboard',
|
|
i: 'infrastructure',
|
|
r: 'research',
|
|
m: 'models',
|
|
k: 'market',
|
|
f: 'finance',
|
|
t: 'talent',
|
|
s: 'settings',
|
|
};
|
|
|
|
export function useKeyboardShortcuts() {
|
|
useEffect(() => {
|
|
let gPressed = false;
|
|
let gTimer: ReturnType<typeof setTimeout>;
|
|
|
|
const handler = (e: KeyboardEvent) => {
|
|
if (e.ctrlKey && e.key === 'd') {
|
|
e.preventDefault();
|
|
window.dispatchEvent(new Event('toggle-dev-menu'));
|
|
return;
|
|
}
|
|
|
|
const target = e.target as HTMLElement;
|
|
if (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.tagName === 'SELECT') return;
|
|
|
|
const store = useGameStore.getState();
|
|
|
|
if (gPressed) {
|
|
gPressed = false;
|
|
clearTimeout(gTimer);
|
|
const page = PAGE_SHORTCUTS[e.key];
|
|
if (page) {
|
|
e.preventDefault();
|
|
store.setActivePage(page);
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (e.key) {
|
|
case ' ':
|
|
e.preventDefault();
|
|
store.togglePause();
|
|
break;
|
|
case '1':
|
|
store.setGameSpeed(1 as GameSpeed);
|
|
break;
|
|
case '2':
|
|
store.setGameSpeed(2 as GameSpeed);
|
|
break;
|
|
case '3':
|
|
store.setGameSpeed(5 as GameSpeed);
|
|
break;
|
|
case 'g':
|
|
gPressed = true;
|
|
gTimer = setTimeout(() => { gPressed = false; }, 500);
|
|
break;
|
|
}
|
|
};
|
|
window.addEventListener('keydown', handler);
|
|
return () => {
|
|
window.removeEventListener('keydown', handler);
|
|
clearTimeout(gTimer);
|
|
};
|
|
}, []);
|
|
}
|