diff --git a/packages/frontend/src/layouts/IDELayout.tsx b/packages/frontend/src/layouts/IDELayout.tsx index 384715a..536639d 100644 --- a/packages/frontend/src/layouts/IDELayout.tsx +++ b/packages/frontend/src/layouts/IDELayout.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect } from "react"; -import { Outlet, Link, useLocation } from "react-router-dom"; +import { useState, useEffect, useCallback } from "react"; +import { Outlet, Link, useLocation, useNavigate } from "react-router-dom"; import { Terminal } from "../components/terminal/Terminal"; import { useWebSocket } from "../hooks/useWebSocket"; import { useTheme } from "../hooks/useTheme"; @@ -18,6 +18,7 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) { const [upstreamBehind, setUpstreamBehind] = useState(0); const [pendingToolset, setPendingToolset] = useState(0); const location = useLocation(); + const navigate = useNavigate(); const { subscribe } = useWebSocket(); const { theme, toggleTheme } = useTheme(); @@ -45,6 +46,38 @@ export function IDELayout({ sidebar }: { sidebar?: React.ReactNode }) { if (location.pathname === "/toolset") setPendingToolset(0); }, [location.pathname]); + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + const tag = (e.target as HTMLElement)?.tagName; + if (tag === "INPUT" || tag === "TEXTAREA" || (e.target as HTMLElement)?.closest?.(".monaco-editor")) { + return; + } + + if (e.ctrlKey && e.shiftKey && e.key === "B") { + e.preventDefault(); + navigate("/build"); + } else if (e.ctrlKey && e.key === "`") { + e.preventDefault(); + setTerminalOpen((v) => !v); + } else if (e.ctrlKey && e.shiftKey && e.key === "F") { + e.preventDefault(); + navigate("/editor"); + } else if (e.ctrlKey && e.shiftKey && e.key === "G") { + e.preventDefault(); + navigate("/repos"); + } else if (e.ctrlKey && e.key === ",") { + e.preventDefault(); + navigate("/settings"); + } + }, + [navigate], + ); + + useEffect(() => { + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [handleKeyDown]); + const getBadge = (path: string): number => { if (path === "/repos") return upstreamBehind; if (path === "/toolset") return pendingToolset;