diff --git a/app/components/chat.tsx b/app/components/chat.tsx
index bcd0e605df2..2594f9fd7e2 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,9 @@ 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";
+import { listen } from "@tauri-apps/api/event";
const Markdown = dynamic(async () => (await import("./markdown")).Markdown, {
loading: () => ,
@@ -555,6 +559,15 @@ export function ChatActions(props: {
icon={}
/>
+ }
+ onClick={() => {
+ chatStore.newSession(chatStore.currentSession().mask);
+ navigate(Path.Chat);
+ }}
+ />
+
{showModelSelector && (
{
+ const unlisten = listen("activate_input_field", () => {
+ inputRef.current?.focus();
+ });
+
+ return () => {
+ unlisten.then((f) => f());
+ };
+ }, []);
+
// chat commands shortcuts
const chatCommands = useChatCommand({
new: () => chatStore.newSession(),
@@ -1100,11 +1123,13 @@ function _Chat() {
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
-
+
const handlePaste = useCallback(
async (event: React.ClipboardEvent) => {
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/components/home.tsx b/app/components/home.tsx
index 8386ba144b9..cc2c6b442e4 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,12 @@ export function useLoadData() {
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+
+ 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 ba6d9345e24..6dc0ac466d7 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,13 @@ function SyncItems() {
>
);
}
+async function useUpdateShortcut(newShortcut: string) {
+ useEffect(() => {
+ (async () => {
+ await invoke("update_shortcut", { shortcut: newShortcut });
+ })();
+ }, []);
+}
export function Settings() {
const navigate = useNavigate();
@@ -650,6 +658,8 @@ export function Settings() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ useUpdateShortcut(config.shortcutQuickChat);
+
const clientConfig = useMemo(() => getClientConfig(), []);
const showAccessCode = enabledAccessControl && !clientConfig?.isApp;
@@ -827,6 +837,22 @@ export function Settings() {
}
>
+
+
+ {
+ updateConfig(
+ (config) =>
+ (config.shortcutQuickChat = e.currentTarget.value),
+ );
+ invoke("update_shortcut", {
+ shortcut: config.shortcutQuickChat,
+ });
+ }}
+ >
+
diff --git a/app/locales/cn.ts b/app/locales/cn.ts
index 5d0c284283e..3a6ae5315c4 100644
--- a/app/locales/cn.ts
+++ b/app/locales/cn.ts
@@ -62,6 +62,7 @@ const cn = {
Prompt: "快捷指令",
Masks: "所有面具",
Clear: "清除聊天",
+ NewChat: "另起聊天",
Settings: "对话设置",
UploadImage: "上传图片",
},
@@ -176,6 +177,7 @@ const cn = {
Title: "预览气泡",
SubTitle: "在预览气泡中预览 Markdown 内容",
},
+ ShortcutQuickChat: "快捷键-快速对话",
AutoGenerateTitle: {
Title: "自动生成标题",
SubTitle: "根据对话内容生成合适的标题",
diff --git a/app/locales/en.ts b/app/locales/en.ts
index 79a91d7ccd5..4e320891437 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",
},
@@ -178,6 +179,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/package.json b/package.json
index c92e0a08459..518d1091e74 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",
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index e0892590223..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 = [
+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 ed3ec32f37b..20dff2179b0 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -1,9 +1,34 @@
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+use tauri::{Manager,CustomMenuItem, SystemTray, SystemTrayEvent, SystemTrayMenu};
+mod shortcuts;
fn main() {
- tauri::Builder::default()
- .plugin(tauri_plugin_window_state::Builder::default().build())
- .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![shortcuts::update_shortcut])
+ .system_tray(system_tray)
+ .on_system_tray_event(|app, event| match event {
+ SystemTrayEvent::LeftClick {..} => {
+ 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/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
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index f03efb0fe49..648a949e959 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
@@ -52,6 +55,10 @@
"all": true
}
},
+ "systemTray": {
+ "iconPath": "../public/favicon-16x16.png",
+ "iconAsTemplate": true
+ },
"bundle": {
"active": true,
"category": "DeveloperTool",