From f2195154f6a94e3ac324465c1adc6150180a186e Mon Sep 17 00:00:00 2001 From: DDMeaqua Date: Mon, 9 Sep 2024 18:55:37 +0800 Subject: [PATCH] feat: add shortcut key --- app/components/chat.module.scss | 49 +++++++++++++ app/components/chat.tsx | 118 ++++++++++++++++++++++++++++++++ app/locales/cn.ts | 9 +++ app/locales/en.ts | 8 +++ app/locales/tw.ts | 8 +++ 5 files changed, 192 insertions(+) diff --git a/app/components/chat.module.scss b/app/components/chat.module.scss index 7176399cc36..98972f623bd 100644 --- a/app/components/chat.module.scss +++ b/app/components/chat.module.scss @@ -646,3 +646,52 @@ bottom: 30px; } } + +.shortcut-key-container { + padding: 10px; + overflow-y: auto; + display: flex; + flex-direction: column; +} + +.shortcut-key-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 16px; +} + +.shortcut-key-item { + display: flex; + flex-direction: column; + overflow: hidden; + border: 1px solid #ddd; + border-radius: 10px; + padding: 10px; + background-color: #fff; +} + +.shortcut-key-title { + font-size: 14px; + color: #333; + margin-bottom: 8px; +} + +.shortcut-key-keys { + display: flex; + gap: 8px; +} + +.shortcut-key { + display: flex; + align-items: center; + justify-content: center; + border: 1px solid #ddd; + border-radius: 8px; + padding: 4px; + background-color: #f9f9f9; + min-width: 32px; +} + +.shortcut-key span { + font-size: 12px; +} \ No newline at end of file diff --git a/app/components/chat.tsx b/app/components/chat.tsx index dad1933ace9..ab94ab41dcd 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -829,6 +829,57 @@ export function DeleteImageButton(props: { deleteImage: () => void }) { ); } +export function ShortcutKeyModal(props: { onClose: () => void }) { + const shortcuts = [ + { title: Locale.Chat.ShortcutKey.newChat, keys: ["⌘", "Shift", "o"] }, + { title: Locale.Chat.ShortcutKey.focusInput, keys: ["Shift", "Esc"] }, + { title: Locale.Chat.ShortcutKey.copyLastCode, keys: ["⌘", "Shift", ";"] }, + { + title: Locale.Chat.ShortcutKey.copyLastMessage, + keys: ["⌘", "Shift", "c"], + }, + { title: Locale.Chat.ShortcutKey.showShortcutKey, keys: ["⌘", "/"] }, + ]; + return ( +
+ } + key="ok" + onClick={() => { + props.onClose(); + }} + />, + ]} + > +
+
+ {shortcuts.map((shortcut, index) => ( +
+
+ {shortcut.title} +
+
+ {shortcut.keys.map((key, i) => ( +
+ {key} +
+ ))} +
+
+ ))} +
+
+
+
+ ); +} + function _Chat() { type RenderMessage = ChatMessage & { preview?: boolean }; @@ -1373,6 +1424,69 @@ function _Chat() { setAttachImages(images); } + // 快捷键 + const [showShortcutKeyModal, setShowShortcutKeyModal] = useState(false); + + useEffect(() => { + const handleKeyDown = (event) => { + // 打开新聊天 command + shift + o + if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.key.toLowerCase() === "o" + ) { + event.preventDefault(); + setTimeout(() => { + chatStore.newSession(); + navigate(Path.Chat); + }, 10); + } + // 聚焦聊天输入 shift + esc + else if (event.shiftKey && event.key.toLowerCase() === "escape") { + event.preventDefault(); + inputRef.current?.focus(); + } + // 复制最后一个代码块 command + shift + ; + else if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.code === "Semicolon" + ) { + event.preventDefault(); + const copyCodeButton = document.querySelectorAll(".copy-code-button"); + if (copyCodeButton.length > 0) { + copyCodeButton[copyCodeButton.length - 1].click(); + } + } + // 复制最后一个回复 command + shift + c + else if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.key.toLowerCase() === "c" + ) { + event.preventDefault(); + const lastNonUserMessage = messages + .filter((message) => message.role !== "user") + .pop(); + if (lastNonUserMessage) { + const lastMessageContent = getMessageTextContent(lastNonUserMessage); + copyToClipboard(lastMessageContent); + } + } + // 展示快捷键 command + / + else if ((event.metaKey || event.ctrlKey) && event.key === "/") { + event.preventDefault(); + setShowShortcutKeyModal(true); + } + }; + + window.addEventListener("keydown", handleKeyDown); + + return () => { + window.removeEventListener("keydown", handleKeyDown); + }; + }, [messages, chatStore, navigate]); + return (
@@ -1760,6 +1874,10 @@ function _Chat() { }} /> )} + + {showShortcutKeyModal && ( + setShowShortcutKeyModal(false)} /> + )}
); } diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 33e368f69f4..24f707e4018 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -1,3 +1,4 @@ +import { ShortcutKeyModal } from "../components/chat"; import { getClientConfig } from "../config/client"; import { SubmitKey } from "../store/config"; @@ -81,6 +82,14 @@ const cn = { SaveAs: "存为面具", }, IsContext: "预设提示词", + ShortcutKey: { + Title: "键盘快捷方式", + newChat: "打开新聊天", + focusInput: "聚焦输入框", + copyLastMessage: "复制最后一个回复", + copyLastCode: "复制最后一个代码块", + showShortcutKey: "显示快捷方式", + }, }, Export: { Title: "分享聊天记录", diff --git a/app/locales/en.ts b/app/locales/en.ts index 403b9b687e7..09b76f1fa12 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -83,6 +83,14 @@ const en: LocaleType = { SaveAs: "Save as Mask", }, IsContext: "Contextual Prompt", + ShortcutKey: { + Title: "Keyboard Shortcuts", + newChat: "Open New Chat", + focusInput: "Focus Input Field", + copyLastMessage: "Copy Last Reply", + copyLastCode: "Copy Last Code Block", + showShortcutKey: "Show Shortcuts", + }, }, Export: { Title: "Export Messages", diff --git a/app/locales/tw.ts b/app/locales/tw.ts index 6b2c0fd65b1..c54a7b8c5ae 100644 --- a/app/locales/tw.ts +++ b/app/locales/tw.ts @@ -81,6 +81,14 @@ const tw = { SaveAs: "另存新檔", }, IsContext: "預設提示詞", + ShortcutKey: { + Title: "鍵盤快捷方式", + newChat: "打開新聊天", + focusInput: "聚焦輸入框", + copyLastMessage: "複製最後一個回覆", + copyLastCode: "複製最後一個代碼塊", + showShortcutKey: "顯示快捷方式", + }, }, Export: { Title: "將聊天記錄匯出為 Markdown",