refactor: shortcuts feature
This commit is contained in:
parent
cd3c053f81
commit
7f2f3ad88a
@ -7,8 +7,9 @@ export default defineConfig({
|
|||||||
plugins: [externalizeDepsPlugin()],
|
plugins: [externalizeDepsPlugin()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
'@main': resolve('src/main'),
|
||||||
'@types': resolve('src/renderer/src/types'),
|
'@types': resolve('src/renderer/src/types'),
|
||||||
'@main': resolve('src/main')
|
'@shared': resolve('packages/shared')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -16,14 +17,15 @@ export default defineConfig({
|
|||||||
plugins: [externalizeDepsPlugin()]
|
plugins: [externalizeDepsPlugin()]
|
||||||
},
|
},
|
||||||
renderer: {
|
renderer: {
|
||||||
|
plugins: [react()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@renderer': resolve('src/renderer/src')
|
'@renderer': resolve('src/renderer/src'),
|
||||||
|
'@shared': resolve('packages/shared')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [react()],
|
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
exclude: ['chunk-KNVOMWSO.js', 'chunk-2NJP6ETL.js']
|
exclude: []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
112
packages/shared/config/constant.ts
Normal file
112
packages/shared/config/constant.ts
Normal file
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -1,95 +1,3 @@
|
|||||||
export const isMac = process.platform === 'darwin'
|
export const isMac = process.platform === 'darwin'
|
||||||
export const isWin = process.platform === 'win32'
|
export const isWin = process.platform === 'win32'
|
||||||
export const isLinux = process.platform === 'linux'
|
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 代码文件
|
|
||||||
]
|
|
||||||
|
|||||||
@ -138,6 +138,7 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
|
|||||||
// shortcuts
|
// shortcuts
|
||||||
ipcMain.handle('shortcuts:update', (_, shortcuts: Shortcut[]) => {
|
ipcMain.handle('shortcuts:update', (_, shortcuts: Shortcut[]) => {
|
||||||
configManager.setShortcuts(shortcuts)
|
configManager.setShortcuts(shortcuts)
|
||||||
|
log.info('[ipc] shortcuts updated', shortcuts)
|
||||||
// Refresh shortcuts registration
|
// Refresh shortcuts registration
|
||||||
if (mainWindow) {
|
if (mainWindow) {
|
||||||
unregisterAllShortcuts()
|
unregisterAllShortcuts()
|
||||||
|
|||||||
@ -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 { app } from 'electron'
|
||||||
import Store from 'electron-store'
|
import Store from 'electron-store'
|
||||||
|
|
||||||
@ -72,7 +73,7 @@ export class ConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getShortcuts() {
|
getShortcuts() {
|
||||||
return this.store.get('shortcuts') as Shortcut[] | undefined
|
return this.store.get('shortcuts', ZOOM_SHORTCUTS) as Shortcut[] | []
|
||||||
}
|
}
|
||||||
|
|
||||||
setShortcuts(shortcuts: Shortcut[]) {
|
setShortcuts(shortcuts: Shortcut[]) {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { documentExts, imageExts } from '@main/constant'
|
|
||||||
import { getFileType } from '@main/utils/file'
|
import { getFileType } from '@main/utils/file'
|
||||||
|
import { documentExts, imageExts } from '@shared/config/constant'
|
||||||
import { FileType } from '@types'
|
import { FileType } from '@types'
|
||||||
import * as crypto from 'crypto'
|
import * as crypto from 'crypto'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { Shortcut } from '@types'
|
import { Shortcut } from '@types'
|
||||||
import { BrowserWindow, globalShortcut } from 'electron'
|
import { BrowserWindow, globalShortcut } from 'electron'
|
||||||
import Logger from 'electron-log'
|
|
||||||
|
|
||||||
import { configManager } from './ConfigManager'
|
import { configManager } from './ConfigManager'
|
||||||
|
|
||||||
@ -9,9 +8,9 @@ let showAppAccelerator: string | null = null
|
|||||||
function getShortcutHandler(shortcut: Shortcut) {
|
function getShortcutHandler(shortcut: Shortcut) {
|
||||||
switch (shortcut.key) {
|
switch (shortcut.key) {
|
||||||
case 'zoom_in':
|
case 'zoom_in':
|
||||||
return () => handleZoom(0.1)
|
return (window: BrowserWindow) => handleZoom(0.1)(window)
|
||||||
case 'zoom_out':
|
case 'zoom_out':
|
||||||
return () => handleZoom(-0.1)
|
return (window: BrowserWindow) => handleZoom(-0.1)(window)
|
||||||
case 'zoom_reset':
|
case 'zoom_reset':
|
||||||
return (window: BrowserWindow) => {
|
return (window: BrowserWindow) => {
|
||||||
window.webContents.setZoomFactor(1)
|
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())
|
window.webContents.setZoomFactor(configManager.getZoomFactor())
|
||||||
|
|
||||||
const register = () => {
|
const register = () => {
|
||||||
@ -56,10 +55,15 @@ function registerWindowShortcuts(window: BrowserWindow) {
|
|||||||
if (!shortcuts) return
|
if (!shortcuts) return
|
||||||
|
|
||||||
shortcuts.forEach((shortcut) => {
|
shortcuts.forEach((shortcut) => {
|
||||||
if (!shortcut.enabled || shortcut.shortcut.length === 0) return
|
if (!shortcut.enabled || shortcut.shortcut.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const handler = getShortcutHandler(shortcut)
|
const handler = getShortcutHandler(shortcut)
|
||||||
if (!handler) return
|
|
||||||
|
if (!handler) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const accelerator = formatShortcutKey(shortcut.shortcut)
|
const accelerator = formatShortcutKey(shortcut.shortcut)
|
||||||
|
|
||||||
@ -67,7 +71,22 @@ function registerWindowShortcuts(window: BrowserWindow) {
|
|||||||
showAppAccelerator = accelerator
|
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))
|
globalShortcut.register(accelerator, () => handler(window))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -79,9 +98,7 @@ function registerWindowShortcuts(window: BrowserWindow) {
|
|||||||
|
|
||||||
if (showAppAccelerator) {
|
if (showAppAccelerator) {
|
||||||
const handler = getShortcutHandler({ key: 'show_app' } as Shortcut)
|
const handler = getShortcutHandler({ key: 'show_app' } as Shortcut)
|
||||||
if (handler) {
|
handler && globalShortcut.register(showAppAccelerator, () => handler(window))
|
||||||
globalShortcut.register(showAppAccelerator, () => handler(window))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,10 +110,6 @@ function registerWindowShortcuts(window: BrowserWindow) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerShortcuts(mainWindow: BrowserWindow) {
|
|
||||||
registerWindowShortcuts(mainWindow)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregisterAllShortcuts() {
|
export function unregisterAllShortcuts() {
|
||||||
showAppAccelerator = null
|
showAppAccelerator = null
|
||||||
globalShortcut.unregisterAll()
|
globalShortcut.unregisterAll()
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { audioExts, documentExts, imageExts, textExts, videoExts } from '@main/constant'
|
import { audioExts, documentExts, imageExts, textExts, videoExts } from '@shared/config/constant'
|
||||||
|
import { FileTypes } from '@types'
|
||||||
import { FileTypes } from '../../renderer/src/types'
|
|
||||||
|
|
||||||
export function getFileType(ext: string): FileTypes {
|
export function getFileType(ext: string): FileTypes {
|
||||||
ext = ext.toLowerCase()
|
ext = ext.toLowerCase()
|
||||||
|
|||||||
@ -3,97 +3,8 @@ export const DEFAULT_CONTEXTCOUNT = 5
|
|||||||
export const DEFAULT_MAX_TOKENS = 4096
|
export const DEFAULT_MAX_TOKENS = 4096
|
||||||
export const FONT_FAMILY =
|
export const FONT_FAMILY =
|
||||||
"Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"
|
"Ubuntu, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif"
|
||||||
|
|
||||||
export const platform = window.electron?.process?.platform
|
export const platform = window.electron?.process?.platform
|
||||||
export const isMac = platform === 'darwin'
|
export const isMac = platform === 'darwin'
|
||||||
export const isWindows = platform === 'win32' || platform === 'win64'
|
export const isWindows = platform === 'win32' || platform === 'win64'
|
||||||
export const isLinux = platform === 'linux'
|
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 代码文件
|
|
||||||
]
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac, isWindows } from '@renderer/config/constant'
|
||||||
import { isLocalAi } from '@renderer/config/env'
|
import { isLocalAi } from '@renderer/config/env'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
import i18n from '@renderer/i18n'
|
import i18n from '@renderer/i18n'
|
||||||
import { useAppDispatch } from '@renderer/store'
|
import { useAppDispatch } from '@renderer/store'
|
||||||
import { setAvatar, setFilesPath } from '@renderer/store/runtime'
|
import { setAvatar, setFilesPath } from '@renderer/store/runtime'
|
||||||
|
import { updateShortcut } from '@renderer/store/shortcuts'
|
||||||
import { runAsyncFunction } from '@renderer/utils'
|
import { runAsyncFunction } from '@renderer/utils'
|
||||||
import { useLiveQuery } from 'dexie-react-hooks'
|
import { useLiveQuery } from 'dexie-react-hooks'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
@ -11,6 +12,7 @@ import { useEffect } from 'react'
|
|||||||
import { useDefaultModel } from './useAssistant'
|
import { useDefaultModel } from './useAssistant'
|
||||||
import { useRuntime } from './useRuntime'
|
import { useRuntime } from './useRuntime'
|
||||||
import { useSettings } from './useSettings'
|
import { useSettings } from './useSettings'
|
||||||
|
import { useShortcuts } from './useShortcuts'
|
||||||
|
|
||||||
export function useAppInit() {
|
export function useAppInit() {
|
||||||
const dispatch = useAppDispatch()
|
const dispatch = useAppDispatch()
|
||||||
@ -18,6 +20,7 @@ export function useAppInit() {
|
|||||||
const { minappShow } = useRuntime()
|
const { minappShow } = useRuntime()
|
||||||
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
|
const { setDefaultModel, setTopicNamingModel, setTranslateModel } = useDefaultModel()
|
||||||
const avatar = useLiveQuery(() => db.settings.get('image://avatar'))
|
const avatar = useLiveQuery(() => db.settings.get('image://avatar'))
|
||||||
|
const { shortcuts } = useShortcuts()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
avatar?.value && dispatch(setAvatar(avatar.value))
|
avatar?.value && dispatch(setAvatar(avatar.value))
|
||||||
@ -69,4 +72,15 @@ export function useAppInit() {
|
|||||||
dispatch(setFilesPath(info.filesPath))
|
dispatch(setFilesPath(info.filesPath))
|
||||||
})
|
})
|
||||||
}, [dispatch])
|
}, [dispatch])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isWindows) {
|
||||||
|
shortcuts.forEach((shortcut) => {
|
||||||
|
if (shortcut.shortcut[0] === 'Command') {
|
||||||
|
shortcut.shortcut[0] = 'Ctrl'
|
||||||
|
dispatch(updateShortcut(shortcut))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, [dispatch, shortcuts])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -49,7 +49,12 @@ export const useShortcut = (
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
enableOnFormTags: options.enableOnFormTags,
|
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 }
|
||||||
|
}
|
||||||
|
|||||||
@ -458,7 +458,9 @@
|
|||||||
"reset_defaults": "Reset Defaults",
|
"reset_defaults": "Reset Defaults",
|
||||||
"reset_defaults_confirm": "Are you sure you want to reset all shortcuts?",
|
"reset_defaults_confirm": "Are you sure you want to reset all shortcuts?",
|
||||||
"press_shortcut": "Press Shortcut",
|
"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.auto": "Auto",
|
||||||
"theme.dark": "Dark",
|
"theme.dark": "Dark",
|
||||||
|
|||||||
@ -458,7 +458,9 @@
|
|||||||
"reset_defaults": "Сбросить настройки по умолчанию",
|
"reset_defaults": "Сбросить настройки по умолчанию",
|
||||||
"reset_defaults_confirm": "Вы уверены, что хотите сбросить все горячие клавиши?",
|
"reset_defaults_confirm": "Вы уверены, что хотите сбросить все горячие клавиши?",
|
||||||
"press_shortcut": "Нажмите сочетание клавиш",
|
"press_shortcut": "Нажмите сочетание клавиш",
|
||||||
"alt_warning": "Mac не поддерживает Alt"
|
"alt_warning": "Mac не поддерживает Alt",
|
||||||
|
"reset_to_default": "Сбросить настройки по умолчанию",
|
||||||
|
"clear_shortcut": "Очистить сочетание клавиш"
|
||||||
},
|
},
|
||||||
"theme.auto": "Автоматически",
|
"theme.auto": "Автоматически",
|
||||||
"theme.dark": "Темная",
|
"theme.dark": "Темная",
|
||||||
|
|||||||
@ -446,7 +446,9 @@
|
|||||||
"reset_defaults": "重置默认快捷键",
|
"reset_defaults": "重置默认快捷键",
|
||||||
"reset_defaults_confirm": "确定要重置所有快捷键吗?",
|
"reset_defaults_confirm": "确定要重置所有快捷键吗?",
|
||||||
"press_shortcut": "按下快捷键",
|
"press_shortcut": "按下快捷键",
|
||||||
"alt_warning": "Mac 系统不支持 Alt 键"
|
"alt_warning": "Mac 系统不支持 Alt 键",
|
||||||
|
"reset_to_default": "重置为默认",
|
||||||
|
"clear_shortcut": "清除快捷键"
|
||||||
},
|
},
|
||||||
"theme.auto": "跟随系统",
|
"theme.auto": "跟随系统",
|
||||||
"theme.dark": "深色主题",
|
"theme.dark": "深色主题",
|
||||||
|
|||||||
@ -446,7 +446,9 @@
|
|||||||
"reset_defaults": "重置預設快捷鍵",
|
"reset_defaults": "重置預設快捷鍵",
|
||||||
"reset_defaults_confirm": "確定要重置所有快捷鍵嗎?",
|
"reset_defaults_confirm": "確定要重置所有快捷鍵嗎?",
|
||||||
"press_shortcut": "按下快捷鍵",
|
"press_shortcut": "按下快捷鍵",
|
||||||
"alt_warning": "Mac 系統不支持 Alt 鍵"
|
"alt_warning": "Mac 系統不支持 Alt 鍵",
|
||||||
|
"reset_to_default": "重置為預設",
|
||||||
|
"clear_shortcut": "清除快捷鍵"
|
||||||
},
|
},
|
||||||
"theme.auto": "自動",
|
"theme.auto": "自動",
|
||||||
"theme.dark": "深色主題",
|
"theme.dark": "深色主題",
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { PaperClipOutlined } from '@ant-design/icons'
|
import { PaperClipOutlined } from '@ant-design/icons'
|
||||||
import { documentExts, imageExts, textExts } from '@renderer/config/constant'
|
|
||||||
import { isVisionModel } from '@renderer/config/models'
|
import { isVisionModel } from '@renderer/config/models'
|
||||||
import { FileType, Model } from '@renderer/types'
|
import { FileType, Model } from '@renderer/types'
|
||||||
|
import { documentExts, imageExts, textExts } from '@shared/config/constant'
|
||||||
import { Tooltip } from 'antd'
|
import { Tooltip } from 'antd'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { PicCenterOutlined } from '@ant-design/icons'
|
import { PicCenterOutlined } from '@ant-design/icons'
|
||||||
import TranslateButton from '@renderer/components/TranslateButton'
|
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 { isVisionModel } from '@renderer/config/models'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
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 { setGenerating, setSearching } from '@renderer/store/runtime'
|
||||||
import { Assistant, FileType, Message, Topic } from '@renderer/types'
|
import { Assistant, FileType, Message, Topic } from '@renderer/types'
|
||||||
import { delay, getFileExtension, uuid } from '@renderer/utils'
|
import { delay, getFileExtension, uuid } from '@renderer/utils'
|
||||||
|
import { documentExts, imageExts, textExts } from '@shared/config/constant'
|
||||||
import { Button, Popconfirm, Tooltip } from 'antd'
|
import { Button, Popconfirm, Tooltip } from 'antd'
|
||||||
import TextArea, { TextAreaRef } from 'antd/es/input/TextArea'
|
import TextArea, { TextAreaRef } from 'antd/es/input/TextArea'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import {
|
|||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup'
|
import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup'
|
||||||
import TextEditPopup from '@renderer/components/Popups/TextEditPopup'
|
import TextEditPopup from '@renderer/components/Popups/TextEditPopup'
|
||||||
import { useDefaultModel } from '@renderer/hooks/useAssistant'
|
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
|
||||||
import { translateText } from '@renderer/services/TranslateService'
|
import { translateText } from '@renderer/services/TranslateService'
|
||||||
import { Message, Model } from '@renderer/types'
|
import { Message, Model } from '@renderer/types'
|
||||||
@ -37,7 +36,6 @@ const MessageMenubar: FC<Props> = (props) => {
|
|||||||
const { message, index, model, isLastMessage, isAssistantMessage, setModel, onEditMessage, onDeleteMessage } = props
|
const { message, index, model, isLastMessage, isAssistantMessage, setModel, onEditMessage, onDeleteMessage } = props
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [copied, setCopied] = useState(false)
|
const [copied, setCopied] = useState(false)
|
||||||
const { translateModel } = useDefaultModel()
|
|
||||||
const [isTranslating, setIsTranslating] = useState(false)
|
const [isTranslating, setIsTranslating] = useState(false)
|
||||||
|
|
||||||
const isUserMessage = message.role === 'user'
|
const isUserMessage = message.role === 'user'
|
||||||
|
|||||||
@ -490,8 +490,4 @@ const InfoIcon = styled(QuestionCircleOutlined)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const RefreshIcon = styled(RedoOutlined)`
|
|
||||||
cursor: pointer;
|
|
||||||
`
|
|
||||||
|
|
||||||
export default PaintingsPage
|
export default PaintingsPage
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { UndoOutlined } from '@ant-design/icons'
|
import { ClearOutlined, UndoOutlined } from '@ant-design/icons'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
import { useAppDispatch, useAppSelector } from '@renderer/store'
|
||||||
import { initialState, resetShortcuts, updateShortcut } from '@renderer/store/shortcuts'
|
import { initialState, resetShortcuts, toggleShortcut, updateShortcut } from '@renderer/store/shortcuts'
|
||||||
import { Button, Input, InputRef, Table as AntTable } from 'antd'
|
import { Shortcut } from '@renderer/types'
|
||||||
|
import { Button, Input, InputRef, Switch, Table as AntTable, Tooltip } from 'antd'
|
||||||
import type { ColumnsType } from 'antd/es/table'
|
import type { ColumnsType } from 'antd/es/table'
|
||||||
import { FC, useRef, useState } from 'react'
|
import { FC, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -12,13 +13,6 @@ import styled from 'styled-components'
|
|||||||
|
|
||||||
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.'
|
import { SettingContainer, SettingDivider, SettingGroup, SettingTitle } from '.'
|
||||||
|
|
||||||
interface ShortcutItem {
|
|
||||||
key: string
|
|
||||||
name: string
|
|
||||||
shortcut: string[]
|
|
||||||
enabled: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const ShortcutSettings: FC = () => {
|
const ShortcutSettings: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { theme } = useTheme()
|
const { theme } = useTheme()
|
||||||
@ -27,7 +21,7 @@ const ShortcutSettings: FC = () => {
|
|||||||
const inputRefs = useRef<Record<string, InputRef>>({})
|
const inputRefs = useRef<Record<string, InputRef>>({})
|
||||||
const [editingKey, setEditingKey] = useState<string | null>(null)
|
const [editingKey, setEditingKey] = useState<string | null>(null)
|
||||||
|
|
||||||
const handleClear = (record: ShortcutItem) => {
|
const handleClear = (record: Shortcut) => {
|
||||||
dispatch(
|
dispatch(
|
||||||
updateShortcut({
|
updateShortcut({
|
||||||
...record,
|
...record,
|
||||||
@ -36,19 +30,19 @@ const ShortcutSettings: FC = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddShortcut = (record: ShortcutItem) => {
|
const handleAddShortcut = (record: Shortcut) => {
|
||||||
setEditingKey(record.key)
|
setEditingKey(record.key)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
inputRefs.current[record.key]?.focus()
|
inputRefs.current[record.key]?.focus()
|
||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isShortcutModified = (record: ShortcutItem) => {
|
const isShortcutModified = (record: Shortcut) => {
|
||||||
const defaultShortcut = initialState.shortcuts.find((s) => s.key === record.key)
|
const defaultShortcut = initialState.shortcuts.find((s) => s.key === record.key)
|
||||||
return defaultShortcut?.shortcut.join('+') !== record.shortcut.join('+')
|
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)
|
const defaultShortcut = initialState.shortcuts.find((s) => s.key === record.key)
|
||||||
if (defaultShortcut) {
|
if (defaultShortcut) {
|
||||||
dispatch(
|
dispatch(
|
||||||
@ -95,6 +89,8 @@ const ShortcutSettings: FC = () => {
|
|||||||
return isMac ? '⌥' : 'Alt'
|
return isMac ? '⌥' : 'Alt'
|
||||||
case 'Shift':
|
case 'Shift':
|
||||||
return isMac ? '⇧' : 'Shift'
|
return isMac ? '⇧' : 'Shift'
|
||||||
|
case 'CommandOrControl':
|
||||||
|
return isMac ? '⌘' : 'Ctrl'
|
||||||
case ' ':
|
case ' ':
|
||||||
return 'Space'
|
return 'Space'
|
||||||
default:
|
default:
|
||||||
@ -104,7 +100,8 @@ const ShortcutSettings: FC = () => {
|
|||||||
.join(' + ')
|
.join(' + ')
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent, record: ShortcutItem) => {
|
const handleKeyDown = (e: React.KeyboardEvent, record: Shortcut) => {
|
||||||
|
console.debug('handleKeyDown', e, record)
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
const keys: string[] = []
|
const keys: string[] = []
|
||||||
@ -115,6 +112,8 @@ const ShortcutSettings: FC = () => {
|
|||||||
|
|
||||||
const key = e.key
|
const key = e.key
|
||||||
|
|
||||||
|
console.debug('key', key)
|
||||||
|
|
||||||
if (!['Control', 'Alt', 'Shift', 'Meta'].includes(key)) {
|
if (!['Control', 'Alt', 'Shift', 'Meta'].includes(key)) {
|
||||||
keys.push(key.toUpperCase())
|
keys.push(key.toUpperCase())
|
||||||
}
|
}
|
||||||
@ -144,7 +143,7 @@ const ShortcutSettings: FC = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns: ColumnsType<ShortcutItem> = [
|
const columns: ColumnsType<Shortcut> = [
|
||||||
{
|
{
|
||||||
title: t('settings.shortcuts.action'),
|
title: t('settings.shortcuts.action'),
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
@ -155,14 +154,16 @@ const ShortcutSettings: FC = () => {
|
|||||||
dataIndex: 'shortcut',
|
dataIndex: 'shortcut',
|
||||||
key: 'shortcut',
|
key: 'shortcut',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
render: (shortcut: string[], record: ShortcutItem) => {
|
render: (shortcut: string[], record: Shortcut) => {
|
||||||
const isEditing = editingKey === record.key
|
const isEditing = editingKey === record.key
|
||||||
|
const shortcutConfig = shortcuts.find((s) => s.key === record.key)
|
||||||
|
const isEditable = shortcutConfig?.editable !== false
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', gap: '8px' }}>
|
<HStack style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end', alignItems: 'center' }}>
|
||||||
<div style={{ position: 'relative', flex: 1 }}>
|
<HStack alignItems="center" style={{ position: 'relative' }}>
|
||||||
{isEditing ? (
|
{isEditing ? (
|
||||||
<Input
|
<ShortcutInput
|
||||||
ref={(el) => el && (inputRefs.current[record.key] = el)}
|
ref={(el) => el && (inputRefs.current[record.key] = el)}
|
||||||
value={formatShortcut(shortcut)}
|
value={formatShortcut(shortcut)}
|
||||||
placeholder={t('settings.shortcuts.press_shortcut')}
|
placeholder={t('settings.shortcuts.press_shortcut')}
|
||||||
@ -173,39 +174,51 @@ const ShortcutSettings: FC = () => {
|
|||||||
setEditingKey(null)
|
setEditingKey(null)
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ width: '120px' }}
|
|
||||||
suffix={
|
|
||||||
isShortcutModified(record) && (
|
|
||||||
<UndoOutlined
|
|
||||||
className="shortcut-undo-icon"
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
right: '8px',
|
|
||||||
top: '50%',
|
|
||||||
transform: 'translateY(-50%)',
|
|
||||||
cursor: 'pointer',
|
|
||||||
color: '#999'
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
handleResetShortcut(record)
|
|
||||||
setEditingKey(null)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ cursor: 'pointer', padding: '4px 11px' }} onClick={() => handleAddShortcut(record)}>
|
<ShortcutText isEditable={isEditable} onClick={() => isEditable && handleAddShortcut(record)}>
|
||||||
{shortcut.length > 0 ? formatShortcut(shortcut) : t('settings.shortcuts.press_shortcut')}
|
{shortcut.length > 0 ? formatShortcut(shortcut) : t('settings.shortcuts.press_shortcut')}
|
||||||
</div>
|
</ShortcutText>
|
||||||
)}
|
)}
|
||||||
</div>
|
</HStack>
|
||||||
<Button onClick={() => (shortcut ? handleClear(record) : handleAddShortcut(record))}>
|
</HStack>
|
||||||
{shortcut ? t('common.clear') : t('common.add')}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('settings.shortcuts.actions'),
|
||||||
|
key: 'actions',
|
||||||
|
align: 'right',
|
||||||
|
width: '70px',
|
||||||
|
render: (record: Shortcut) => (
|
||||||
|
<HStack style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end', alignItems: 'center' }}>
|
||||||
|
<Tooltip title={t('settings.shortcuts.reset_to_default')}>
|
||||||
|
<Button
|
||||||
|
icon={<UndoOutlined />}
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleResetShortcut(record)}
|
||||||
|
disabled={!isShortcutModified(record)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={t('settings.shortcuts.clear_shortcut')}>
|
||||||
|
<Button
|
||||||
|
icon={<ClearOutlined />}
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleClear(record)}
|
||||||
|
disabled={record.shortcut.length === 0 || !record.editable}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</HStack>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('settings.shortcuts.enabled'),
|
||||||
|
key: 'enabled',
|
||||||
|
align: 'right',
|
||||||
|
width: '50px',
|
||||||
|
render: (record: Shortcut) => (
|
||||||
|
<Switch size="small" checked={record.enabled} onChange={() => dispatch(toggleShortcut(record.key))} />
|
||||||
|
)
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -216,7 +229,7 @@ const ShortcutSettings: FC = () => {
|
|||||||
<SettingDivider style={{ marginBottom: 0 }} />
|
<SettingDivider style={{ marginBottom: 0 }} />
|
||||||
<Table
|
<Table
|
||||||
columns={columns as ColumnsType<unknown>}
|
columns={columns as ColumnsType<unknown>}
|
||||||
dataSource={shortcuts.map((s) => ({ ...s, name: t(s.name) }))}
|
dataSource={shortcuts.map((s) => ({ ...s, name: t(`settings.shortcuts.${s.key}`) }))}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
size="middle"
|
size="middle"
|
||||||
showHeader={false}
|
showHeader={false}
|
||||||
@ -245,4 +258,15 @@ const Table = styled(AntTable)`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const ShortcutInput = styled(Input)`
|
||||||
|
width: 120px;
|
||||||
|
text-align: center;
|
||||||
|
`
|
||||||
|
|
||||||
|
const ShortcutText = styled.span<{ isEditable: boolean }>`
|
||||||
|
cursor: ${({ isEditable }) => (isEditable ? 'pointer' : 'not-allowed')};
|
||||||
|
padding: 4px 11px;
|
||||||
|
opacity: ${({ isEditable }) => (isEditable ? 1 : 0.5)};
|
||||||
|
`
|
||||||
|
|
||||||
export default ShortcutSettings
|
export default ShortcutSettings
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { Shortcut } from '@renderer/types'
|
import { Shortcut } from '@renderer/types'
|
||||||
|
import { ZOOM_SHORTCUTS } from '@shared/config/constant'
|
||||||
|
|
||||||
export interface ShortcutsState {
|
export interface ShortcutsState {
|
||||||
shortcuts: Shortcut[]
|
shortcuts: Shortcut[]
|
||||||
@ -8,34 +9,17 @@ export interface ShortcutsState {
|
|||||||
|
|
||||||
const initialState: ShortcutsState = {
|
const initialState: ShortcutsState = {
|
||||||
shortcuts: [
|
shortcuts: [
|
||||||
|
...ZOOM_SHORTCUTS,
|
||||||
{
|
{
|
||||||
key: 'new_topic',
|
key: 'new_topic',
|
||||||
name: 'settings.shortcuts.new_topic',
|
|
||||||
shortcut: [isMac ? 'Command' : 'Ctrl', 'N'],
|
shortcut: [isMac ? 'Command' : 'Ctrl', 'N'],
|
||||||
enabled: true
|
editable: true,
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'zoom_in',
|
|
||||||
name: 'settings.shortcuts.zoom_in',
|
|
||||||
shortcut: [isMac ? 'Command' : 'Ctrl', '='],
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'zoom_out',
|
|
||||||
name: 'settings.shortcuts.zoom_out',
|
|
||||||
shortcut: [isMac ? 'Command' : 'Ctrl', '-'],
|
|
||||||
enabled: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'zoom_reset',
|
|
||||||
name: 'settings.shortcuts.zoom_reset',
|
|
||||||
shortcut: [isMac ? 'Command' : 'Ctrl', '0'],
|
|
||||||
enabled: true
|
enabled: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'show_app',
|
key: 'show_app',
|
||||||
name: 'settings.shortcuts.show_app',
|
shortcut: [],
|
||||||
shortcut: [isMac ? 'Command' : 'Ctrl', 'Shift', 'A'],
|
editable: true,
|
||||||
enabled: true
|
enabled: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -44,7 +28,6 @@ const initialState: ShortcutsState = {
|
|||||||
const getSerializableShortcuts = (shortcuts: Shortcut[]) => {
|
const getSerializableShortcuts = (shortcuts: Shortcut[]) => {
|
||||||
return shortcuts.map((shortcut) => ({
|
return shortcuts.map((shortcut) => ({
|
||||||
key: shortcut.key,
|
key: shortcut.key,
|
||||||
name: shortcut.name,
|
|
||||||
shortcut: [...shortcut.shortcut],
|
shortcut: [...shortcut.shortcut],
|
||||||
enabled: shortcut.enabled
|
enabled: shortcut.enabled
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -165,7 +165,7 @@ export type AppInfo = {
|
|||||||
|
|
||||||
export interface Shortcut {
|
export interface Shortcut {
|
||||||
key: string
|
key: string
|
||||||
name: string
|
|
||||||
shortcut: string[]
|
shortcut: string[]
|
||||||
|
editable: boolean
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,20 +5,19 @@
|
|||||||
"src/main/**/*",
|
"src/main/**/*",
|
||||||
"src/preload/**/*",
|
"src/preload/**/*",
|
||||||
"src/main/env.d.ts",
|
"src/main/env.d.ts",
|
||||||
"src/renderer/src/types/index.ts"
|
"src/renderer/src/types/index.ts",
|
||||||
|
"packages/shared/**/*"
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"types": [
|
"types": [
|
||||||
"electron-vite/node"
|
"electron-vite/node"
|
||||||
],
|
],
|
||||||
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@types": [
|
"@main/*": ["src/main/*"],
|
||||||
"./src/renderer/src/types/index.ts"
|
"@types": ["src/renderer/src/types/index.ts"],
|
||||||
],
|
"@shared/*": ["packages/shared/*"]
|
||||||
"@main/*": [
|
|
||||||
"./src/main/*"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,18 @@
|
|||||||
{
|
{
|
||||||
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
|
||||||
"include": [
|
"include": [
|
||||||
"src/renderer/src/env.d.ts",
|
|
||||||
"src/renderer/src/**/*",
|
"src/renderer/src/**/*",
|
||||||
"src/renderer/src/**/*.tsx",
|
|
||||||
"src/preload/*.d.ts",
|
"src/preload/*.d.ts",
|
||||||
"local/src/renderer/**/*",
|
"local/src/renderer/**/*",
|
||||||
|
"packages/shared/**/*"
|
||||||
],
|
],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@renderer/*": [
|
"@renderer/*": ["src/renderer/src/*"],
|
||||||
"src/renderer/src/*"
|
"@shared/*": ["packages/shared/*"]
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user