From 9c55b4516c2cc04776dfecf7bf2c9645eeeb84ac Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Sun, 19 Jan 2025 19:22:10 +0800 Subject: [PATCH] feat: add a startup switch for quick assistant --- src/main/ipc.ts | 9 ++++ src/main/services/ConfigManager.ts | 8 +++ src/main/services/TrayService.ts | 27 ++++++++-- src/main/services/WindowService.ts | 28 +++++++---- src/preload/index.d.ts | 7 +++ src/preload/index.ts | 7 +++ src/renderer/src/i18n/locales/en-us.json | 4 +- src/renderer/src/i18n/locales/ja-jp.json | 4 +- src/renderer/src/i18n/locales/ru-ru.json | 4 +- src/renderer/src/i18n/locales/zh-cn.json | 4 +- src/renderer/src/i18n/locales/zh-tw.json | 4 +- .../pages/settings/QuickAssistantSettings.tsx | 50 ++++++++++++++++--- src/renderer/src/store/migrate.ts | 2 + src/renderer/src/store/settings.ts | 8 ++- .../src/windows/mini/home/HomeWindow.tsx | 4 +- 15 files changed, 137 insertions(+), 33 deletions(-) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 148ec97f..cc203cc0 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -14,6 +14,7 @@ import FileStorage from './services/FileStorage' import { GeminiService } from './services/GeminiService' import KnowledgeService from './services/KnowledgeService' import { registerShortcuts, unregisterAllShortcuts } from './services/ShortcutService' +import { TrayService } from './services/TrayService' import { windowService } from './services/WindowService' import { compress, decompress } from './utils/zip' @@ -52,6 +53,8 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { configManager.setTray(isActive) }) + ipcMain.handle('app:restart-tray', () => TrayService.getInstance().restartTray()) + ipcMain.handle('config:set', (_, key: string, value: any) => { configManager.set(key, value) }) @@ -184,4 +187,10 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ipcMain.handle('gemini:retrieve-file', GeminiService.retrieveFile) ipcMain.handle('gemini:list-files', GeminiService.listFiles) ipcMain.handle('gemini:delete-file', GeminiService.deleteFile) + + // mini window + ipcMain.handle('miniwindow:show', () => windowService.showMiniWindow()) + ipcMain.handle('miniwindow:hide', () => windowService.hideMiniWindow()) + ipcMain.handle('miniwindow:close', () => windowService.closeMiniWindow()) + ipcMain.handle('miniwindow:toggle', () => windowService.toggleMiniWindow()) } diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index aa93f124..719d089e 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -92,6 +92,14 @@ export class ConfigManager { this.store.set('clickTrayToShowQuickAssistant', value) } + getEnableQuickAssistant(): boolean { + return this.store.get('enableQuickAssistant', false) as boolean + } + + setEnableQuickAssistant(value: boolean) { + this.store.set('enableQuickAssistant', value) + } + set(key: string, value: any) { this.store.set(key, value) } diff --git a/src/main/services/TrayService.ts b/src/main/services/TrayService.ts index 9c16f147..0d8d58ba 100644 --- a/src/main/services/TrayService.ts +++ b/src/main/services/TrayService.ts @@ -1,6 +1,6 @@ import { isMac } from '@main/constant' import { locales } from '@main/utils/locales' -import { app, Menu, nativeImage, nativeTheme, Tray } from 'electron' +import { app, Menu, MenuItemConstructorOptions, nativeImage, nativeTheme, Tray } from 'electron' import icon from '../../../build/tray_icon.png?asset' import iconDark from '../../../build/tray_icon_dark.png?asset' @@ -9,11 +9,17 @@ import { configManager } from './ConfigManager' import { windowService } from './WindowService' export class TrayService { + private static instance: TrayService private tray: Tray | null = null constructor() { this.updateTray() this.watchTrayChanges() + TrayService.instance = this + } + + public static getInstance() { + return TrayService.instance } private createTray() { @@ -40,12 +46,14 @@ export class TrayService { const locale = locales[configManager.getLanguage()] const { tray: trayLocale } = locale.translation - const contextMenu = Menu.buildFromTemplate([ + const enableQuickAssistant = configManager.getEnableQuickAssistant() + + const template = [ { label: trayLocale.show_window, click: () => windowService.showMainWindow() }, - { + enableQuickAssistant && { label: trayLocale.show_mini_window, click: () => windowService.showMiniWindow() }, @@ -54,7 +62,9 @@ export class TrayService { label: trayLocale.quit, click: () => this.quit() } - ]) + ].filter(Boolean) as MenuItemConstructorOptions[] + + const contextMenu = Menu.buildFromTemplate(template) if (process.platform === 'linux') { this.tray.setContextMenu(contextMenu) @@ -67,7 +77,7 @@ export class TrayService { }) this.tray.on('click', () => { - if (configManager.getClickTrayToShowQuickAssistant()) { + if (enableQuickAssistant && configManager.getClickTrayToShowQuickAssistant()) { windowService.showMiniWindow() } else { windowService.showMainWindow() @@ -84,6 +94,13 @@ export class TrayService { } } + public restartTray() { + if (configManager.getTray()) { + this.destroyTray() + this.createTray() + } + } + private destroyTray() { if (this.tray) { this.tray.destroy() diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index 5b155214..09430290 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -67,8 +67,6 @@ export class WindowService { this.setupMainWindow(this.mainWindow, mainWindowState) - setTimeout(() => this.showMiniWindow(), 5000) - return this.mainWindow } @@ -241,6 +239,12 @@ export class WindowService { } public showMiniWindow() { + const enableQuickAssistant = configManager.getEnableQuickAssistant() + + if (!enableQuickAssistant) { + return + } + if (this.selectionMenuWindow) { this.selectionMenuWindow.hide() } @@ -260,7 +264,7 @@ export class WindowService { this.miniWindow = new BrowserWindow({ width: 500, height: 520, - show: false, + show: true, autoHideMenuBar: true, transparent: isMac, vibrancy: 'under-window', @@ -282,14 +286,6 @@ export class WindowService { this.miniWindow?.hide() }) - this.miniWindow.on('close', (event) => { - if (this.isQuitting) { - return - } - event.preventDefault() - this.miniWindow?.hide() - }) - this.miniWindow.on('closed', () => { this.miniWindow = null }) @@ -313,9 +309,19 @@ export class WindowService { } } + public hideMiniWindow() { + this.miniWindow?.hide() + } + + public closeMiniWindow() { + this.miniWindow?.close() + } + public toggleMiniWindow() { if (this.miniWindow) { this.miniWindow.isVisible() ? this.miniWindow.hide() : this.miniWindow.show() + } else { + this.showMiniWindow() } } diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 032f583d..efc7eeda 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -18,6 +18,7 @@ declare global { setProxy: (proxy: string | undefined) => void setLanguage: (theme: LanguageVarious) => void setTray: (isActive: boolean) => void + restartTray: () => void setTheme: (theme: 'light' | 'dark') => void minApp: (options: { url: string; windowOptions?: Electron.BrowserWindowConstructorOptions }) => void reload: () => void @@ -96,6 +97,12 @@ declare global { set: (key: string, value: any) => Promise get: (key: string) => Promise } + miniWindow: { + show: () => Promise + hide: () => Promise + close: () => Promise + toggle: () => Promise + } } } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 593b21cb..ce0801f5 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -10,6 +10,7 @@ const api = { checkForUpdate: () => ipcRenderer.invoke('app:check-for-update'), setLanguage: (lang: string) => ipcRenderer.invoke('app:set-language', lang), setTray: (isActive: boolean) => ipcRenderer.invoke('app:set-tray', isActive), + restartTray: () => ipcRenderer.invoke('app:restart-tray'), setTheme: (theme: 'light' | 'dark') => ipcRenderer.invoke('app:set-theme', theme), openWebsite: (url: string) => ipcRenderer.invoke('open:website', url), minApp: (url: string) => ipcRenderer.invoke('minapp', url), @@ -89,6 +90,12 @@ const api = { config: { set: (key: string, value: any) => ipcRenderer.invoke('config:set', key, value), get: (key: string) => ipcRenderer.invoke('config:get', key) + }, + miniWindow: { + show: () => ipcRenderer.invoke('miniwindow:show'), + hide: () => ipcRenderer.invoke('miniwindow:hide'), + close: () => ipcRenderer.invoke('miniwindow:close'), + toggle: () => ipcRenderer.invoke('miniwindow:toggle') } } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index fd8c9e0c..fbafae0d 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -387,7 +387,9 @@ }, "quickAssistant": { "title": "Quick Assistant", - "click_tray_to_show": "Click the system tray icon to open" + "click_tray_to_show": "Click the tray icon to start", + "enable_quick_assistant": "Enable Quick Assistant", + "use_shortcut_to_show": "Right-click the tray icon or use shortcuts to start" }, "display.title": "Display Settings", "font_size.title": "Message font size", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 9465dac3..ff610e25 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -385,7 +385,9 @@ }, "quickAssistant": { "title": "クイックアシスタント", - "click_tray_to_show": "システムトレイアイコンをクリックして開く" + "click_tray_to_show": "トレイアイコンをクリックして起動", + "enable_quick_assistant": "クイックアシスタントを有効にする", + "use_shortcut_to_show": "トレイアイコンを右クリックするか、ショートカットキーで起動できます" }, "display.title": "表示設定", "font_size.title": "メッセージのフォントサイズ", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 3e3bde58..cef2c336 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -387,7 +387,9 @@ }, "quickAssistant": { "title": "Быстрый помощник", - "click_tray_to_show": "Нажмите на иконку системного трея для открытия" + "click_tray_to_show": "Нажмите на иконку трея для запуска", + "enable_quick_assistant": "Включить быстрый помощник", + "use_shortcut_to_show": "Нажмите на иконку трея или используйте горячие клавиши для запуска" }, "display.title": "Настройки отображения", "font_size.title": "Размер шрифта сообщений", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 46acc537..f6776406 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -388,7 +388,9 @@ }, "quickAssistant": { "title": "快捷助手", - "click_tray_to_show": "点击系统托盘图标打开" + "click_tray_to_show": "点击托盘图标启动", + "enable_quick_assistant": "启用快捷助手", + "use_shortcut_to_show": "右键点击托盘图标或使用快捷键启动" }, "display.title": "显示设置", "font_size.title": "消息字体大小", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index c851c6ca..4f0d53d9 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -387,7 +387,9 @@ }, "quickAssistant": { "title": "快捷助手", - "click_tray_to_show": "點擊系統托盤圖標打開" + "click_tray_to_show": "點擊托盤圖標啟動", + "enable_quick_assistant": "啟用快捷助手", + "use_shortcut_to_show": "右鍵點擊托盤圖標或使用快捷鍵啟動" }, "display.title": "顯示設定", "font_size.title": "訊息字體大小", diff --git a/src/renderer/src/pages/settings/QuickAssistantSettings.tsx b/src/renderer/src/pages/settings/QuickAssistantSettings.tsx index 7e8b3b62..85ff8b01 100644 --- a/src/renderer/src/pages/settings/QuickAssistantSettings.tsx +++ b/src/renderer/src/pages/settings/QuickAssistantSettings.tsx @@ -1,9 +1,10 @@ +import { InfoCircleOutlined } from '@ant-design/icons' import { useTheme } from '@renderer/context/ThemeProvider' import { useSettings } from '@renderer/hooks/useSettings' import { useAppDispatch } from '@renderer/store' -import { setClickTrayToShowQuickAssistant } from '@renderer/store/settings' +import { setClickTrayToShowQuickAssistant, setEnableQuickAssistant } from '@renderer/store/settings' import HomeWindow from '@renderer/windows/mini/home/HomeWindow' -import { Switch } from 'antd' +import { Switch, Tooltip } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -13,9 +14,26 @@ import { SettingContainer, SettingDivider, SettingGroup, SettingRow, SettingRowT const QuickAssistantSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() - const { clickTrayToShowQuickAssistant, setTray } = useSettings() + const { enableQuickAssistant, clickTrayToShowQuickAssistant, setTray } = useSettings() const dispatch = useAppDispatch() + const handleEnableQuickAssistant = async (enable: boolean) => { + dispatch(setEnableQuickAssistant(enable)) + await window.api.config.set('enableQuickAssistant', enable) + window.api.restartTray() + const disable = !enable + disable && window.api.miniWindow.close() + + if (enable && !clickTrayToShowQuickAssistant) { + window.message.info({ + content: t('settings.quickAssistant.use_shortcut_to_show'), + duration: 4, + icon: , + key: 'quick-assistant-info' + }) + } + } + const handleClickTrayToShowQuickAssistant = async (checked: boolean) => { dispatch(setClickTrayToShowQuickAssistant(checked)) await window.api.config.set('clickTrayToShowQuickAssistant', checked) @@ -31,13 +49,29 @@ const QuickAssistantSettings: FC = () => { {t('settings.quickAssistant.title')} - {t('settings.quickAssistant.click_tray_to_show')} - + + {t('settings.quickAssistant.enable_quick_assistant')} + + + + + + {enableQuickAssistant && ( + <> + + + {t('settings.quickAssistant.click_tray_to_show')} + + + + )} - {}}> - - + {enableQuickAssistant && ( + + + + )} ) } diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 35735f40..a2d0c56a 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -846,6 +846,8 @@ const migrateConfig = { } }) + state.settings.enableQuickAssistant = false + return state } } diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 4e04599d..2af74530 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -61,6 +61,7 @@ export interface SettingsState { disabled: SidebarIcon[] } narrowMode: boolean + enableQuickAssistant: boolean clickTrayToShowQuickAssistant: boolean } @@ -107,6 +108,7 @@ const initialState: SettingsState = { disabled: [] }, narrowMode: false, + enableQuickAssistant: false, clickTrayToShowQuickAssistant: false } @@ -245,6 +247,9 @@ const settingsSlice = createSlice({ }, setClickTrayToShowQuickAssistant: (state, action: PayloadAction) => { state.clickTrayToShowQuickAssistant = action.payload + }, + setEnableQuickAssistant: (state, action: PayloadAction) => { + state.enableQuickAssistant = action.payload } } }) @@ -291,7 +296,8 @@ export const { setTopicNamingPrompt, setSidebarIcons, setNarrowMode, - setClickTrayToShowQuickAssistant + setClickTrayToShowQuickAssistant, + setEnableQuickAssistant } = settingsSlice.actions export default settingsSlice.reducer diff --git a/src/renderer/src/windows/mini/home/HomeWindow.tsx b/src/renderer/src/windows/mini/home/HomeWindow.tsx index 081534cf..68904cee 100644 --- a/src/renderer/src/windows/mini/home/HomeWindow.tsx +++ b/src/renderer/src/windows/mini/home/HomeWindow.tsx @@ -34,8 +34,6 @@ const HomeWindow: FC = () => { textRef.current = `${referenceText}\n\n${text}` - const isMiniWindow = window.location.hash === '#/mini' - const onReadClipboard = useCallback(async () => { const text = await navigator.clipboard.readText() setClipboardText(text.trim()) @@ -49,7 +47,7 @@ const HomeWindow: FC = () => { i18n.changeLanguage(language || navigator.language || 'en-US') }, [language]) - const onCloseWindow = () => isMiniWindow && window.close() + const onCloseWindow = () => window.api.miniWindow.hide() const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Escape') {