mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-05-23 22:20:23 +09:00
feat: add shortcut key
This commit is contained in:
parent
d51d7b6797
commit
f2195154f6
@ -646,3 +646,52 @@
|
|||||||
bottom: 30px;
|
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;
|
||||||
|
}
|
@ -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 (
|
||||||
|
<div className="modal-mask">
|
||||||
|
<Modal
|
||||||
|
title={Locale.Chat.ShortcutKey.Title}
|
||||||
|
onClose={props.onClose}
|
||||||
|
actions={[
|
||||||
|
<IconButton
|
||||||
|
type="primary"
|
||||||
|
text={Locale.UI.Confirm}
|
||||||
|
icon={<ConfirmIcon />}
|
||||||
|
key="ok"
|
||||||
|
onClick={() => {
|
||||||
|
props.onClose();
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div className={styles["shortcut-key-container"]}>
|
||||||
|
<div className={styles["shortcut-key-grid"]}>
|
||||||
|
{shortcuts.map((shortcut, index) => (
|
||||||
|
<div key={index} className={styles["shortcut-key-item"]}>
|
||||||
|
<div className={styles["shortcut-key-title"]}>
|
||||||
|
{shortcut.title}
|
||||||
|
</div>
|
||||||
|
<div className={styles["shortcut-key-keys"]}>
|
||||||
|
{shortcut.keys.map((key, i) => (
|
||||||
|
<div key={i} className={styles["shortcut-key"]}>
|
||||||
|
<span>{key}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function _Chat() {
|
function _Chat() {
|
||||||
type RenderMessage = ChatMessage & { preview?: boolean };
|
type RenderMessage = ChatMessage & { preview?: boolean };
|
||||||
|
|
||||||
@ -1373,6 +1424,69 @@ function _Chat() {
|
|||||||
setAttachImages(images);
|
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 (
|
return (
|
||||||
<div className={styles.chat} key={session.id}>
|
<div className={styles.chat} key={session.id}>
|
||||||
<div className="window-header" data-tauri-drag-region>
|
<div className="window-header" data-tauri-drag-region>
|
||||||
@ -1760,6 +1874,10 @@ function _Chat() {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{showShortcutKeyModal && (
|
||||||
|
<ShortcutKeyModal onClose={() => setShowShortcutKeyModal(false)} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ShortcutKeyModal } from "../components/chat";
|
||||||
import { getClientConfig } from "../config/client";
|
import { getClientConfig } from "../config/client";
|
||||||
import { SubmitKey } from "../store/config";
|
import { SubmitKey } from "../store/config";
|
||||||
|
|
||||||
@ -81,6 +82,14 @@ const cn = {
|
|||||||
SaveAs: "存为面具",
|
SaveAs: "存为面具",
|
||||||
},
|
},
|
||||||
IsContext: "预设提示词",
|
IsContext: "预设提示词",
|
||||||
|
ShortcutKey: {
|
||||||
|
Title: "键盘快捷方式",
|
||||||
|
newChat: "打开新聊天",
|
||||||
|
focusInput: "聚焦输入框",
|
||||||
|
copyLastMessage: "复制最后一个回复",
|
||||||
|
copyLastCode: "复制最后一个代码块",
|
||||||
|
showShortcutKey: "显示快捷方式",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "分享聊天记录",
|
Title: "分享聊天记录",
|
||||||
|
@ -83,6 +83,14 @@ const en: LocaleType = {
|
|||||||
SaveAs: "Save as Mask",
|
SaveAs: "Save as Mask",
|
||||||
},
|
},
|
||||||
IsContext: "Contextual Prompt",
|
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: {
|
Export: {
|
||||||
Title: "Export Messages",
|
Title: "Export Messages",
|
||||||
|
@ -81,6 +81,14 @@ const tw = {
|
|||||||
SaveAs: "另存新檔",
|
SaveAs: "另存新檔",
|
||||||
},
|
},
|
||||||
IsContext: "預設提示詞",
|
IsContext: "預設提示詞",
|
||||||
|
ShortcutKey: {
|
||||||
|
Title: "鍵盤快捷方式",
|
||||||
|
newChat: "打開新聊天",
|
||||||
|
focusInput: "聚焦輸入框",
|
||||||
|
copyLastMessage: "複製最後一個回覆",
|
||||||
|
copyLastCode: "複製最後一個代碼塊",
|
||||||
|
showShortcutKey: "顯示快捷方式",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Export: {
|
Export: {
|
||||||
Title: "將聊天記錄匯出為 Markdown",
|
Title: "將聊天記錄匯出為 Markdown",
|
||||||
|
Loading…
Reference in New Issue
Block a user