From a695c4e8933ca87d2c3233a6c847a59481394923 Mon Sep 17 00:00:00 2001 From: Reekin Date: Sun, 10 Mar 2024 16:26:05 +0800 Subject: [PATCH 1/4] Add Shortcut QuickChat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 快捷键:呼出窗口并激活输入光标 --- app/components/chat.tsx | 11 +++++++++++ app/components/home.tsx | 2 ++ app/components/settings.tsx | 23 +++++++++++++++++++++++ app/locales/cn.ts | 1 + app/locales/en.ts | 1 + app/store/config.ts | 1 + app/utils.ts | 1 + src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 20 ++++++++++++++++++++ src-tauri/tauri.conf.json | 3 +++ 10 files changed, 64 insertions(+), 1 deletion(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index bcd0e605df2..a9e6c9b70b2 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -97,6 +97,7 @@ import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; +import { listen } from '@tauri-apps/api/event'; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -711,6 +712,16 @@ function _Chat() { // eslint-disable-next-line react-hooks/exhaustive-deps useEffect(measure, [userInput]); + useEffect(() => { + const unlisten = listen('activate_input_field', () => { + inputRef.current?.focus(); + }); + + return () => { + unlisten.then((f) => f());; + }; + }, []); + // chat commands shortcuts const chatCommands = useChatCommand({ new: () => chatStore.newSession(), diff --git a/app/components/home.tsx b/app/components/home.tsx index 8386ba144b9..f179ef3fe23 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -29,6 +29,7 @@ import { AuthPage } from "./auth"; import { getClientConfig } from "../config/client"; import { ClientApi } from "../client/api"; import { useAccessStore } from "../store"; +import { invoke } from '@tauri-apps/api/tauri'; export function Loading(props: { noLogo?: boolean }) { return ( @@ -183,6 +184,7 @@ export function useLoadData() { })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + invoke('update_shortcut', { shortcut: config.shortcutQuickChat }); } export function Home() { diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 84ae7edf651..5adb952a4a9 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -71,6 +71,7 @@ import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; import { ProviderType } from "../utils/cloud"; +import { invoke } from '@tauri-apps/api/tauri'; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -557,6 +558,9 @@ function SyncItems() { ); } +async function updateShortcut(newShortcut: string) { + await invoke('update_shortcut', { shortcut: newShortcut }); +} export function Settings() { const navigate = useNavigate(); @@ -650,6 +654,8 @@ export function Settings() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + updateShortcut(config.shortcutQuickChat); + const clientConfig = useMemo(() => getClientConfig(), []); const showAccessCode = enabledAccessControl && !clientConfig?.isApp; @@ -825,6 +831,23 @@ export function Settings() { } > + + + { + updateConfig( + (config) => + (config.shortcutQuickChat = e.currentTarget.value), + ); + updateShortcut(config.shortcutQuickChat); + } + } + > + diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 5d0c284283e..ba25bb02078 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -176,6 +176,7 @@ const cn = { Title: "预览气泡", SubTitle: "在预览气泡中预览 Markdown 内容", }, + ShortcutQuickChat:"快捷键-快速对话", AutoGenerateTitle: { Title: "自动生成标题", SubTitle: "根据对话内容生成合适的标题", diff --git a/app/locales/en.ts b/app/locales/en.ts index 79a91d7ccd5..2378345461c 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -178,6 +178,7 @@ const en: LocaleType = { Title: "Send Preview Bubble", SubTitle: "Preview markdown in bubble", }, + ShortcutQuickChat:"Shortcut-QuickChat", AutoGenerateTitle: { Title: "Auto Generate Title", SubTitle: "Generate a suitable title based on the conversation content", diff --git a/app/store/config.ts b/app/store/config.ts index 6f2f558a042..ce565f50117 100644 --- a/app/store/config.ts +++ b/app/store/config.ts @@ -34,6 +34,7 @@ export const DEFAULT_CONFIG = { theme: Theme.Auto as Theme, tightBorder: !!getClientConfig()?.isApp, sendPreviewBubble: true, + shortcutQuickChat: "Alt+B", enableAutoGenerateTitle: true, sidebarWidth: DEFAULT_SIDEBAR_WIDTH, diff --git a/app/utils.ts b/app/utils.ts index 8b755afeac1..bd1da2febe7 100644 --- a/app/utils.ts +++ b/app/utils.ts @@ -295,6 +295,7 @@ export function isVisionModel(model: string) { return ( // model.startsWith("gpt-4-vision") || // model.startsWith("gemini-pro-vision") || + model.startsWith("claude-3-opus-20240229") || model.includes("vision") ); } diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index e0892590223..67ef604d737 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.5.1", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.5.4", features = [ +tauri = { version = "1.5.4", features = [ "global-shortcut-all", "notification-all", "fs-all", "clipboard-all", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ed3ec32f37b..1e94997c9dc 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,9 +1,29 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use tauri::{GlobalShortcutManager, Manager}; + +#[tauri::command] +fn update_shortcut(shortcut: String, handle: tauri::AppHandle) { + handle + .global_shortcut_manager() + .unregister_all() + .unwrap(); + + let window = handle.get_window("main").unwrap(); + handle + .global_shortcut_manager() + .register(&shortcut, move || { + window.show().unwrap(); + window.set_focus().unwrap(); + window.emit("activate_input_field", {}).unwrap(); + }) + .unwrap(); +} fn main() { tauri::Builder::default() .plugin(tauri_plugin_window_state::Builder::default().build()) + .invoke_handler(tauri::generate_handler![update_shortcut]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 405d267ff65..73827b87743 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -14,6 +14,9 @@ "tauri": { "allowlist": { "all": false, + "globalShortcut": { + "all":true + }, "shell": { "all": false, "open": true From e79af3372a6d3409f241f792480ab175a1921fd5 Mon Sep 17 00:00:00 2001 From: Reekin Date: Mon, 11 Mar 2024 19:29:12 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE=E5=91=BC=E5=87=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 之前的提交有问题,窗口最小化无法呼出 --- app/components/home.tsx | 9 +++++-- app/components/settings.tsx | 37 +++++++++++++++------------- package.json | 3 ++- src-tauri/Cargo.toml | 2 +- src-tauri/src/main.rs | 48 ++++++++++++++++++++++++++++++------- src-tauri/tauri.conf.json | 4 ++++ 6 files changed, 73 insertions(+), 30 deletions(-) diff --git a/app/components/home.tsx b/app/components/home.tsx index f179ef3fe23..cc2c6b442e4 100644 --- a/app/components/home.tsx +++ b/app/components/home.tsx @@ -29,7 +29,7 @@ import { AuthPage } from "./auth"; import { getClientConfig } from "../config/client"; import { ClientApi } from "../client/api"; import { useAccessStore } from "../store"; -import { invoke } from '@tauri-apps/api/tauri'; +import { invoke } from "@tauri-apps/api/tauri"; export function Loading(props: { noLogo?: boolean }) { return ( @@ -184,7 +184,12 @@ export function useLoadData() { })(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - invoke('update_shortcut', { shortcut: config.shortcutQuickChat }); + + useEffect(() => { + (async () => { + await invoke("update_shortcut", { shortcut: config.shortcutQuickChat }); + })(); + }, []); } export function Home() { diff --git a/app/components/settings.tsx b/app/components/settings.tsx index 5adb952a4a9..b27957e06d4 100644 --- a/app/components/settings.tsx +++ b/app/components/settings.tsx @@ -71,7 +71,7 @@ import { useSyncStore } from "../store/sync"; import { nanoid } from "nanoid"; import { useMaskStore } from "../store/mask"; import { ProviderType } from "../utils/cloud"; -import { invoke } from '@tauri-apps/api/tauri'; +import { invoke } from "@tauri-apps/api/tauri"; function EditPromptModal(props: { id: string; onClose: () => void }) { const promptStore = usePromptStore(); @@ -558,8 +558,12 @@ function SyncItems() { ); } -async function updateShortcut(newShortcut: string) { - await invoke('update_shortcut', { shortcut: newShortcut }); +async function useUpdateShortcut(newShortcut: string) { + useEffect(() => { + (async () => { + await invoke("update_shortcut", { shortcut: newShortcut }); + })(); + }, []); } export function Settings() { @@ -654,7 +658,7 @@ export function Settings() { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - updateShortcut(config.shortcutQuickChat); + useUpdateShortcut(config.shortcutQuickChat); const clientConfig = useMemo(() => getClientConfig(), []); const showAccessCode = enabledAccessControl && !clientConfig?.isApp; @@ -832,20 +836,19 @@ export function Settings() { > - + { - updateConfig( - (config) => - (config.shortcutQuickChat = e.currentTarget.value), - ); - updateShortcut(config.shortcutQuickChat); - } - } + type="text" + value={config.shortcutQuickChat} + onChange={(e) => { + updateConfig( + (config) => + (config.shortcutQuickChat = e.currentTarget.value), + ); + invoke("update_shortcut", { + shortcut: config.shortcutQuickChat, + }); + }} > diff --git a/package.json b/package.json index b31d6a901a0..4520db33aaa 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "@hello-pangea/dnd": "^16.5.0", "@next/third-parties": "^14.1.0", "@svgr/webpack": "^6.5.1", + "@tauri-apps/api": "^1.5.3", "@vercel/analytics": "^0.1.11", "@vercel/speed-insights": "^1.0.2", "emoji-picker-react": "^4.5.15", @@ -64,4 +65,4 @@ "resolutions": { "lint-staged/yaml": "^2.2.2" } -} \ No newline at end of file +} diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 67ef604d737..d059dc6dedf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,7 +17,7 @@ tauri-build = { version = "1.5.1", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.5.4", features = [ "global-shortcut-all", +tauri = { version = "1.5.4", features = [ "system-tray", "global-shortcut-all", "notification-all", "fs-all", "clipboard-all", diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1e94997c9dc..561b3eb875f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,6 +1,6 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::{GlobalShortcutManager, Manager}; +use tauri::{GlobalShortcutManager, Manager,CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem}; #[tauri::command] fn update_shortcut(shortcut: String, handle: tauri::AppHandle) { @@ -10,20 +10,50 @@ fn update_shortcut(shortcut: String, handle: tauri::AppHandle) { .unwrap(); let window = handle.get_window("main").unwrap(); - handle + match handle .global_shortcut_manager() .register(&shortcut, move || { - window.show().unwrap(); + println!("Shortcut triggered successfully"); + window.unminimize().unwrap(); window.set_focus().unwrap(); window.emit("activate_input_field", {}).unwrap(); }) - .unwrap(); + { + Ok(_) => println!("Shortcut registered successfully"), + Err(err) => eprintln!("Failed to register shortcut: {}", err), + } } fn main() { - tauri::Builder::default() - .plugin(tauri_plugin_window_state::Builder::default().build()) - .invoke_handler(tauri::generate_handler![update_shortcut]) - .run(tauri::generate_context!()) - .expect("error while running tauri application"); + let quit = CustomMenuItem::new("quit".to_string(), "Quit"); + let tray_menu = SystemTrayMenu::new() + .add_item(quit); + + let system_tray = SystemTray::new() + .with_menu(tray_menu); + + tauri::Builder::default() + .plugin(tauri_plugin_window_state::Builder::default().build()) + .invoke_handler(tauri::generate_handler![update_shortcut]) + .system_tray(system_tray) + .on_system_tray_event(|app, event| match event { + SystemTrayEvent::LeftClick { + position: _, + size: _, + .. + } => { + let window = app.get_window("main").unwrap(); + window.show().unwrap(); + window.set_focus().unwrap(); + } + SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() { + "quit" => { + std::process::exit(0); + } + _ => {} + }, + _ => {} + }) + .run(tauri::generate_context!()) + .expect("error while running tauri application"); } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 73827b87743..29348eee1dc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -55,6 +55,10 @@ "all": true } }, + "systemTray": { + "iconPath": "../public/favicon-16x16.png", + "iconAsTemplate": true + }, "bundle": { "active": true, "category": "DeveloperTool", From d3d656239174dea9a302e65fd3a5b358dd7ad9ad Mon Sep 17 00:00:00 2001 From: Reekin Date: Tue, 12 Mar 2024 13:12:19 +0800 Subject: [PATCH 3/4] Split code into package level Split a the code according to the guidance. --- src-tauri/src/main.rs | 33 ++++----------------------------- src-tauri/src/shortcuts.rs | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 29 deletions(-) create mode 100644 src-tauri/src/shortcuts.rs diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 561b3eb875f..20dff2179b0 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,28 +1,7 @@ // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use tauri::{GlobalShortcutManager, Manager,CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem}; - -#[tauri::command] -fn update_shortcut(shortcut: String, handle: tauri::AppHandle) { - handle - .global_shortcut_manager() - .unregister_all() - .unwrap(); - - let window = handle.get_window("main").unwrap(); - match handle - .global_shortcut_manager() - .register(&shortcut, move || { - println!("Shortcut triggered successfully"); - window.unminimize().unwrap(); - window.set_focus().unwrap(); - window.emit("activate_input_field", {}).unwrap(); - }) - { - Ok(_) => println!("Shortcut registered successfully"), - Err(err) => eprintln!("Failed to register shortcut: {}", err), - } -} +use tauri::{Manager,CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu}; +mod shortcuts; fn main() { let quit = CustomMenuItem::new("quit".to_string(), "Quit"); @@ -34,14 +13,10 @@ fn main() { tauri::Builder::default() .plugin(tauri_plugin_window_state::Builder::default().build()) - .invoke_handler(tauri::generate_handler![update_shortcut]) + .invoke_handler(tauri::generate_handler![shortcuts::update_shortcut]) .system_tray(system_tray) .on_system_tray_event(|app, event| match event { - SystemTrayEvent::LeftClick { - position: _, - size: _, - .. - } => { + SystemTrayEvent::LeftClick {..} => { let window = app.get_window("main").unwrap(); window.show().unwrap(); window.set_focus().unwrap(); diff --git a/src-tauri/src/shortcuts.rs b/src-tauri/src/shortcuts.rs new file mode 100644 index 00000000000..5497f79dfa1 --- /dev/null +++ b/src-tauri/src/shortcuts.rs @@ -0,0 +1,22 @@ +use tauri::{Manager,AppHandle, GlobalShortcutManager}; + +#[tauri::command] +pub fn update_shortcut(shortcut: String, handle: AppHandle) { + handle + .global_shortcut_manager() + .unregister_all() + .unwrap(); + + let window = handle.get_window("main").unwrap(); + match handle + .global_shortcut_manager() + .register(&shortcut, move || { + println!("Shortcut triggered successfully"); + window.unminimize().unwrap(); + window.set_focus().unwrap(); + window.emit("activate_input_field", {}).unwrap(); + }) { + Ok(_) => println!("Shortcut registered successfully"), + Err(err) => eprintln!("Failed to register shortcut: {}", err), + } +} \ No newline at end of file From 2b5fbb7f1870c722dac474fc6d465f7552d4f385 Mon Sep 17 00:00:00 2001 From: Reekin Date: Mon, 18 Mar 2024 05:17:55 +0800 Subject: [PATCH 4/4] Add Quick NewChat Action --- app/components/chat.tsx | 18 ++++++++++++++++-- app/locales/cn.ts | 1 + app/locales/en.ts | 1 + 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/app/components/chat.tsx b/app/components/chat.tsx index bcd0e605df2..99e3e2d941b 100644 --- a/app/components/chat.tsx +++ b/app/components/chat.tsx @@ -37,6 +37,7 @@ import AutoIcon from "../icons/auto.svg"; import BottomIcon from "../icons/bottom.svg"; import StopIcon from "../icons/pause.svg"; import RobotIcon from "../icons/robot.svg"; +import AddIcon from "../icons/add.svg"; import { ChatMessage, @@ -97,6 +98,8 @@ import { ExportMessageModal } from "./exporter"; import { getClientConfig } from "../config/client"; import { useAllModels } from "../utils/hooks"; import { MultimodalContent } from "../client/api"; +import { InputRange } from "./input-range"; +import { config } from "process"; const Markdown = dynamic(async () => (await import("./markdown")).Markdown, { loading: () => , @@ -555,6 +558,15 @@ export function ChatActions(props: { icon={} /> + } + onClick={() => { + chatStore.newSession(chatStore.currentSession().mask); + navigate(Path.Chat); + }} + /> + {showModelSelector && ( ) => { const currentModel = chatStore.currentSession().mask.modelConfig.model; - if(!isVisionModel(currentModel)){return;} + if (!isVisionModel(currentModel)) { + return; + } const items = (event.clipboardData || window.clipboardData).items; for (const item of items) { if (item.kind === "file" && item.type.startsWith("image/")) { diff --git a/app/locales/cn.ts b/app/locales/cn.ts index 5d0c284283e..491774efe8b 100644 --- a/app/locales/cn.ts +++ b/app/locales/cn.ts @@ -62,6 +62,7 @@ const cn = { Prompt: "快捷指令", Masks: "所有面具", Clear: "清除聊天", + NewChat: "另起聊天", Settings: "对话设置", UploadImage: "上传图片", }, diff --git a/app/locales/en.ts b/app/locales/en.ts index 79a91d7ccd5..129ca95e58b 100644 --- a/app/locales/en.ts +++ b/app/locales/en.ts @@ -64,6 +64,7 @@ const en: LocaleType = { Prompt: "Prompts", Masks: "Masks", Clear: "Clear Context", + NewChat: "New Chat", Settings: "Settings", UploadImage: "Upload Images", },