"use client"; import { useState, useEffect, useRef } from "react"; import { RotateCcw } from "lucide-react"; import { useChatStore } from "@/lib/store"; import { SectionHeader } from "./SettingRow"; import { cn } from "@/lib/utils"; const DEFAULT_SHORTCUTS: Record = { "new-conversation": "Ctrl+Shift+N", "send-message": "Enter", "focus-input": "Ctrl+L", "toggle-sidebar": "Ctrl+B", "open-settings": "Ctrl+,", "command-palette": "Ctrl+K", }; const SHORTCUT_LABELS: Record = { "new-conversation": { label: "New conversation", description: "Start a fresh conversation" }, "send-message": { label: "Send message", description: "Submit the current message" }, "focus-input": { label: "Focus input", description: "Jump to the message input" }, "toggle-sidebar": { label: "Toggle sidebar", description: "Show or hide the sidebar" }, "open-settings": { label: "Open settings", description: "Open this settings panel" }, "command-palette": { label: "Command palette", description: "Open the command palette" }, }; function captureKeyCombo(e: KeyboardEvent): string { e.preventDefault(); const parts: string[] = []; if (e.ctrlKey || e.metaKey) parts.push("Ctrl"); if (e.altKey) parts.push("Alt"); if (e.shiftKey) parts.push("Shift"); if (e.key && !["Control", "Alt", "Shift", "Meta"].includes(e.key)) { parts.push(e.key === " " ? "Space" : e.key); } return parts.join("+"); } function ShortcutRow({ id, binding, isDefault, isConflict, onRebind, onReset, }: { id: string; binding: string; isDefault: boolean; isConflict: boolean; onRebind: (combo: string) => void; onReset: () => void; }) { const [listening, setListening] = useState(false); const ref = useRef(null); const info = SHORTCUT_LABELS[id]; useEffect(() => { if (!listening) return; function handler(e: KeyboardEvent) { if (e.key === "Escape") { setListening(false); return; } const combo = captureKeyCombo(e); if (combo) { onRebind(combo); setListening(false); } } window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [listening, onRebind]); return (

{info?.label ?? id}

{info?.description}

{isConflict && (

Conflict with another shortcut

)}
{!isDefault && ( )}
); } export function KeyboardSettings() { const { settings, updateSettings, resetSettings } = useChatStore(); const keybindings = settings.keybindings; // Find conflicts const bindingValues = Object.values(keybindings); const conflicts = new Set( bindingValues.filter((v, i) => bindingValues.indexOf(v) !== i) ); function rebind(id: string, combo: string) { updateSettings({ keybindings: { ...keybindings, [id]: combo } }); } function resetOne(id: string) { updateSettings({ keybindings: { ...keybindings, [id]: DEFAULT_SHORTCUTS[id] }, }); } return (
resetSettings("keybindings")} />

Click a shortcut to rebind it. Press Escape to cancel.

{Object.entries(keybindings).map(([id, binding]) => ( rebind(id, combo)} onReset={() => resetOne(id)} /> ))}
); }