diff --git a/electron.vite.config.ts b/electron.vite.config.ts index accab509..338fc001 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -7,8 +7,9 @@ export default defineConfig({ plugins: [externalizeDepsPlugin()], resolve: { alias: { + '@main': resolve('src/main'), '@types': resolve('src/renderer/src/types'), - '@main': resolve('src/main') + '@shared': resolve('packages/shared') } } }, @@ -16,14 +17,15 @@ export default defineConfig({ plugins: [externalizeDepsPlugin()] }, renderer: { + plugins: [react()], resolve: { alias: { - '@renderer': resolve('src/renderer/src') + '@renderer': resolve('src/renderer/src'), + '@shared': resolve('packages/shared') } }, - plugins: [react()], optimizeDeps: { - exclude: ['chunk-KNVOMWSO.js', 'chunk-2NJP6ETL.js'] + exclude: [] } } }) diff --git a/packages/shared/config/constant.ts b/packages/shared/config/constant.ts new file mode 100644 index 00000000..c5c19a3e --- /dev/null +++ b/packages/shared/config/constant.ts @@ -0,0 +1,112 @@ +export const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] +export const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'] +export const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac'] +export const documentExts = ['.pdf', '.docx', '.pptx', '.xlsx', '.odt', '.odp', '.ods'] +export const textExts = [ + '.txt', // 普通文本文件 + '.md', // Markdown 文件 + '.mdx', // Markdown 文件 + '.html', // HTML 文件 + '.htm', // HTML 文件的另一种扩展名 + '.xml', // XML 文件 + '.json', // JSON 文件 + '.yaml', // YAML 文件 + '.yml', // YAML 文件的另一种扩展名 + '.csv', // 逗号分隔值文件 + '.tsv', // 制表符分隔值文件 + '.ini', // 配置文件 + '.log', // 日志文件 + '.rtf', // 富文本格式文件 + '.tex', // LaTeX 文件 + '.srt', // 字幕文件 + '.xhtml', // XHTML 文件 + '.nfo', // 信息文件(主要用于场景发布) + '.conf', // 配置文件 + '.config', // 配置文件 + '.env', // 环境变量文件 + '.rst', // reStructuredText 文件 + '.php', // PHP 脚本文件,包含嵌入的 HTML + '.js', // JavaScript 文件(部分是文本,部分可能包含代码) + '.ts', // TypeScript 文件 + '.jsp', // JavaServer Pages 文件 + '.aspx', // ASP.NET 文件 + '.bat', // Windows 批处理文件 + '.sh', // Unix/Linux Shell 脚本文件 + '.py', // Python 脚本文件 + '.rb', // Ruby 脚本文件 + '.pl', // Perl 脚本文件 + '.sql', // SQL 脚本文件 + '.css', // Cascading Style Sheets 文件 + '.less', // Less CSS 预处理器文件 + '.scss', // Sass CSS 预处理器文件 + '.sass', // Sass 文件 + '.styl', // Stylus CSS 预处理器文件 + '.coffee', // CoffeeScript 文件 + '.ino', // Arduino 代码文件 + '.asm', // Assembly 语言文件 + '.go', // Go 语言文件 + '.scala', // Scala 语言文件 + '.swift', // Swift 语言文件 + '.kt', // Kotlin 语言文件 + '.rs', // Rust 语言文件 + '.lua', // Lua 语言文件 + '.groovy', // Groovy 语言文件 + '.dart', // Dart 语言文件 + '.hs', // Haskell 语言文件 + '.clj', // Clojure 语言文件 + '.cljs', // ClojureScript 语言文件 + '.elm', // Elm 语言文件 + '.erl', // Erlang 语言文件 + '.ex', // Elixir 语言文件 + '.exs', // Elixir 脚本文件 + '.pug', // Pug (formerly Jade) 模板文件 + '.haml', // Haml 模板文件 + '.slim', // Slim 模板文件 + '.tpl', // 模板文件(通用) + '.ejs', // Embedded JavaScript 模板文件 + '.hbs', // Handlebars 模板文件 + '.mustache', // Mustache 模板文件 + '.jade', // Jade 模板文件 (已重命名为 Pug) + '.twig', // Twig 模板文件 + '.blade', // Blade 模板文件 (Laravel) + '.vue', // Vue.js 单文件组件 + '.jsx', // React JSX 文件 + '.tsx', // React TSX 文件 + '.graphql', // GraphQL 查询语言文件 + '.gql', // GraphQL 查询语言文件 + '.proto', // Protocol Buffers 文件 + '.thrift', // Thrift 文件 + '.toml', // TOML 配置文件 + '.edn', // Clojure 数据表示文件 + '.cake', // CakePHP 配置文件 + '.ctp', // CakePHP 视图文件 + '.cfm', // ColdFusion 标记语言文件 + '.cfc', // ColdFusion 组件文件 + '.m', // Objective-C 源文件 + '.mm', // Objective-C++ 源文件 + '.gradle', // Gradle 构建文件 + '.groovy', // Gradle 构建文件 + '.kts', // Kotlin Script 文件 + '.java' // Java 代码文件 +] + +export const ZOOM_SHORTCUTS = [ + { + key: 'zoom_in', + shortcut: ['CommandOrControl', '='], + editable: false, + enabled: true + }, + { + key: 'zoom_out', + shortcut: ['CommandOrControl', '-'], + editable: false, + enabled: true + }, + { + key: 'zoom_reset', + shortcut: ['CommandOrControl', '0'], + editable: false, + enabled: true + } +] diff --git a/src/main/constant.ts b/src/main/constant.ts index 54bc4661..12b4d17a 100644 --- a/src/main/constant.ts +++ b/src/main/constant.ts @@ -1,95 +1,3 @@ export const isMac = process.platform === 'darwin' export const isWin = process.platform === 'win32' export const isLinux = process.platform === 'linux' - -export const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] -export const videoExts = ['.mp4', '.avi', '.mov', '.wmv', '.flv', '.mkv'] -export const audioExts = ['.mp3', '.wav', '.ogg', '.flac', '.aac'] -export const documentExts = ['.pdf', '.docx', '.pptx', '.xlsx', '.odt', '.odp', '.ods'] -export const textExts = [ - '.txt', // 普通文本文件 - '.md', // Markdown 文件 - '.mdx', // Markdown 文件 - '.html', // HTML 文件 - '.htm', // HTML 文件的另一种扩展名 - '.xml', // XML 文件 - '.json', // JSON 文件 - '.yaml', // YAML 文件 - '.yml', // YAML 文件的另一种扩展名 - '.csv', // 逗号分隔值文件 - '.tsv', // 制表符分隔值文件 - '.ini', // 配置文件 - '.log', // 日志文件 - '.rtf', // 富文本格式文件 - '.tex', // LaTeX 文件 - '.srt', // 字幕文件 - '.xhtml', // XHTML 文件 - '.nfo', // 信息文件(主要用于场景发布) - '.conf', // 配置文件 - '.config', // 配置文件 - '.env', // 环境变量文件 - '.rst', // reStructuredText 文件 - '.php', // PHP 脚本文件,包含嵌入的 HTML - '.js', // JavaScript 文件(部分是文本,部分可能包含代码) - '.ts', // TypeScript 文件 - '.jsp', // JavaServer Pages 文件 - '.aspx', // ASP.NET 文件 - '.bat', // Windows 批处理文件 - '.sh', // Unix/Linux Shell 脚本文件 - '.py', // Python 脚本文件 - '.rb', // Ruby 脚本文件 - '.pl', // Perl 脚本文件 - '.sql', // SQL 脚本文件 - '.css', // Cascading Style Sheets 文件 - '.less', // Less CSS 预处理器文件 - '.scss', // Sass CSS 预处理器文件 - '.sass', // Sass 文件 - '.styl', // Stylus CSS 预处理器文件 - '.coffee', // CoffeeScript 文件 - '.ino', // Arduino 代码文件 - '.asm', // Assembly 语言文件 - '.go', // Go 语言文件 - '.scala', // Scala 语言文件 - '.swift', // Swift 语言文件 - '.kt', // Kotlin 语言文件 - '.rs', // Rust 语言文件 - '.lua', // Lua 语言文件 - '.groovy', // Groovy 语言文件 - '.dart', // Dart 语言文件 - '.hs', // Haskell 语言文件 - '.clj', // Clojure 语言文件 - '.cljs', // ClojureScript 语言文件 - '.elm', // Elm 语言文件 - '.erl', // Erlang 语言文件 - '.ex', // Elixir 语言文件 - '.exs', // Elixir 脚本文件 - '.pug', // Pug (formerly Jade) 模板文件 - '.haml', // Haml 模板文件 - '.slim', // Slim 模板文件 - '.tpl', // 模板文件(通用) - '.ejs', // Embedded JavaScript 模板文件 - '.hbs', // Handlebars 模板文件 - '.mustache', // Mustache 模板文件 - '.jade', // Jade 模板文件 (已重命名为 Pug) - '.twig', // Twig 模板文件 - '.blade', // Blade 模板文件 (Laravel) - '.vue', // Vue.js 单文件组件 - '.jsx', // React JSX 文件 - '.tsx', // React TSX 文件 - '.graphql', // GraphQL 查询语言文件 - '.gql', // GraphQL 查询语言文件 - '.proto', // Protocol Buffers 文件 - '.thrift', // Thrift 文件 - '.toml', // TOML 配置文件 - '.edn', // Clojure 数据表示文件 - '.cake', // CakePHP 配置文件 - '.ctp', // CakePHP 视图文件 - '.cfm', // ColdFusion 标记语言文件 - '.cfc', // ColdFusion 组件文件 - '.m', // Objective-C 源文件 - '.mm', // Objective-C++ 源文件 - '.gradle', // Gradle 构建文件 - '.groovy', // Gradle 构建文件 - '.kts', // Kotlin Script 文件 - '.java' // Java 代码文件 -] diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 90303677..7703e5f5 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -138,6 +138,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { // shortcuts ipcMain.handle('shortcuts:update', (_, shortcuts: Shortcut[]) => { configManager.setShortcuts(shortcuts) + log.info('[ipc] shortcuts updated', shortcuts) // Refresh shortcuts registration if (mainWindow) { unregisterAllShortcuts() diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index bf185b16..65a06596 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -1,4 +1,5 @@ -import { LanguageVarious, ThemeMode } from '@types' +import { ZOOM_SHORTCUTS } from '@shared/config/constant' +import { LanguageVarious, Shortcut, ThemeMode } from '@types' import { app } from 'electron' import Store from 'electron-store' @@ -72,7 +73,7 @@ export class ConfigManager { } getShortcuts() { - return this.store.get('shortcuts') as Shortcut[] | undefined + return this.store.get('shortcuts', ZOOM_SHORTCUTS) as Shortcut[] | [] } setShortcuts(shortcuts: Shortcut[]) { diff --git a/src/main/services/FileStorage.ts b/src/main/services/FileStorage.ts index 00919d77..887a351d 100644 --- a/src/main/services/FileStorage.ts +++ b/src/main/services/FileStorage.ts @@ -1,5 +1,5 @@ -import { documentExts, imageExts } from '@main/constant' import { getFileType } from '@main/utils/file' +import { documentExts, imageExts } from '@shared/config/constant' import { FileType } from '@types' import * as crypto from 'crypto' import { diff --git a/src/main/services/ShortcutService.ts b/src/main/services/ShortcutService.ts index 1833032a..b05a3de9 100644 --- a/src/main/services/ShortcutService.ts +++ b/src/main/services/ShortcutService.ts @@ -1,6 +1,5 @@ import { Shortcut } from '@types' import { BrowserWindow, globalShortcut } from 'electron' -import Logger from 'electron-log' import { configManager } from './ConfigManager' @@ -9,9 +8,9 @@ let showAppAccelerator: string | null = null function getShortcutHandler(shortcut: Shortcut) { switch (shortcut.key) { case 'zoom_in': - return () => handleZoom(0.1) + return (window: BrowserWindow) => handleZoom(0.1)(window) case 'zoom_out': - return () => handleZoom(-0.1) + return (window: BrowserWindow) => handleZoom(-0.1)(window) case 'zoom_reset': return (window: BrowserWindow) => { window.webContents.setZoomFactor(1) @@ -46,7 +45,7 @@ function handleZoom(delta: number) { } } -function registerWindowShortcuts(window: BrowserWindow) { +export function registerShortcuts(window: BrowserWindow) { window.webContents.setZoomFactor(configManager.getZoomFactor()) const register = () => { @@ -56,10 +55,15 @@ function registerWindowShortcuts(window: BrowserWindow) { if (!shortcuts) return shortcuts.forEach((shortcut) => { - if (!shortcut.enabled || shortcut.shortcut.length === 0) return + if (!shortcut.enabled || shortcut.shortcut.length === 0) { + return + } const handler = getShortcutHandler(shortcut) - if (!handler) return + + if (!handler) { + return + } const accelerator = formatShortcutKey(shortcut.shortcut) @@ -67,7 +71,22 @@ function registerWindowShortcuts(window: BrowserWindow) { showAppAccelerator = accelerator } - Logger.info(`Register shortcut: ${accelerator}`) + if (shortcut.key.includes('zoom')) { + switch (shortcut.key) { + case 'zoom_in': + globalShortcut.register('CommandOrControl+=', () => handler(window)) + globalShortcut.register('CommandOrControl+numadd', () => handler(window)) + return + case 'zoom_out': + globalShortcut.register('CommandOrControl+-', () => handler(window)) + globalShortcut.register('CommandOrControl+numsub', () => handler(window)) + return + case 'zoom_reset': + globalShortcut.register('CommandOrControl+0', () => handler(window)) + return + } + } + globalShortcut.register(accelerator, () => handler(window)) }) } @@ -79,9 +98,7 @@ function registerWindowShortcuts(window: BrowserWindow) { if (showAppAccelerator) { const handler = getShortcutHandler({ key: 'show_app' } as Shortcut) - if (handler) { - globalShortcut.register(showAppAccelerator, () => handler(window)) - } + handler && globalShortcut.register(showAppAccelerator, () => handler(window)) } } @@ -93,10 +110,6 @@ function registerWindowShortcuts(window: BrowserWindow) { } } -export function registerShortcuts(mainWindow: BrowserWindow) { - registerWindowShortcuts(mainWindow) -} - export function unregisterAllShortcuts() { showAppAccelerator = null globalShortcut.unregisterAll() diff --git a/src/main/utils/file.ts b/src/main/utils/file.ts index fa8c0ef9..51971122 100644 --- a/src/main/utils/file.ts +++ b/src/main/utils/file.ts @@ -1,6 +1,5 @@ -import { audioExts, documentExts, imageExts, textExts, videoExts } from '@main/constant' - -import { FileTypes } from '../../renderer/src/types' +import { audioExts, documentExts, imageExts, textExts, videoExts } from '@shared/config/constant' +import { FileTypes } from '@types' export function getFileType(ext: string): FileTypes { ext = ext.toLowerCase() diff --git a/src/renderer/src/config/constant.ts b/src/renderer/src/config/constant.ts index 3bb9e07f..a6bbfbfe 100644 --- a/src/renderer/src/config/constant.ts +++ b/src/renderer/src/config/constant.ts @@ -3,97 +3,8 @@ export const DEFAULT_CONTEXTCOUNT = 5 export const DEFAULT_MAX_TOKENS = 4096 export const FONT_FAMILY = "Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif" + export const platform = window.electron?.process?.platform export const isMac = platform === 'darwin' export const isWindows = platform === 'win32' || platform === 'win64' export const isLinux = platform === 'linux' - -export const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'] -export const documentExts = ['.pdf', '.docx', '.pptx', '.xlsx', '.odt', '.odp', '.ods'] -export const textExts = [ - '.txt', // 普通文本文件 - '.md', // Markdown 文件 - '.mdx', // Markdown 文件 - '.html', // HTML 文件 - '.htm', // HTML 文件的另一种扩展名 - '.xml', // XML 文件 - '.json', // JSON 文件 - '.yaml', // YAML 文件 - '.yml', // YAML 文件的另一种扩展名 - '.csv', // 逗号分隔值文件 - '.tsv', // 制表符分隔值文件 - '.ini', // 配置文件 - '.log', // 日志文件 - '.rtf', // 富文本格式文件 - '.tex', // LaTeX 文件 - '.srt', // 字幕文件 - '.xhtml', // XHTML 文件 - '.nfo', // 信息文件(主要用于场景发布) - '.conf', // 配置文件 - '.config', // 配置文件 - '.env', // 环境变量文件 - '.rst', // reStructuredText 文件 - '.php', // PHP 脚本文件,包含嵌入的 HTML - '.js', // JavaScript 文件(部分是文本,部分可能包含代码) - '.ts', // TypeScript 文件 - '.jsp', // JavaServer Pages 文件 - '.aspx', // ASP.NET 文件 - '.bat', // Windows 批处理文件 - '.sh', // Unix/Linux Shell 脚本文件 - '.py', // Python 脚本文件 - '.rb', // Ruby 脚本文件 - '.pl', // Perl 脚本文件 - '.sql', // SQL 脚本文件 - '.css', // Cascading Style Sheets 文件 - '.less', // Less CSS 预处理器文件 - '.scss', // Sass CSS 预处理器文件 - '.sass', // Sass 文件 - '.styl', // Stylus CSS 预处理器文件 - '.coffee', // CoffeeScript 文件 - '.ino', // Arduino 代码文件 - '.asm', // Assembly 语言文件 - '.go', // Go 语言文件 - '.scala', // Scala 语言文件 - '.swift', // Swift 语言文件 - '.kt', // Kotlin 语言文件 - '.rs', // Rust 语言文件 - '.lua', // Lua 语言文件 - '.groovy', // Groovy 语言文件 - '.dart', // Dart 语言文件 - '.hs', // Haskell 语言文件 - '.clj', // Clojure 语言文件 - '.cljs', // ClojureScript 语言文件 - '.elm', // Elm 语言文件 - '.erl', // Erlang 语言文件 - '.ex', // Elixir 语言文件 - '.exs', // Elixir 脚本文件 - '.pug', // Pug (formerly Jade) 模板文件 - '.haml', // Haml 模板文件 - '.slim', // Slim 模板文件 - '.tpl', // 模板文件(通用) - '.ejs', // Embedded JavaScript 模板文件 - '.hbs', // Handlebars 模板文件 - '.mustache', // Mustache 模板文件 - '.jade', // Jade 模板文件 (已重命名为 Pug) - '.twig', // Twig 模板文件 - '.blade', // Blade 模板文件 (Laravel) - '.vue', // Vue.js 单文件组件 - '.jsx', // React JSX 文件 - '.tsx', // React TSX 文件 - '.graphql', // GraphQL 查询语言文件 - '.gql', // GraphQL 查询语言文件 - '.proto', // Protocol Buffers 文件 - '.thrift', // Thrift 文件 - '.toml', // TOML 配置文件 - '.edn', // Clojure 数据表示文件 - '.cake', // CakePHP 配置文件 - '.ctp', // CakePHP 视图文件 - '.cfm', // ColdFusion 标记语言文件 - '.cfc', // ColdFusion 组件文件 - '.m', // Objective-C 源文件 - '.mm', // Objective-C++ 源文件 - '.gradle', // Gradle 构建文件 - '.groovy', // Gradle 构建文件 - '.kts', // Kotlin Script 文件 - '.java' // Java 代码文件 -] diff --git a/src/renderer/src/hooks/useAppInit.ts b/src/renderer/src/hooks/useAppInit.ts index 3e111599..83e80150 100644 --- a/src/renderer/src/hooks/useAppInit.ts +++ b/src/renderer/src/hooks/useAppInit.ts @@ -1,9 +1,10 @@ -import { isMac } from '@renderer/config/constant' +import { isMac, isWindows } from '@renderer/config/constant' import { isLocalAi } from '@renderer/config/env' import db from '@renderer/databases' import i18n from '@renderer/i18n' import { useAppDispatch } from '@renderer/store' import { setAvatar, setFilesPath } from '@renderer/store/runtime' +import { updateShortcut } from '@renderer/store/shortcuts' import { runAsyncFunction } from '@renderer/utils' import { useLiveQuery } from 'dexie-react-hooks' import { useEffect } from 'react' @@ -11,6 +12,7 @@ import { useEffect } from 'react' import { useDefaultModel } from './useAssistant' import { useRuntime } from './useRuntime' import { useSettings } from './useSettings' +import { useShortcuts } from './useShortcuts' export function useAppInit() { const dispatch = useAppDispatch() @@ -18,6 +20,7 @@ export function useAppInit() { const { minappShow } = useRuntime() const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel() const avatar = useLiveQuery(() => db.settings.get('image://avatar')) + const { shortcuts } = useShortcuts() useEffect(() => { avatar?.value && dispatch(setAvatar(avatar.value)) @@ -69,4 +72,15 @@ export function useAppInit() { dispatch(setFilesPath(info.filesPath)) }) }, [dispatch]) + + useEffect(() => { + if (isWindows) { + shortcuts.forEach((shortcut) => { + if (shortcut.shortcut[0] === 'Command') { + shortcut.shortcut[0] = 'Ctrl' + dispatch(updateShortcut(shortcut)) + } + }) + } + }, [dispatch, shortcuts]) } diff --git a/src/renderer/src/hooks/useShortcuts.ts b/src/renderer/src/hooks/useShortcuts.ts index 754399b5..14283c3b 100644 --- a/src/renderer/src/hooks/useShortcuts.ts +++ b/src/renderer/src/hooks/useShortcuts.ts @@ -49,7 +49,12 @@ export const useShortcut = ( }, { enableOnFormTags: options.enableOnFormTags, - description: options.description || shortcutConfig?.name + description: options.description || shortcutConfig?.key } ) } + +export function useShortcuts() { + const shortcuts = useAppSelector((state) => state.shortcuts.shortcuts) + return { shortcuts } +} diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 7f479a75..d37f7313 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -458,7 +458,9 @@ "reset_defaults": "Reset Defaults", "reset_defaults_confirm": "Are you sure you want to reset all shortcuts?", "press_shortcut": "Press Shortcut", - "alt_warning": "Mac does not support the Alt key" + "alt_warning": "Mac does not support the Alt key", + "reset_to_default": "Reset to Default", + "clear_shortcut": "Clear Shortcut" }, "theme.auto": "Auto", "theme.dark": "Dark", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 07d2741c..a371beb0 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -458,7 +458,9 @@ "reset_defaults": "Сбросить настройки по умолчанию", "reset_defaults_confirm": "Вы уверены, что хотите сбросить все горячие клавиши?", "press_shortcut": "Нажмите сочетание клавиш", - "alt_warning": "Mac не поддерживает Alt" + "alt_warning": "Mac не поддерживает Alt", + "reset_to_default": "Сбросить настройки по умолчанию", + "clear_shortcut": "Очистить сочетание клавиш" }, "theme.auto": "Автоматически", "theme.dark": "Темная", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 5f15c1f7..e6108f63 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -446,7 +446,9 @@ "reset_defaults": "重置默认快捷键", "reset_defaults_confirm": "确定要重置所有快捷键吗?", "press_shortcut": "按下快捷键", - "alt_warning": "Mac 系统不支持 Alt 键" + "alt_warning": "Mac 系统不支持 Alt 键", + "reset_to_default": "重置为默认", + "clear_shortcut": "清除快捷键" }, "theme.auto": "跟随系统", "theme.dark": "深色主题", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 19f6c530..ea8518ac 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -446,7 +446,9 @@ "reset_defaults": "重置預設快捷鍵", "reset_defaults_confirm": "確定要重置所有快捷鍵嗎?", "press_shortcut": "按下快捷鍵", - "alt_warning": "Mac 系統不支持 Alt 鍵" + "alt_warning": "Mac 系統不支持 Alt 鍵", + "reset_to_default": "重置為預設", + "clear_shortcut": "清除快捷鍵" }, "theme.auto": "自動", "theme.dark": "深色主題", diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx index e31ef1e0..d1dc0cdc 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentButton.tsx @@ -1,7 +1,7 @@ import { PaperClipOutlined } from '@ant-design/icons' -import { documentExts, imageExts, textExts } from '@renderer/config/constant' import { isVisionModel } from '@renderer/config/models' import { FileType, Model } from '@renderer/types' +import { documentExts, imageExts, textExts } from '@shared/config/constant' import { Tooltip } from 'antd' import { FC } from 'react' import { useTranslation } from 'react-i18next' diff --git a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx index d68fa0fb..44db8982 100644 --- a/src/renderer/src/pages/home/Inputbar/Inputbar.tsx +++ b/src/renderer/src/pages/home/Inputbar/Inputbar.tsx @@ -9,7 +9,7 @@ import { } from '@ant-design/icons' import { PicCenterOutlined } from '@ant-design/icons' import TranslateButton from '@renderer/components/TranslateButton' -import { documentExts, imageExts, isMac, textExts } from '@renderer/config/constant' +import { isMac } from '@renderer/config/constant' import { isVisionModel } from '@renderer/config/models' import db from '@renderer/databases' import { useAssistant } from '@renderer/hooks/useAssistant' @@ -26,6 +26,7 @@ import store, { useAppDispatch, useAppSelector } from '@renderer/store' import { setGenerating, setSearching } from '@renderer/store/runtime' import { Assistant, FileType, Message, Topic } from '@renderer/types' import { delay, getFileExtension, uuid } from '@renderer/utils' +import { documentExts, imageExts, textExts } from '@shared/config/constant' import { Button, Popconfirm, Tooltip } from 'antd' import TextArea, { TextAreaRef } from 'antd/es/input/TextArea' import dayjs from 'dayjs' diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index 89089ee3..72c7827f 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -11,7 +11,6 @@ import { } from '@ant-design/icons' import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup' import TextEditPopup from '@renderer/components/Popups/TextEditPopup' -import { useDefaultModel } from '@renderer/hooks/useAssistant' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { translateText } from '@renderer/services/TranslateService' import { Message, Model } from '@renderer/types' @@ -37,7 +36,6 @@ const MessageMenubar: FC = (props) => { const { message, index, model, isLastMessage, isAssistantMessage, setModel, onEditMessage, onDeleteMessage } = props const { t } = useTranslation() const [copied, setCopied] = useState(false) - const { translateModel } = useDefaultModel() const [isTranslating, setIsTranslating] = useState(false) const isUserMessage = message.role === 'user' diff --git a/src/renderer/src/pages/paintings/PaintingsPage.tsx b/src/renderer/src/pages/paintings/PaintingsPage.tsx index 5373d283..d25905ff 100644 --- a/src/renderer/src/pages/paintings/PaintingsPage.tsx +++ b/src/renderer/src/pages/paintings/PaintingsPage.tsx @@ -490,8 +490,4 @@ const InfoIcon = styled(QuestionCircleOutlined)` } ` -const RefreshIcon = styled(RedoOutlined)` - cursor: pointer; -` - export default PaintingsPage diff --git a/src/renderer/src/pages/settings/ShortcutSettings.tsx b/src/renderer/src/pages/settings/ShortcutSettings.tsx index 1a6a6d84..280d5a5f 100644 --- a/src/renderer/src/pages/settings/ShortcutSettings.tsx +++ b/src/renderer/src/pages/settings/ShortcutSettings.tsx @@ -1,10 +1,11 @@ -import { UndoOutlined } from '@ant-design/icons' +import { ClearOutlined, UndoOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' import { isMac } from '@renderer/config/constant' import { useTheme } from '@renderer/context/ThemeProvider' import { useAppDispatch, useAppSelector } from '@renderer/store' -import { initialState, resetShortcuts, updateShortcut } from '@renderer/store/shortcuts' -import { Button, Input, InputRef, Table as AntTable } from 'antd' +import { initialState, resetShortcuts, toggleShortcut, updateShortcut } from '@renderer/store/shortcuts' +import { Shortcut } from '@renderer/types' +import { Button, Input, InputRef, Switch, Table as AntTable, Tooltip } from 'antd' import type { ColumnsType } from 'antd/es/table' import { FC, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -12,13 +13,6 @@ import styled from 'styled-components' import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.' -interface ShortcutItem { - key: string - name: string - shortcut: string[] - enabled: boolean -} - const ShortcutSettings: FC = () => { const { t } = useTranslation() const { theme } = useTheme() @@ -27,7 +21,7 @@ const ShortcutSettings: FC = () => { const inputRefs = useRef>({}) const [editingKey, setEditingKey] = useState(null) - const handleClear = (record: ShortcutItem) => { + const handleClear = (record: Shortcut) => { dispatch( updateShortcut({ ...record, @@ -36,19 +30,19 @@ const ShortcutSettings: FC = () => { ) } - const handleAddShortcut = (record: ShortcutItem) => { + const handleAddShortcut = (record: Shortcut) => { setEditingKey(record.key) setTimeout(() => { inputRefs.current[record.key]?.focus() }, 0) } - const isShortcutModified = (record: ShortcutItem) => { + const isShortcutModified = (record: Shortcut) => { const defaultShortcut = initialState.shortcuts.find((s) => s.key === record.key) return defaultShortcut?.shortcut.join('+') !== record.shortcut.join('+') } - const handleResetShortcut = (record: ShortcutItem) => { + const handleResetShortcut = (record: Shortcut) => { const defaultShortcut = initialState.shortcuts.find((s) => s.key === record.key) if (defaultShortcut) { dispatch( @@ -95,6 +89,8 @@ const ShortcutSettings: FC = () => { return isMac ? '⌥' : 'Alt' case 'Shift': return isMac ? '⇧' : 'Shift' + case 'CommandOrControl': + return isMac ? '⌘' : 'Ctrl' case ' ': return 'Space' default: @@ -104,7 +100,8 @@ const ShortcutSettings: FC = () => { .join(' + ') } - const handleKeyDown = (e: React.KeyboardEvent, record: ShortcutItem) => { + const handleKeyDown = (e: React.KeyboardEvent, record: Shortcut) => { + console.debug('handleKeyDown', e, record) e.preventDefault() const keys: string[] = [] @@ -115,6 +112,8 @@ const ShortcutSettings: FC = () => { const key = e.key + console.debug('key', key) + if (!['Control', 'Alt', 'Shift', 'Meta'].includes(key)) { keys.push(key.toUpperCase()) } @@ -144,7 +143,7 @@ const ShortcutSettings: FC = () => { }) } - const columns: ColumnsType = [ + const columns: ColumnsType = [ { title: t('settings.shortcuts.action'), dataIndex: 'name', @@ -155,14 +154,16 @@ const ShortcutSettings: FC = () => { dataIndex: 'shortcut', key: 'shortcut', align: 'right', - render: (shortcut: string[], record: ShortcutItem) => { + render: (shortcut: string[], record: Shortcut) => { const isEditing = editingKey === record.key + const shortcutConfig = shortcuts.find((s) => s.key === record.key) + const isEditable = shortcutConfig?.editable !== false return ( -
-
+ + {isEditing ? ( - el && (inputRefs.current[record.key] = el)} value={formatShortcut(shortcut)} placeholder={t('settings.shortcuts.press_shortcut')} @@ -173,39 +174,51 @@ const ShortcutSettings: FC = () => { setEditingKey(null) } }} - style={{ width: '120px' }} - suffix={ - isShortcutModified(record) && ( - { - handleResetShortcut(record) - setEditingKey(null) - }} - /> - ) - } /> ) : ( -
handleAddShortcut(record)}> + isEditable && handleAddShortcut(record)}> {shortcut.length > 0 ? formatShortcut(shortcut) : t('settings.shortcuts.press_shortcut')} -
+ )} -
- -
+ + ) } + }, + { + title: t('settings.shortcuts.actions'), + key: 'actions', + align: 'right', + width: '70px', + render: (record: Shortcut) => ( + + +