From 625a6705481a17309d4a5cf017dac38789910bfa Mon Sep 17 00:00:00 2001 From: kareena0229 Date: Wed, 17 Jun 2026 20:26:18 +0530 Subject: [PATCH] feat: add keyboard shortcut support for chat actions --- frontend/src/components/chat/ChatPanel.tsx | 107 +++++++++++++++------ 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/frontend/src/components/chat/ChatPanel.tsx b/frontend/src/components/chat/ChatPanel.tsx index 59e58094..87a4059b 100644 --- a/frontend/src/components/chat/ChatPanel.tsx +++ b/frontend/src/components/chat/ChatPanel.tsx @@ -8,6 +8,7 @@ import { useChatStore, type ChatMsg, type SourceBoundingBox, type SourceChunk } import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; import { Textarea } from "@/components/ui/textarea"; + import MessageBubble from "./MessageBubble"; import SourceCard from "./SourceCard"; import { Send, Loader2, Trash2, MessageSquare, Download, Mic, MicOff, HelpCircle } from "lucide-react"; @@ -413,44 +414,70 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) { } }; - // ── NEW KEYBOARD SHORTCUTS ENGINE EFFECT ────────────────────────── - useEffect(() => { - const handleGlobalKeyDown = (e: KeyboardEvent) => { - const isCmdOrCtrl = e.metaKey || e.ctrlKey; - - // Shortcut 1: Ctrl/Cmd + Enter -> Send Message (When textarea has focus) - if (isCmdOrCtrl && e.key === "Enter") { - if (document.activeElement === textareaRef.current) { - e.preventDefault(); - handleSend(); - } - } +// ── KEYBOARD SHORTCUTS ────────────────────────── +useEffect(() => { + const handleGlobalKeyDown = (e: KeyboardEvent) => { + const isCmdOrCtrl = e.metaKey || e.ctrlKey; - // Shortcut 2: Escape -> Clear Input / Close Modal - if (e.key === "Escape") { - if (document.activeElement === textareaRef.current) { - e.preventDefault(); - setInput(""); // Clear textarea state - } else if (showHelpModal) { - setShowHelpModal(false); // Close shortcuts modal if open - } + // Ctrl + Enter => Send + if (isCmdOrCtrl && e.key === "Enter") { + if (document.activeElement === textareaRef.current) { + e.preventDefault(); + handleSend(); } + } - // Shortcut 3: Ctrl/Cmd + K -> Focus chat input from anywhere - if (isCmdOrCtrl && (e.key === "k" || e.key === "K")) { + // Escape => Clear input / close modal + if (e.key === "Escape") { + if (document.activeElement === textareaRef.current) { e.preventDefault(); - textareaRef.current?.focus(); + setInput(""); + } else if (showHelpModal) { + setShowHelpModal(false); } - }; + } - window.addEventListener("keydown", handleGlobalKeyDown); - return () => { - window.removeEventListener("keydown", handleGlobalKeyDown); - }; - }, [input, streaming, showHelpModal]); // Dependencies updated to capture fresh state data + // Ctrl + K => Focus input + if (isCmdOrCtrl && (e.key === "k" || e.key === "K")) { + e.preventDefault(); + textareaRef.current?.focus(); + } + // Ctrl + Shift + C => Copy latest assistant response + if ( + isCmdOrCtrl && + e.shiftKey && + (e.key === "c" || e.key === "C") + ) { + e.preventDefault(); + + const latestAssistant = [...messages] + .reverse() + .find((msg) => msg.role === "assistant"); + + if (latestAssistant?.content) { + navigator.clipboard.writeText(latestAssistant.content); + } + } + }; + + window.addEventListener("keydown", handleGlobalKeyDown); + + return () => { + window.removeEventListener("keydown", handleGlobalKeyDown); + }; + }, [ + input, + streaming, + showHelpModal, + messages, + setInput, + handleSend, +]); + return (
+ {/* ── Chat Messages ──────────────────────────── */}
{historyLoading ? ( @@ -507,6 +534,7 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
+ {/* ── Input Area ─────────────────────────────── */}
@@ -737,6 +765,24 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
  • + + Copy Latest Response + +
    + + Ctrl + + + + + Shift + + + + + C + +
    +
  • +
  • Clear Chat Input
    Esc @@ -753,7 +799,10 @@ export default function ChatPanel({ activeDoc, onCitationClick }: Props) {
  • + )} +
    + ); } \ No newline at end of file