refactor: update theme handling and preference types

- Removed the App_SetTheme IPC channel and related theme management code.
- Updated imports to use preferenceTypes for theme and selection action items.
- Refactored preference service to preload preferences and adjusted related components.
- Cleaned up unused code and comments related to theme management.
- Enhanced the organization of preference-related types and actions for better maintainability.
This commit is contained in:
fullex 2025-09-01 23:49:17 +08:00
parent 0dce1c57fc
commit 0ef3852029
64 changed files with 482 additions and 354 deletions

View File

@ -13,7 +13,7 @@ export enum IpcChannel {
App_SetLaunchToTray = 'app:set-launch-to-tray',
App_SetTray = 'app:set-tray',
App_SetTrayOnClose = 'app:set-tray-on-close',
App_SetTheme = 'app:set-theme',
// App_SetTheme = 'app:set-theme',
App_SetAutoUpdate = 'app:set-auto-update',
App_SetTestPlan = 'app:set-test-plan',
App_SetTestChannel = 'app:set-test-channel',
@ -234,7 +234,7 @@ export enum IpcChannel {
// events
BackupProgress = 'backup-progress',
DataMigrateProgress = 'data-migrate-progress',
ThemeUpdated = 'theme:updated',
NativeThemeUpdated = 'native-theme:updated',
UpdateDownloadedCancelled = 'update-downloaded-cancelled',
RestoreProgress = 'restore-progress',
UpdateError = 'update-error',

View File

@ -1,8 +1,8 @@
/**
*
* preferences.ts中
*/
import type { SelectionActionItem } from './types'
import type { SelectionActionItem } from './preferenceTypes'
export const defaultActionItems: SelectionActionItem[] = [
{ id: 'translate', name: 'selection.action.builtin.translate', enabled: true, isBuiltIn: true, icon: 'languages' },

View File

@ -3,7 +3,7 @@ import { PreferencesType } from './preferences'
export type PreferenceDefaultScopeType = PreferencesType['default']
export type PreferenceKeyType = keyof PreferenceDefaultScopeType
export interface PreferenceUpdateOptions {
export type PreferenceUpdateOptions = {
optimistic: boolean
}
@ -16,7 +16,7 @@ export type PreferenceShortcutType = {
export type SelectionTriggerMode = 'selected' | 'ctrlkey' | 'shortcut'
export type SelectionFilterMode = 'default' | 'whitelist' | 'blacklist'
export interface SelectionActionItem {
export type SelectionActionItem = {
id: string
name: string
enabled: boolean
@ -27,3 +27,9 @@ export interface SelectionActionItem {
selectedText?: string
searchEngine?: string
}
export enum ThemeMode {
light = 'light',
dark = 'dark',
system = 'system'
}

View File

@ -1,9 +1,6 @@
import { defaultActionItems } from './pending_default_values'
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from './types'
/**
* Auto-generated preferences configuration
* Generated at: 2025-08-15T03:23:46.568Z
* Generated at: 2025-09-01T14:36:18.273Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
@ -12,10 +9,14 @@ import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } f
* === AUTO-GENERATED CONTENT START ===
*/
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from './preferenceTypes'
import { ThemeMode } from './preferenceTypes'
/* eslint @typescript-eslint/member-ordering: ["error", {
"interfaces": { "order": "alphabetically" },
"typeLiterals": { "order": "alphabetically" }
}] */
export interface PreferencesType {
default: {
// redux/settings/enableDeveloperMode
@ -51,9 +52,9 @@ export interface PreferencesType {
// redux/settings/spellCheckLanguages
'app.spell_check.languages': unknown[]
// redux/settings/theme
'app.theme.mode': string
// redux/settings/userTheme
'app.theme.user_defined': Record<string, unknown>
'app.theme.mode': ThemeMode
// redux/settings/userTheme.colorPrimary
'app.theme.user.color_primary': string
// redux/settings/windowStyle
'app.theme.window_style': string
// redux/settings/tray
@ -100,8 +101,6 @@ export interface PreferencesType {
'chat.code.viewer.theme_light': string
// redux/settings/codeWrappable
'chat.code.wrappable': boolean
// redux/settings/enableBackspaceDeleteModel
'chat.input.backspace_delete_model': boolean
// redux/settings/pasteLongTextAsFile
'chat.input.paste_long_text_as_file': boolean
// redux/settings/pasteLongTextThreshold
@ -312,6 +311,24 @@ export interface PreferencesType {
'feature.selection.remember_win_size': boolean
// redux/selectionStore/triggerMode
'feature.selection.trigger_mode': SelectionTriggerMode
// redux/shortcuts/shortcuts.exit_fullscreen
'shortcut.app.exit_fullscreen': Record<string, unknown>
// redux/shortcuts/shortcuts.search_message
'shortcut.app.search_message': Record<string, unknown>
// redux/shortcuts/shortcuts.show_app
'shortcut.app.show_main_window': Record<string, unknown>
// redux/shortcuts/shortcuts.mini_window
'shortcut.app.show_mini_window': Record<string, unknown>
// redux/shortcuts/shortcuts.show_settings
'shortcut.app.show_settings': Record<string, unknown>
// redux/shortcuts/shortcuts.toggle_show_assistants
'shortcut.app.toggle_show_assistants': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_in
'shortcut.app.zoom_in': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_out
'shortcut.app.zoom_out': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_reset
'shortcut.app.zoom_reset': Record<string, unknown>
// redux/shortcuts/shortcuts.clear_topic
'shortcut.chat.clear': Record<string, unknown>
// redux/shortcuts/shortcuts.copy_last_message
@ -320,26 +337,12 @@ export interface PreferencesType {
'shortcut.chat.search_message': Record<string, unknown>
// redux/shortcuts/shortcuts.toggle_new_context
'shortcut.chat.toggle_new_context': Record<string, unknown>
// redux/shortcuts/shortcuts.exit_fullscreen
'shortcut.exit_fullscreen': Record<string, unknown>
// redux/shortcuts/shortcuts.search_message
'shortcut.global.search_message': Record<string, unknown>
// redux/shortcuts/shortcuts.show_app
'shortcut.main_window.show': Record<string, unknown>
// redux/shortcuts/shortcuts.mini_window
'shortcut.mini_window.show': Record<string, unknown>
// redux/shortcuts/shortcuts.show_settings
'shortcut.show_settings': Record<string, unknown>
// redux/shortcuts/shortcuts.toggle_show_assistants
'shortcut.toggle_show_assistants': Record<string, unknown>
// redux/shortcuts/shortcuts.selection_assistant_select_text
'shortcut.selection.get_text': Record<string, unknown>
// redux/shortcuts/shortcuts.selection_assistant_toggle
'shortcut.selection.toggle_enabled': Record<string, unknown>
// redux/shortcuts/shortcuts.new_topic
'shortcut.topic.new': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_in
'shortcut.zoom_in': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_out
'shortcut.zoom_out': Record<string, unknown>
// redux/shortcuts/shortcuts.zoom_reset
'shortcut.zoom_reset': Record<string, unknown>
// redux/settings/enableTopicNaming
'topic.naming.enabled': boolean
// redux/settings/topicNamingPrompt
@ -357,7 +360,7 @@ export interface PreferencesType {
// redux/settings/customCss
'ui.custom_css': string
// redux/settings/navbarPosition
'ui.navbar.position': string
'ui.navbar.position': 'left' | 'top'
}
}
@ -380,8 +383,8 @@ export const DefaultPreferences: PreferencesType = {
'app.proxy.url': null,
'app.spell_check.enabled': false,
'app.spell_check.languages': [],
'app.theme.mode': 'ThemeMode.system',
'app.theme.user_defined': { colorPrimary: '#00b96b' },
'app.theme.mode': ThemeMode.system,
'app.theme.user.color_primary': '#00b96b',
'app.theme.window_style': 'opaque',
'app.tray.enabled': true,
'app.tray.on_close': true,
@ -405,7 +408,6 @@ export const DefaultPreferences: PreferencesType = {
'chat.code.viewer.theme_dark': 'auto',
'chat.code.viewer.theme_light': 'auto',
'chat.code.wrappable': false,
'chat.input.backspace_delete_model': true,
'chat.input.paste_long_text_as_file': false,
'chat.input.paste_long_text_threshold': 1500,
'chat.input.quick_panel.triggers_enabled': false,
@ -500,7 +502,34 @@ export const DefaultPreferences: PreferencesType = {
'feature.quick_assistant.click_tray_to_show': false,
'feature.quick_assistant.enabled': false,
'feature.quick_assistant.read_clipboard_at_startup': true,
'feature.selection.action_items': defaultActionItems,
'feature.selection.action_items': [
{
enabled: true,
icon: 'languages',
id: 'translate',
isBuiltIn: true,
name: 'selection.action.builtin.translate'
},
{
enabled: true,
icon: 'file-question',
id: 'explain',
isBuiltIn: true,
name: 'selection.action.builtin.explain'
},
{ enabled: true, icon: 'scan-text', id: 'summary', isBuiltIn: true, name: 'selection.action.builtin.summary' },
{
enabled: true,
icon: 'search',
id: 'search',
isBuiltIn: true,
name: 'selection.action.builtin.search',
searchEngine: 'Google|https://www.google.com/search?q={{queryString}}'
},
{ enabled: true, icon: 'clipboard-copy', id: 'copy', isBuiltIn: true, name: 'selection.action.builtin.copy' },
{ enabled: false, icon: 'wand-sparkles', id: 'refine', isBuiltIn: true, name: 'selection.action.builtin.refine' },
{ enabled: false, icon: 'quote', id: 'quote', isBuiltIn: true, name: 'selection.action.builtin.quote' }
],
'feature.selection.action_window_opacity': 100,
'feature.selection.auto_close': false,
'feature.selection.auto_pin': false,
@ -511,6 +540,25 @@ export const DefaultPreferences: PreferencesType = {
'feature.selection.follow_toolbar': true,
'feature.selection.remember_win_size': false,
'feature.selection.trigger_mode': 'selected',
'shortcut.app.exit_fullscreen': { editable: false, enabled: true, key: ['Escape'], system: true },
'shortcut.app.search_message': {
editable: true,
enabled: true,
key: ['CommandOrControl', 'Shift', 'F'],
system: false
},
'shortcut.app.show_main_window': { editable: true, enabled: true, key: [], system: true },
'shortcut.app.show_mini_window': { editable: true, enabled: false, key: ['CommandOrControl', 'E'], system: true },
'shortcut.app.show_settings': { editable: false, enabled: true, key: ['CommandOrControl', ','], system: true },
'shortcut.app.toggle_show_assistants': {
editable: true,
enabled: true,
key: ['CommandOrControl', '['],
system: false
},
'shortcut.app.zoom_in': { editable: false, enabled: true, key: ['CommandOrControl', '='], system: true },
'shortcut.app.zoom_out': { editable: false, enabled: true, key: ['CommandOrControl', '-'], system: true },
'shortcut.app.zoom_reset': { editable: false, enabled: true, key: ['CommandOrControl', '0'], system: true },
'shortcut.chat.clear': { editable: true, enabled: true, key: ['CommandOrControl', 'L'], system: false },
'shortcut.chat.copy_last_message': {
editable: true,
@ -525,21 +573,9 @@ export const DefaultPreferences: PreferencesType = {
key: ['CommandOrControl', 'K'],
system: false
},
'shortcut.exit_fullscreen': { editable: false, enabled: true, key: ['Escape'], system: true },
'shortcut.global.search_message': {
editable: true,
enabled: true,
key: ['CommandOrControl', 'Shift', 'F'],
system: false
},
'shortcut.main_window.show': { editable: true, enabled: true, key: [], system: true },
'shortcut.mini_window.show': { editable: true, enabled: false, key: ['CommandOrControl', 'E'], system: true },
'shortcut.show_settings': { editable: false, enabled: true, key: ['CommandOrControl', ','], system: true },
'shortcut.toggle_show_assistants': { editable: true, enabled: true, key: ['CommandOrControl', '['], system: false },
'shortcut.selection.get_text': { editable: true, enabled: false, key: [], system: true },
'shortcut.selection.toggle_enabled': { editable: true, enabled: false, key: [], system: true },
'shortcut.topic.new': { editable: true, enabled: true, key: ['CommandOrControl', 'N'], system: false },
'shortcut.zoom_in': { editable: false, enabled: true, key: ['CommandOrControl', '='], system: true },
'shortcut.zoom_out': { editable: false, enabled: true, key: ['CommandOrControl', '-'], system: true },
'shortcut.zoom_reset': { editable: false, enabled: true, key: ['CommandOrControl', '0'], system: true },
'topic.naming.enabled': true,
'topic.naming.prompt': '',
'topic.pin_to_top': false,
@ -556,8 +592,8 @@ export const DefaultPreferences: PreferencesType = {
/**
* :
* - 总配置项: 170
* - 总配置项: 171
* - electronStore项: 2
* - redux项: 168
* - redux项: 169
* - localStorage项: 0
*/

View File

@ -1,7 +1,7 @@
import { dbService } from '@data/db/DbService'
import { loggerService } from '@logger'
import { DefaultPreferences } from '@shared/data/preferences'
import type { PreferenceDefaultScopeType, PreferenceKeyType } from '@shared/data/types'
import type { PreferenceDefaultScopeType, PreferenceKeyType } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { and, eq } from 'drizzle-orm'
import { BrowserWindow, ipcMain } from 'electron'

View File

@ -1,6 +1,6 @@
/**
* Auto-generated preference mappings from classification.json
* Generated at: 2025-08-10T12:47:40.247Z
* Generated at: 2025-09-01T14:38:08.063Z
*
* This file contains pure mapping relationships without default values.
* Default values are managed in packages/shared/data/preferences.ts
@ -131,8 +131,8 @@ export const REDUX_STORE_MAPPINGS = {
targetKey: 'app.launch_on_boot'
},
{
originalKey: 'userTheme',
targetKey: 'app.theme.user_defined'
originalKey: 'userTheme.colorPrimary',
targetKey: 'app.theme.user.color_primary'
},
{
originalKey: 'windowStyle',
@ -446,10 +446,6 @@ export const REDUX_STORE_MAPPINGS = {
originalKey: 'enableQuickPanelTriggers',
targetKey: 'chat.input.quick_panel.triggers_enabled'
},
{
originalKey: 'enableBackspaceDeleteModel',
targetKey: 'chat.input.backspace_delete_model'
},
{
originalKey: 'exportMenuOptions.image',
targetKey: 'data.export.menus.image'
@ -654,6 +650,72 @@ export const REDUX_STORE_MAPPINGS = {
originalKey: 'nutstoreSkipBackupFile',
targetKey: 'data.backup.nutstore.skip_backup_file'
}
],
shortcuts: [
{
originalKey: 'shortcuts.zoom_in',
targetKey: 'shortcut.app.zoom_in'
},
{
originalKey: 'shortcuts.zoom_out',
targetKey: 'shortcut.app.zoom_out'
},
{
originalKey: 'shortcuts.zoom_reset',
targetKey: 'shortcut.app.zoom_reset'
},
{
originalKey: 'shortcuts.show_settings',
targetKey: 'shortcut.app.show_settings'
},
{
originalKey: 'shortcuts.show_app',
targetKey: 'shortcut.app.show_main_window'
},
{
originalKey: 'shortcuts.mini_window',
targetKey: 'shortcut.app.show_mini_window'
},
{
originalKey: 'shortcuts.selection_assistant_toggle',
targetKey: 'shortcut.selection.toggle_enabled'
},
{
originalKey: 'shortcuts.selection_assistant_select_text',
targetKey: 'shortcut.selection.get_text'
},
{
originalKey: 'shortcuts.new_topic',
targetKey: 'shortcut.topic.new'
},
{
originalKey: 'shortcuts.toggle_show_assistants',
targetKey: 'shortcut.app.toggle_show_assistants'
},
{
originalKey: 'shortcuts.copy_last_message',
targetKey: 'shortcut.chat.copy_last_message'
},
{
originalKey: 'shortcuts.search_message_in_chat',
targetKey: 'shortcut.chat.search_message'
},
{
originalKey: 'shortcuts.search_message',
targetKey: 'shortcut.app.search_message'
},
{
originalKey: 'shortcuts.clear_topic',
targetKey: 'shortcut.chat.clear'
},
{
originalKey: 'shortcuts.toggle_new_context',
targetKey: 'shortcut.chat.toggle_new_context'
},
{
originalKey: 'shortcuts.exit_fullscreen',
targetKey: 'shortcut.app.exit_fullscreen'
}
]
} as const
@ -662,9 +724,9 @@ export const REDUX_STORE_MAPPINGS = {
/**
* :
* - ElectronStore项: 2
* - Redux Store项: 154
* - Redux分类: settings, selectionStore, nutstore
* - 总配置项: 156
* - Redux Store项: 169
* - Redux分类: settings, selectionStore, nutstore, shortcuts
* - 总配置项: 171
*
* 使:
* 1. ElectronStore读取: configManager.get(mapping.originalKey)

View File

@ -11,7 +11,7 @@ import { handleZoomFactor } from '@main/utils/zoom'
import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
import { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH, UpgradeChannel } from '@shared/config/constant'
import { IpcChannel } from '@shared/IpcChannel'
import { FileMetadata, Provider, Shortcut, ThemeMode } from '@types'
import { FileMetadata, Provider, Shortcut } from '@types'
import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron'
import { Notification } from 'src/renderer/src/types/notification'
@ -53,7 +53,6 @@ import {
tokenUsage
} from './services/SpanCacheService'
import storeSyncService from './services/StoreSyncService'
import { themeService } from './services/ThemeService'
import VertexAIService from './services/VertexAIService'
import { setOpenLinkExternal } from './services/WebviewService'
import { windowService } from './services/WindowService'
@ -225,10 +224,10 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
return configManager.get(key)
})
// theme
ipcMain.handle(IpcChannel.App_SetTheme, (_, theme: ThemeMode) => {
themeService.setTheme(theme)
})
// // theme
// ipcMain.handle(IpcChannel.App_SetTheme, (_, theme: ThemeMode) => {
// themeService.setTheme(theme)
// })
ipcMain.handle(IpcChannel.App_HandleZoomFactor, (_, delta: number, reset: boolean = false) => {
const windows = BrowserWindow.getAllWindows()

View File

@ -1,5 +1,5 @@
import { defaultLanguage, UpgradeChannel, ZOOM_SHORTCUTS } from '@shared/config/constant'
import { LanguageVarious, Shortcut, ThemeMode } from '@types'
import { LanguageVarious, Shortcut } from '@types'
import { app } from 'electron'
import Store from 'electron-store'
@ -47,13 +47,13 @@ export class ConfigManager {
this.setAndNotify(ConfigKeys.Language, lang)
}
getTheme(): ThemeMode {
return this.get(ConfigKeys.Theme, ThemeMode.system)
}
// getTheme(): ThemeMode {
// return this.get(ConfigKeys.Theme, ThemeMode.system)
// }
setTheme(theme: ThemeMode) {
this.set(ConfigKeys.Theme, theme)
}
// setTheme(theme: ThemeMode) {
// this.set(ConfigKeys.Theme, theme)
// }
getLaunchToTray(): boolean {
return !!this.get(ConfigKeys.LaunchToTray, false)

View File

@ -2,7 +2,7 @@ import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { SELECTION_FINETUNED_LIST, SELECTION_PREDEFINED_BLACKLIST } from '@main/configs/SelectionConfig'
import { isDev, isMac, isWin } from '@main/constant'
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from '@shared/data/types'
import type { SelectionActionItem, SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { app, BrowserWindow, ipcMain, screen, systemPreferences } from 'electron'
import { join } from 'path'
@ -470,11 +470,11 @@ export class SelectionService {
})
/** uncomment to open dev tools in dev mode */
// if (isDev) {
// this.toolbarWindow.once('ready-to-show', () => {
// this.toolbarWindow!.webContents.openDevTools({ mode: 'detach' })
// })
// }
if (isDev) {
this.toolbarWindow.once('ready-to-show', () => {
this.toolbarWindow!.webContents.openDevTools({ mode: 'detach' })
})
}
if (readyCallback) {
this.toolbarWindow.once('ready-to-show', readyCallback)

View File

@ -1,23 +1,28 @@
import { preferenceService } from '@data/PreferenceService'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { ThemeMode } from '@types'
import { BrowserWindow, nativeTheme } from 'electron'
import { titleBarOverlayDark, titleBarOverlayLight } from '../config'
import { configManager } from './ConfigManager'
class ThemeService {
private theme: ThemeMode = ThemeMode.system
constructor() {
this.theme = configManager.getTheme()
this.theme = preferenceService.get('app.theme.mode')
if (this.theme === ThemeMode.dark || this.theme === ThemeMode.light || this.theme === ThemeMode.system) {
nativeTheme.themeSource = this.theme
} else {
// 兼容旧版本
configManager.setTheme(ThemeMode.system)
preferenceService.set('app.theme.mode', ThemeMode.system)
nativeTheme.themeSource = ThemeMode.system
}
nativeTheme.on('updated', this.themeUpdatadHandler.bind(this))
preferenceService.subscribeChange('app.theme.mode', (newTheme) => {
this.theme = newTheme
nativeTheme.themeSource = newTheme
})
}
themeUpdatadHandler() {
@ -30,19 +35,12 @@ class ThemeService {
// Because it may be called with some windows have some title bar
}
}
win.webContents.send(IpcChannel.ThemeUpdated, nativeTheme.shouldUseDarkColors ? ThemeMode.dark : ThemeMode.light)
win.webContents.send(
IpcChannel.NativeThemeUpdated,
nativeTheme.shouldUseDarkColors ? ThemeMode.dark : ThemeMode.light
)
})
}
setTheme(theme: ThemeMode) {
if (theme === this.theme) {
return
}
this.theme = theme
nativeTheme.themeSource = theme
configManager.setTheme(theme)
}
}
export const themeService = new ThemeService()

View File

@ -4,8 +4,8 @@ import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core'
import { SpanContext } from '@opentelemetry/api'
import { UpgradeChannel } from '@shared/config/constant'
import type { LogLevel, LogSourceWithContext } from '@shared/config/logger'
import type { PreferenceDefaultScopeType, PreferenceKeyType, SelectionActionItem } from '@shared/data/types'
import type { FileChangeEvent } from '@shared/config/types'
import type { PreferenceDefaultScopeType, PreferenceKeyType, SelectionActionItem } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import {
AddMemoryOptions,
@ -25,7 +25,6 @@ import {
S3Config,
Shortcut,
SupportedOcrFile,
ThemeMode,
WebDavConfig
} from '@types'
import { contextBridge, ipcRenderer, OpenDialogOptions, shell, webUtils } from 'electron'
@ -57,7 +56,7 @@ const api = {
setTrayOnClose: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetTrayOnClose, isActive),
setTestPlan: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetTestPlan, isActive),
setTestChannel: (channel: UpgradeChannel) => ipcRenderer.invoke(IpcChannel.App_SetTestChannel, channel),
setTheme: (theme: ThemeMode) => ipcRenderer.invoke(IpcChannel.App_SetTheme, theme),
// setTheme: (theme: ThemeMode) => ipcRenderer.invoke(IpcChannel.App_SetTheme, theme),
handleZoomFactor: (delta: number, reset: boolean = false) =>
ipcRenderer.invoke(IpcChannel.App_HandleZoomFactor, delta, reset),
setAutoUpdate: (isActive: boolean) => ipcRenderer.invoke(IpcChannel.App_SetAutoUpdate, isActive),

View File

@ -17,7 +17,7 @@ import Router from './Router'
const logger = loggerService.withContext('App.tsx')
preferenceService.loadAll()
preferenceService.preloadAll()
// 创建 React Query 客户端
const queryClient = new QueryClient({

View File

@ -7,7 +7,7 @@ import Sidebar from './components/app/Sidebar'
import { ErrorBoundary } from './components/ErrorBoundary'
import TabsContainer from './components/Tab/TabContainer'
import NavigationHandler from './handler/NavigationHandler'
import { useNavbarPosition } from './hooks/useSettings'
import { useNavbarPosition } from './hooks/useNavbar'
import AgentsPage from './pages/agents/AgentsPage'
import CodeToolsPage from './pages/code/CodeToolsPage'
import FilesPage from './pages/files/FilesPage'

View File

@ -1,8 +1,8 @@
import { CodeOutlined } from '@ant-design/icons'
import { loggerService } from '@logger'
import { useTheme } from '@renderer/context/ThemeProvider'
import { ThemeMode } from '@renderer/types'
import { extractHtmlTitle, getFileNameFromHtmlTitle } from '@renderer/utils/formats'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button } from 'antd'
import { Code, DownloadIcon, Globe, LinkIcon, Sparkles } from 'lucide-react'
import { FC, useState } from 'react'

View File

@ -4,8 +4,8 @@ import IndicatorLight from '@renderer/components/IndicatorLight'
import { loadCustomMiniApp, ORIGIN_DEFAULT_MIN_APPS, updateDefaultMinApps } from '@renderer/config/minapps'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { setOpenedKeepAliveMinapps } from '@renderer/store/runtime'
import { MinAppType } from '@renderer/types'
import type { MenuProps } from 'antd'

View File

@ -17,8 +17,9 @@ import { useBridge } from '@renderer/hooks/useBridge'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
import { useMinapps } from '@renderer/hooks/useMinapps'
import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useSettings } from '@renderer/hooks/useSettings'
import { useTimer } from '@renderer/hooks/useTimer'
import { useAppDispatch } from '@renderer/store'
import { setMinappsOpenLinkExternal } from '@renderer/store/settings'

View File

@ -1,4 +1,5 @@
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import { WebviewTag } from 'electron'
import { memo, useEffect, useRef } from 'react'

View File

@ -8,8 +8,8 @@ import tabsService from '@renderer/services/TabsService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import type { Tab } from '@renderer/store/tabs'
import { addTab, removeTab, setActiveTab } from '@renderer/store/tabs'
import { ThemeMode } from '@renderer/types'
import { classNames } from '@renderer/utils'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Tooltip } from 'antd'
import {
FileSearch,

View File

@ -1,7 +1,7 @@
import { isLinux, isMac, isWin } from '@renderer/config/constant'
import { useFullscreen } from '@renderer/hooks/useFullscreen'
import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import type { FC, PropsWithChildren } from 'react'
import type { HTMLAttributes } from 'react'
import styled from 'styled-components'

View File

@ -1,8 +1,9 @@
import { useTheme } from '@renderer/context/ThemeProvider'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useRuntime } from '@renderer/hooks/useRuntime'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useSettings } from '@renderer/hooks/useSettings'
import { MinAppType } from '@renderer/types'
import type { MenuProps } from 'antd'
import { Dropdown, Tooltip } from 'antd'

View File

@ -10,8 +10,8 @@ import useNavBackgroundColor from '@renderer/hooks/useNavBackgroundColor'
import { modelGenerating, useRuntime } from '@renderer/hooks/useRuntime'
import { useSettings } from '@renderer/hooks/useSettings'
import { getSidebarIconLabel, getThemeModeLabel } from '@renderer/i18n/label'
import { ThemeMode } from '@renderer/types'
import { isEmoji } from '@renderer/utils'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Avatar, Tooltip } from 'antd'
import {
Code,

View File

@ -2,8 +2,8 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import { useMermaid } from '@renderer/hooks/useMermaid'
import { useSettings } from '@renderer/hooks/useSettings'
import { HighlightChunkResult, ShikiPreProperties, shikiStreamService } from '@renderer/services/ShikiStreamService'
import { ThemeMode } from '@renderer/types'
import { getHighlighter, getMarkdownIt, getShiki, loadLanguageIfNeeded, loadThemeIfNeeded } from '@renderer/utils/shiki'
import { ThemeMode } from '@shared/data/preferenceTypes'
import * as cmThemes from '@uiw/codemirror-themes-all'
import type React from 'react'
import { createContext, type PropsWithChildren, use, useCallback, useEffect, useMemo, useState } from 'react'

View File

@ -1,10 +1,10 @@
import { usePreference } from '@data/hooks/usePreference'
import { isMac, isWin } from '@renderer/config/constant'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import useUserTheme from '@renderer/hooks/useUserTheme'
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import React, { createContext, PropsWithChildren, use, useEffect, useState } from 'react'
interface ThemeContextType {
theme: ThemeMode
settedTheme: ThemeMode
@ -25,7 +25,10 @@ interface ThemeProviderProps extends PropsWithChildren {
export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
// 用户设置的主题
const { theme: settedTheme, setTheme: setSettedTheme } = useSettings()
// const { theme: settedTheme, setTheme: setSettedTheme } = useSettings()
const [settedTheme, setSettedTheme] = usePreference('app.theme.mode')
const [actualTheme, setActualTheme] = useState<ThemeMode>(
window.matchMedia('(prefers-color-scheme: dark)').matches ? ThemeMode.dark : ThemeMode.light
)
@ -56,18 +59,14 @@ export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
initUserTheme()
// listen for theme updates from main process
return window.electron.ipcRenderer.on(IpcChannel.ThemeUpdated, (_, actualTheme: ThemeMode) => {
return window.electron.ipcRenderer.on(IpcChannel.NativeThemeUpdated, (_, actualTheme: ThemeMode) => {
document.body.setAttribute('theme-mode', actualTheme)
setActualTheme(actualTheme)
})
}, [actualTheme, initUserTheme, navbarPosition, setSettedTheme, settedTheme])
useEffect(() => {
window.api.setTheme(settedTheme)
}, [settedTheme])
return (
<ThemeContext value={{ theme: actualTheme, settedTheme, toggleTheme, setTheme: setSettedTheme }}>
<ThemeContext value={{ theme: actualTheme, settedTheme: settedTheme, toggleTheme, setTheme: setSettedTheme }}>
{children}
</ThemeContext>
)

View File

@ -1,6 +1,10 @@
import { loggerService } from '@logger'
import { DefaultPreferences } from '@shared/data/preferences'
import type { PreferenceDefaultScopeType, PreferenceKeyType, PreferenceUpdateOptions } from '@shared/data/types'
import type {
PreferenceDefaultScopeType,
PreferenceKeyType,
PreferenceUpdateOptions
} from '@shared/data/preferenceTypes'
const logger = loggerService.withContext('PreferenceService')
/**
@ -100,6 +104,8 @@ export class PreferenceService {
return this.cache[key] as PreferenceDefaultScopeType[K]
}
logger.verbose(`get: ${key} not found in cache`)
try {
// Fetch from main process if not cached
const value = await window.api.preference.get(key)
@ -215,6 +221,7 @@ export class PreferenceService {
if (key in this.cache && this.cache[key] !== undefined) {
cachedResults[key] = this.cache[key]
} else {
logger.verbose(`getMultiple: ${key} not found in cache`)
uncachedKeys.push(key)
}
}
@ -408,7 +415,7 @@ export class PreferenceService {
* Load all preferences from main process at once
* Provides optimal performance by loading complete preference set into memory
*/
public async loadAll(): Promise<PreferenceDefaultScopeType> {
public async preloadAll(): Promise<PreferenceDefaultScopeType> {
try {
const allPreferences = await window.api.preference.getAll()
@ -442,7 +449,7 @@ export class PreferenceService {
/**
* Preload specific preferences into cache
*/
async preload(keys: PreferenceKeyType[]): Promise<void> {
public async preload(keys: PreferenceKeyType[]): Promise<void> {
const uncachedKeys = keys.filter((key) => !this.isCached(key))
if (uncachedKeys.length > 0) {

View File

@ -1,7 +1,11 @@
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { DefaultPreferences } from '@shared/data/preferences'
import type { PreferenceDefaultScopeType, PreferenceKeyType, PreferenceUpdateOptions } from '@shared/data/types'
import type {
PreferenceDefaultScopeType,
PreferenceKeyType,
PreferenceUpdateOptions
} from '@shared/data/preferenceTypes'
import { useCallback, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'
const logger = loggerService.withContext('usePreference')

View File

@ -1,5 +1,5 @@
import { useTheme } from '@renderer/context/ThemeProvider'
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { useEffect, useState } from 'react'
// 跟踪 mermaid 模块状态,单例模式

View File

@ -0,0 +1,12 @@
import { usePreference } from '@data/hooks/usePreference'
export const useNavbarPosition = () => {
const [navbarPosition, setNavbarPosition] = usePreference('ui.navbar.position')
return {
navbarPosition,
isLeftNavbar: navbarPosition === 'left',
isTopNavbar: navbarPosition === 'top',
setNavbarPosition: (position: 'left' | 'top') => setNavbarPosition(position)
}
}

View File

@ -8,7 +8,6 @@ import {
setEnableDeveloperMode,
setLaunchOnBoot,
setLaunchToTray,
setNavbarPosition,
setPinTopicsToTop,
setSendMessageShortcut as _setSendMessageShortcut,
setShowTokens,
@ -23,8 +22,9 @@ import {
setTrayOnClose,
setWindowStyle
} from '@renderer/store/settings'
import { SidebarIcon, ThemeMode, TranslateLanguageCode } from '@renderer/types'
import { SidebarIcon, TranslateLanguageCode } from '@renderer/types'
import { UpgradeChannel } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
export function useSettings() {
const settings = useAppSelector((state) => state.settings)
@ -140,15 +140,3 @@ export const useEnableDeveloperMode = () => {
export const getEnableDeveloperMode = () => {
return store.getState().settings.enableDeveloperMode
}
export const useNavbarPosition = () => {
const navbarPosition = useAppSelector((state) => state.settings.navbarPosition)
const dispatch = useAppDispatch()
return {
navbarPosition,
isLeftNavbar: navbarPosition === 'left',
isTopNavbar: navbarPosition === 'top',
setNavbarPosition: (position: 'left' | 'top') => dispatch(setNavbarPosition(position))
}
}

View File

@ -1,13 +1,13 @@
import { useAppDispatch, useAppSelector } from '@renderer/store'
import { setUserTheme, UserTheme } from '@renderer/store/settings'
// import { useAppDispatch, useAppSelector } from '@renderer/store'
// import { setUserTheme, UserTheme } from '@renderer/store/settings'
import { usePreference } from '@data/hooks/usePreference'
import Color from 'color'
export default function useUserTheme() {
const userTheme = useAppSelector((state) => state.settings.userTheme)
const [colorPrimary, setColorPrimary] = usePreference('app.theme.user.color_primary')
const dispatch = useAppDispatch()
const initUserTheme = (theme: UserTheme = userTheme) => {
const initUserTheme = (theme: { colorPrimary: string } = { colorPrimary }) => {
const colorPrimary = Color(theme.colorPrimary)
document.body.style.setProperty('--color-primary', colorPrimary.toString())
@ -16,13 +16,14 @@ export default function useUserTheme() {
}
return {
colorPrimary: Color(userTheme.colorPrimary),
colorPrimary: Color(colorPrimary),
initUserTheme,
setUserTheme(userTheme: UserTheme) {
dispatch(setUserTheme(userTheme))
userTheme: { colorPrimary },
setUserTheme(userTheme: { colorPrimary: string }) {
setColorPrimary(userTheme.colorPrimary)
initUserTheme(userTheme)
}
}

View File

@ -5,7 +5,7 @@ import ListItem from '@renderer/components/ListItem'
import Scrollbar from '@renderer/components/Scrollbar'
import CustomTag from '@renderer/components/Tags/CustomTag'
import { useAgents } from '@renderer/hooks/useAgents'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { createAssistantFromAgent } from '@renderer/services/AssistantService'
import { Agent } from '@renderer/types'
import { uuid } from '@renderer/utils'

View File

@ -5,7 +5,8 @@ import MultiSelectActionPopup from '@renderer/components/Popups/MultiSelectionPo
import { QuickPanelProvider } from '@renderer/components/QuickPanel'
import { useAssistant } from '@renderer/hooks/useAssistant'
import { useChatContext } from '@renderer/hooks/useChatContext'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShortcut } from '@renderer/hooks/useShortcuts'
import { useShowAssistants, useShowTopics } from '@renderer/hooks/useStore'
import { useTimer } from '@renderer/hooks/useTimer'

View File

@ -1,6 +1,7 @@
import { ErrorBoundary } from '@renderer/components/ErrorBoundary'
import { useAssistants } from '@renderer/hooks/useAssistant'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import { useActiveTopic } from '@renderer/hooks/useTopic'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import NavigationService from '@renderer/services/NavigationService'

View File

@ -2,7 +2,7 @@ import { FormOutlined } from '@ant-design/icons'
import { useTheme } from '@renderer/context/ThemeProvider'
import { EventEmitter } from '@renderer/services/EventService'
import { EVENT_NAMES } from '@renderer/services/EventService'
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button as AntdButton } from 'antd'
import { FC } from 'react'
import { useTranslation } from 'react-i18next'

View File

@ -43,9 +43,10 @@ import {
setShowTranslateConfirm,
setThoughtAutoCollapse
} from '@renderer/store/settings'
import { Assistant, AssistantSettings, CodeStyleVarious, MathEngine, ThemeMode } from '@renderer/types'
import { Assistant, AssistantSettings, CodeStyleVarious, MathEngine } from '@renderer/types'
import { modalConfirm } from '@renderer/utils'
import { getSendMessageShortcutLabel } from '@renderer/utils/input'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button, Col, InputNumber, Row, Slider, Switch, Tooltip } from 'antd'
import { CircleHelp, Settings2 } from 'lucide-react'
import { FC, useCallback, useEffect, useMemo, useState } from 'react'

View File

@ -1,6 +1,7 @@
import AddAssistantPopup from '@renderer/components/Popups/AddAssistantPopup'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import { useShowTopics } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService'
import { Assistant, Topic } from '@renderer/types'

View File

@ -2,7 +2,7 @@ import { Navbar, NavbarMain } from '@renderer/components/app/Navbar'
import App from '@renderer/components/MinApp/MinApp'
import Scrollbar from '@renderer/components/Scrollbar'
import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { Button, Input } from 'antd'
import { Search, SettingsIcon } from 'lucide-react'
import React, { FC, useState } from 'react'

View File

@ -9,9 +9,9 @@ import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import { handleSaveData, useAppDispatch } from '@renderer/store'
import { setUpdateState } from '@renderer/store/runtime'
import { ThemeMode } from '@renderer/types'
import { runAsyncFunction } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Avatar, Button, Progress, Radio, Row, Switch, Tag, Tooltip } from 'antd'
import { debounce } from 'lodash'
import { Bug, FileCheck, Github, Globe, Mail, Rss } from 'lucide-react'

View File

@ -5,7 +5,8 @@ import TextBadge from '@renderer/components/TextBadge'
import { isMac, THEME_COLOR_PRESETS } from '@renderer/config/constant'
import { DEFAULT_SIDEBAR_ICONS } from '@renderer/config/sidebar'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import { useSettings } from '@renderer/hooks/useSettings'
import useUserTheme from '@renderer/hooks/useUserTheme'
import { useAppDispatch } from '@renderer/store'
import {
@ -17,7 +18,7 @@ import {
setShowTopicTime,
setSidebarIcons
} from '@renderer/store/settings'
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button, ColorPicker, Segmented, Switch } from 'antd'
import { Minus, Monitor, Moon, Plus, Sun } from 'lucide-react'
import { FC, useCallback, useEffect, useMemo, useState } from 'react'
@ -65,16 +66,14 @@ const DisplaySettings: FC = () => {
pinTopicsToTop,
customCss,
sidebarIcons,
setTheme,
assistantIconType,
userTheme
assistantIconType
} = useSettings()
const { navbarPosition, setNavbarPosition } = useNavbarPosition()
const { theme, settedTheme } = useTheme()
const { theme, settedTheme, setTheme } = useTheme()
const { t } = useTranslation()
const dispatch = useAppDispatch()
const [currentZoom, setCurrentZoom] = useState(1.0)
const { setUserTheme } = useUserTheme()
const { userTheme, setUserTheme } = useUserTheme()
const [visibleIcons, setVisibleIcons] = useState(sidebarIcons?.visible || DEFAULT_SIDEBAR_ICONS)
const [disabledIcons, setDisabledIcons] = useState(sidebarIcons?.disabled || [])

View File

@ -3,7 +3,7 @@ import { isMac, isWin } from '@renderer/config/constant'
import { useTheme } from '@renderer/context/ThemeProvider'
import { getSelectionDescriptionLabel } from '@renderer/i18n/label'
import SelectionToolbar from '@renderer/windows/selection/toolbar/SelectionToolbar'
import type { SelectionFilterMode, SelectionTriggerMode } from '@shared/data/types'
import type { SelectionFilterMode, SelectionTriggerMode } from '@shared/data/preferenceTypes'
import { Button, Radio, Row, Slider, Switch, Tooltip } from 'antd'
import { CircleHelp, Edit2 } from 'lucide-react'
import { FC, useEffect, useState } from 'react'

View File

@ -1,6 +1,6 @@
import type { DroppableProvided } from '@hello-pangea/dnd'
import { Draggable, Droppable } from '@hello-pangea/dnd'
import type { ActionItem as ActionItemType } from '@renderer/types/selectionTypes'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { memo } from 'react'
import styled from 'styled-components'
@ -8,9 +8,9 @@ import ActionsListItemComponent from './ActionsListItem'
interface ActionListProps {
droppableId: 'enabled' | 'disabled'
items: ActionItemType[]
items: SelectionActionItem[]
isLastEnabledItem: boolean
onEdit: (item: ActionItemType) => void
onEdit: (item: SelectionActionItem) => void
onDelete: (id: string) => void
getSearchEngineInfo: (engine: string) => { icon: any; name: string } | null
}

View File

@ -1,5 +1,5 @@
import type { DraggableProvided } from '@hello-pangea/dnd'
import type { ActionItem as ActionItemType } from '@renderer/types/selectionTypes'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { Button } from 'antd'
import { Pencil, Settings2, Trash } from 'lucide-react'
import { DynamicIcon } from 'lucide-react/dynamic'
@ -8,11 +8,11 @@ import { useTranslation } from 'react-i18next'
import styled from 'styled-components'
interface ActionItemProps {
item: ActionItemType
item: SelectionActionItem
provided: DraggableProvided
listType: 'enabled' | 'disabled'
isLastEnabledItem: boolean
onEdit: (item: ActionItemType) => void
onEdit: (item: SelectionActionItem) => void
onDelete: (id: string) => void
getSearchEngineInfo: (engine: string) => { icon: any; name: string } | null
}
@ -49,8 +49,8 @@ const ActionsListItem = memo(
)
interface ActionOperationsProps {
item: ActionItemType
onEdit: (item: ActionItemType) => void
item: SelectionActionItem
onEdit: (item: SelectionActionItem) => void
onDelete: (id: string) => void
}

View File

@ -1,5 +1,5 @@
import { loggerService } from '@logger'
import type { ActionItem } from '@renderer/types/selectionTypes'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { Button, Form, Input, Modal, Select } from 'antd'
import { Globe } from 'lucide-react'
import { FC, useEffect } from 'react'
@ -79,7 +79,7 @@ interface SelectionActionSearchModalProps {
isModalOpen: boolean
onOk: (searchEngine: string) => void
onCancel: () => void
currentAction?: ActionItem
currentAction?: SelectionActionItem
}
const SelectionActionSearchModal: FC<SelectionActionSearchModalProps> = ({

View File

@ -2,7 +2,7 @@ import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import CopyButton from '@renderer/components/CopyButton'
import { useAssistants, useDefaultAssistant } from '@renderer/hooks/useAssistant'
import { getDefaultModel } from '@renderer/services/AssistantService'
import type { ActionItem } from '@renderer/types/selectionTypes'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { Col, Input, Modal, Radio, Row, Select, Space, Tooltip } from 'antd'
import { CircleHelp, Dices, OctagonX } from 'lucide-react'
import { DynamicIcon, iconNames } from 'lucide-react/dynamic'
@ -12,8 +12,8 @@ import styled from 'styled-components'
interface SelectionActionUserModalProps {
isModalOpen: boolean
editingAction: ActionItem | null
onOk: (data: ActionItem) => void
editingAction: SelectionActionItem | null
onOk: (data: SelectionActionItem) => void
onCancel: () => void
}
@ -27,8 +27,8 @@ const SelectionActionUserModal: FC<SelectionActionUserModalProps> = ({
const { assistants: userPredefinedAssistants } = useAssistants()
const { defaultAssistant } = useDefaultAssistant()
const [formData, setFormData] = useState<Partial<ActionItem>>({})
const [errors, setErrors] = useState<Partial<Record<keyof ActionItem, string>>>({})
const [formData, setFormData] = useState<Partial<SelectionActionItem>>({})
const [errors, setErrors] = useState<Partial<Record<keyof SelectionActionItem, string>>>({})
useEffect(() => {
if (isModalOpen) {
@ -46,7 +46,7 @@ const SelectionActionUserModal: FC<SelectionActionUserModalProps> = ({
}, [isModalOpen, editingAction])
const validateForm = (): boolean => {
const newErrors: Partial<Record<keyof ActionItem, string>> = {}
const newErrors: Partial<Record<keyof SelectionActionItem, string>> = {}
if (!formData.name?.trim()) {
newErrors.name = t('selection.settings.user_modal.name.hint')
@ -66,7 +66,7 @@ const SelectionActionUserModal: FC<SelectionActionUserModalProps> = ({
}
// 构建完整的 ActionItem
const actionItem: ActionItem = {
const actionItem: SelectionActionItem = {
id: editingAction?.id || `user-${Date.now()}`,
name: formData.name || 'USER',
enabled: editingAction?.enabled || false,
@ -79,7 +79,7 @@ const SelectionActionUserModal: FC<SelectionActionUserModalProps> = ({
onOk(actionItem)
}
const handleInputChange = (field: keyof ActionItem, value: string) => {
const handleInputChange = (field: keyof SelectionActionItem, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }))
// Clear error when user starts typing
if (errors[field]) {

View File

@ -1,8 +1,8 @@
import { DragDropContext } from '@hello-pangea/dnd'
import { useTheme } from '@renderer/context/ThemeProvider'
import { defaultActionItems } from '@renderer/store/selectionStore'
import type { ActionItem } from '@renderer/types/selectionTypes'
import SelectionToolbar from '@renderer/windows/selection/toolbar/SelectionToolbar'
import { DefaultPreferences } from '@shared/data/preferences'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { Row } from 'antd'
import { FC } from 'react'
import styled from 'styled-components'
@ -20,8 +20,8 @@ import SettingsActionsListHeader from './SettingsActionsListHeader'
// Props for the main component
interface SelectionActionsListProps {
actionItems: ActionItem[] | undefined // List of all available actions
setActionItems: (items: ActionItem[]) => void // Function to update action items
actionItems: SelectionActionItem[] | undefined // List of all available actions
setActionItems: (items: SelectionActionItem[]) => void // Function to update action items
}
const SelectionActionsList: FC<SelectionActionsListProps> = ({ actionItems, setActionItems }) => {
@ -49,7 +49,7 @@ const SelectionActionsList: FC<SelectionActionsListProps> = ({ actionItems, setA
const { theme } = useTheme()
if (!actionItems || actionItems.length === 0) {
setActionItems(defaultActionItems)
setActionItems(DefaultPreferences.default['feature.selection.action_items'])
}
return (

View File

@ -1,7 +1,7 @@
import { DropResult } from '@hello-pangea/dnd'
import { loggerService } from '@logger'
import { defaultActionItems } from '@renderer/store/selectionStore'
import type { ActionItem } from '@renderer/types/selectionTypes'
import { DefaultPreferences } from '@shared/data/preferences'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -13,19 +13,19 @@ const MAX_CUSTOM_ITEMS = 8
const MAX_ENABLED_ITEMS = 6
export const useActionItems = (
initialItems: ActionItem[] | undefined,
setActionItems: (items: ActionItem[]) => void
initialItems: SelectionActionItem[] | undefined,
setActionItems: (items: SelectionActionItem[]) => void
) => {
const { t } = useTranslation()
const [isUserModalOpen, setIsUserModalOpen] = useState(false)
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false)
const [userEditingAction, setUserEditingAction] = useState<ActionItem | null>(null)
const [userEditingAction, setUserEditingAction] = useState<SelectionActionItem | null>(null)
const enabledItems = useMemo(() => initialItems?.filter((item) => item.enabled) ?? [], [initialItems])
const disabledItems = useMemo(() => initialItems?.filter((item) => !item.enabled) ?? [], [initialItems])
const customItemsCount = useMemo(() => initialItems?.filter((item) => !item.isBuiltIn).length ?? 0, [initialItems])
const handleEditActionItem = (item: ActionItem) => {
const handleEditActionItem = (item: SelectionActionItem) => {
if (item.isBuiltIn) {
if (item.id === 'search') {
setIsSearchModalOpen(true)
@ -43,7 +43,7 @@ export const useActionItems = (
setIsUserModalOpen(true)
}
const handleUserModalOk = (actionItem: ActionItem) => {
const handleUserModalOk = (actionItem: SelectionActionItem) => {
if (userEditingAction && initialItems) {
const updatedItems = initialItems.map((item) => (item.id === userEditingAction.id ? actionItem : item))
setActionItems(updatedItems)
@ -83,7 +83,7 @@ export const useActionItems = (
content: t('selection.settings.actions.reset.confirm'),
onOk: () => {
const userItems = initialItems.filter((item) => !item.isBuiltIn).map((item) => ({ ...item, enabled: false }))
setActionItems([...defaultActionItems, ...userItems])
setActionItems([...DefaultPreferences.default['feature.selection.action_items'], ...userItems])
}
})
}

View File

@ -1,4 +1,4 @@
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { AnimatePresence, motion } from 'framer-motion'
import { ChevronDown, ChevronRight } from 'lucide-react'
import { useState } from 'react'

View File

@ -1,4 +1,4 @@
import { ThemeMode } from '@renderer/types'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Divider } from 'antd'
import Link from 'antd/es/typography/Link'
import styled, { CSSProp } from 'styled-components'

View File

@ -26,7 +26,7 @@ import ocr from './ocr'
import paintings from './paintings'
import preprocess from './preprocess'
import runtime from './runtime'
import selectionStore from './selectionStore'
// import selectionStore from './selectionStore'
import settings from './settings'
import shortcuts from './shortcuts'
import tabs from './tabs'
@ -52,7 +52,7 @@ const rootReducer = combineReducers({
mcp,
memory,
copilot,
selectionStore,
// selectionStore,
tabs,
preprocess,
messages: newMessagesReducer,

View File

@ -41,7 +41,7 @@ import { DEFAULT_TOOL_ORDER } from './inputTools'
import { initialState as llmInitialState, moveProvider } from './llm'
import { mcpSlice } from './mcp'
import { initialState as notesInitialState } from './note'
import { defaultActionItems } from './selectionStore'
// import { defaultActionItems } from './selectionStore'
import { initialState as settingsInitialState } from './settings'
import { initialState as shortcutsInitialState } from './shortcuts'
import { defaultWebSearchProviders } from './websearch'
@ -127,14 +127,15 @@ function updateWebSearchProvider(state: RootState, provider: Partial<WebSearchPr
}
function addSelectionAction(state: RootState, id: string) {
if (state.selectionStore && state.selectionStore.actionItems) {
if (!state.selectionStore.actionItems.some((item) => item.id === id)) {
const action = defaultActionItems.find((item) => item.id === id)
if (action) {
state.selectionStore.actionItems.push(action)
}
}
}
// if (state.selectionStore && state.selectionStore.actionItems) {
// if (!state.selectionStore.actionItems.some((item) => item.id === id)) {
// const action = defaultActionItems.find((item) => item.id === id)
// if (action) {
// state.selectionStore.actionItems.push(action)
// }
// }
// }
return [state, id]
}
/**

View File

@ -1,89 +1,89 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'
import { ActionItem, FilterMode, SelectionState, TriggerMode } from '@renderer/types/selectionTypes'
// import { createSlice, PayloadAction } from '@reduxjs/toolkit'
// import { SelectionActionItem, FilterMode, SelectionState, TriggerMode } from '@shared/data/preferenceTypes'
export const defaultActionItems: ActionItem[] = [
{ id: 'translate', name: 'selection.action.builtin.translate', enabled: true, isBuiltIn: true, icon: 'languages' },
{ id: 'explain', name: 'selection.action.builtin.explain', enabled: true, isBuiltIn: true, icon: 'file-question' },
{ id: 'summary', name: 'selection.action.builtin.summary', enabled: true, isBuiltIn: true, icon: 'scan-text' },
{
id: 'search',
name: 'selection.action.builtin.search',
enabled: true,
isBuiltIn: true,
icon: 'search',
searchEngine: 'Google|https://www.google.com/search?q={{queryString}}'
},
{ id: 'copy', name: 'selection.action.builtin.copy', enabled: true, isBuiltIn: true, icon: 'clipboard-copy' },
{ id: 'refine', name: 'selection.action.builtin.refine', enabled: false, isBuiltIn: true, icon: 'wand-sparkles' },
{ id: 'quote', name: 'selection.action.builtin.quote', enabled: false, isBuiltIn: true, icon: 'quote' }
]
// export const defaultActionItems: ActionItem[] = [
// { id: 'translate', name: 'selection.action.builtin.translate', enabled: true, isBuiltIn: true, icon: 'languages' },
// { id: 'explain', name: 'selection.action.builtin.explain', enabled: true, isBuiltIn: true, icon: 'file-question' },
// { id: 'summary', name: 'selection.action.builtin.summary', enabled: true, isBuiltIn: true, icon: 'scan-text' },
// {
// id: 'search',
// name: 'selection.action.builtin.search',
// enabled: true,
// isBuiltIn: true,
// icon: 'search',
// searchEngine: 'Google|https://www.google.com/search?q={{queryString}}'
// },
// { id: 'copy', name: 'selection.action.builtin.copy', enabled: true, isBuiltIn: true, icon: 'clipboard-copy' },
// { id: 'refine', name: 'selection.action.builtin.refine', enabled: false, isBuiltIn: true, icon: 'wand-sparkles' },
// { id: 'quote', name: 'selection.action.builtin.quote', enabled: false, isBuiltIn: true, icon: 'quote' }
// ]
export const initialState: SelectionState = {
selectionEnabled: false,
triggerMode: 'selected',
isCompact: false,
isAutoClose: false,
isAutoPin: false,
isFollowToolbar: true,
isRemeberWinSize: false,
filterMode: 'default',
filterList: [],
actionWindowOpacity: 100,
actionItems: defaultActionItems
}
// export const initialState: SelectionState = {
// selectionEnabled: false,
// triggerMode: 'selected',
// isCompact: false,
// isAutoClose: false,
// isAutoPin: false,
// isFollowToolbar: true,
// isRemeberWinSize: false,
// filterMode: 'default',
// filterList: [],
// actionWindowOpacity: 100,
// actionItems: defaultActionItems
// }
const selectionSlice = createSlice({
name: 'selectionStore',
initialState,
reducers: {
setSelectionEnabled: (state, action: PayloadAction<boolean>) => {
state.selectionEnabled = action.payload
},
setTriggerMode: (state, action: PayloadAction<TriggerMode>) => {
state.triggerMode = action.payload
},
setIsCompact: (state, action: PayloadAction<boolean>) => {
state.isCompact = action.payload
},
setIsAutoClose: (state, action: PayloadAction<boolean>) => {
state.isAutoClose = action.payload
},
setIsAutoPin: (state, action: PayloadAction<boolean>) => {
state.isAutoPin = action.payload
},
setIsFollowToolbar: (state, action: PayloadAction<boolean>) => {
state.isFollowToolbar = action.payload
},
setIsRemeberWinSize: (state, action: PayloadAction<boolean>) => {
state.isRemeberWinSize = action.payload
},
setFilterMode: (state, action: PayloadAction<FilterMode>) => {
state.filterMode = action.payload
},
setFilterList: (state, action: PayloadAction<string[]>) => {
state.filterList = action.payload
},
setActionWindowOpacity: (state, action: PayloadAction<number>) => {
state.actionWindowOpacity = action.payload
},
setActionItems: (state, action: PayloadAction<ActionItem[]>) => {
state.actionItems = action.payload
}
}
})
// const selectionSlice = createSlice({
// name: 'selectionStore',
// initialState,
// reducers: {
// setSelectionEnabled: (state, action: PayloadAction<boolean>) => {
// state.selectionEnabled = action.payload
// },
// setTriggerMode: (state, action: PayloadAction<TriggerMode>) => {
// state.triggerMode = action.payload
// },
// setIsCompact: (state, action: PayloadAction<boolean>) => {
// state.isCompact = action.payload
// },
// setIsAutoClose: (state, action: PayloadAction<boolean>) => {
// state.isAutoClose = action.payload
// },
// setIsAutoPin: (state, action: PayloadAction<boolean>) => {
// state.isAutoPin = action.payload
// },
// setIsFollowToolbar: (state, action: PayloadAction<boolean>) => {
// state.isFollowToolbar = action.payload
// },
// setIsRemeberWinSize: (state, action: PayloadAction<boolean>) => {
// state.isRemeberWinSize = action.payload
// },
// setFilterMode: (state, action: PayloadAction<FilterMode>) => {
// state.filterMode = action.payload
// },
// setFilterList: (state, action: PayloadAction<string[]>) => {
// state.filterList = action.payload
// },
// setActionWindowOpacity: (state, action: PayloadAction<number>) => {
// state.actionWindowOpacity = action.payload
// },
// setActionItems: (state, action: PayloadAction<ActionItem[]>) => {
// state.actionItems = action.payload
// }
// }
// })
export const {
setSelectionEnabled,
setTriggerMode,
setIsCompact,
setIsAutoClose,
setIsAutoPin,
setIsFollowToolbar,
setIsRemeberWinSize,
setFilterMode,
setFilterList,
setActionWindowOpacity,
setActionItems
} = selectionSlice.actions
// export const {
// setSelectionEnabled,
// setTriggerMode,
// setIsCompact,
// setIsAutoClose,
// setIsAutoPin,
// setIsFollowToolbar,
// setIsRemeberWinSize,
// setFilterMode,
// setFilterList,
// setActionWindowOpacity,
// setActionItems
// } = selectionSlice.actions
export default selectionSlice.reducer
// export default selectionSlice.reducer

View File

@ -13,11 +13,11 @@ import {
PaintingProvider,
S3Config,
SidebarIcon,
ThemeMode,
TranslateLanguageCode
} from '@renderer/types'
import { uuid } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { OpenAIVerbosity } from '@types'
import { RemoteSyncState } from './backup'
@ -57,7 +57,9 @@ export interface SettingsState {
trayOnClose: boolean
tray: boolean
theme: ThemeMode
userTheme: UserTheme
userTheme: {
colorPrimary: string
}
windowStyle: 'transparent' | 'opaque'
fontSize: number
topicPosition: 'left' | 'right'

View File

@ -532,12 +532,6 @@ export type MinAppType = {
type?: 'Custom' | 'Default' // Added the 'type' property
}
export enum ThemeMode {
light = 'light',
dark = 'dark',
system = 'system'
}
/** 有限的UI语言 */
export type LanguageVarious = 'zh-CN' | 'zh-TW' | 'el-GR' | 'en-US' | 'es-ES' | 'fr-FR' | 'ja-JP' | 'pt-PT' | 'ru-RU'

View File

@ -1,6 +1,7 @@
import { AppLogo } from '@renderer/config/env'
import { usePreference } from '@renderer/data/hooks/usePreference'
import { loggerService } from '@renderer/services/LoggerService'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Button, Card, Col, Divider, Layout, Row, Space, Typography } from 'antd'
import { Database, FlaskConical, Settings, TestTube } from 'lucide-react'
import React from 'react'
@ -53,7 +54,7 @@ const TestApp: React.FC = () => {
const [zoomFactor] = usePreference('app.zoom_factor')
// Apply theme-based styling
const isDarkTheme = theme === 'ThemeMode.dark'
const isDarkTheme = theme === ThemeMode.dark
const headerBg = isDarkTheme ? '#141414' : '#fff'
const borderColor = isDarkTheme ? '#303030' : '#f0f0f0'
const textColor = isDarkTheme ? '#fff' : '#000'
@ -176,7 +177,7 @@ const TestApp: React.FC = () => {
<Button
icon={isDarkTheme ? '☀️' : '🌙'}
onClick={async () => {
await setTheme(isDarkTheme ? 'ThemeMode.light' : 'ThemeMode.dark')
await setTheme(isDarkTheme ? ThemeMode.light : ThemeMode.dark)
}}
style={{
backgroundColor: isDarkTheme ? '#434343' : '#f0f0f0',

View File

@ -1,5 +1,5 @@
import { usePreference } from '@renderer/data/hooks/usePreference'
import type { PreferenceKeyType } from '@shared/data/types'
import { type PreferenceKeyType, ThemeMode } from '@shared/data/preferenceTypes'
import { Button, Input, message, Select, Slider, Space, Switch, Typography } from 'antd'
import React, { useState } from 'react'
import styled from 'styled-components'
@ -21,7 +21,7 @@ const PreferenceBasicTests: React.FC = () => {
// Add theme monitoring for visual changes
const [currentTheme] = usePreference('app.theme.mode')
const isDarkTheme = currentTheme === 'ThemeMode.dark'
const isDarkTheme = currentTheme === ThemeMode.dark
const handleSetValue = async () => {
try {

View File

@ -1,7 +1,7 @@
import { usePreference } from '@renderer/data/hooks/usePreference'
import { preferenceService } from '@renderer/data/PreferenceService'
import { loggerService } from '@renderer/services/LoggerService'
import type { PreferenceKeyType } from '@shared/data/types'
import { type PreferenceKeyType, ThemeMode } from '@shared/data/preferenceTypes'
import { Button, Card, message, Space, Typography } from 'antd'
import React, { useState } from 'react'
import styled from 'styled-components'
@ -77,7 +77,7 @@ const PreferenceHookTests: React.FC = () => {
// Test batch set
await preferenceService.setMultiple({
'app.theme.mode': theme1 === 'ThemeMode.dark' ? 'ThemeMode.light' : 'ThemeMode.dark',
'app.theme.mode': theme1 === ThemeMode.dark ? ThemeMode.light : ThemeMode.dark,
'app.language': language === 'zh-CN' ? 'en-US' : 'zh-CN'
})
@ -102,7 +102,10 @@ const PreferenceHookTests: React.FC = () => {
// Test rapid writes
const writeStart = performance.now()
for (let i = 0; i < 10; i++) {
await preferenceService.set('app.theme.mode', `ThemeMode.test_${i}`)
await preferenceService.set(
'app.theme.mode',
i % 3 === 0 ? ThemeMode.light : i % 3 === 1 ? ThemeMode.dark : ThemeMode.system
)
}
const writeTime = performance.now() - writeStart

View File

@ -1,6 +1,6 @@
import { usePreference } from '@renderer/data/hooks/usePreference'
import { preferenceService } from '@renderer/data/PreferenceService'
import type { PreferenceKeyType } from '@shared/data/types'
import { type PreferenceKeyType, ThemeMode } from '@shared/data/preferenceTypes'
import { Button, Input, message, Space, Typography } from 'antd'
import React, { useState } from 'react'
import styled from 'styled-components'
@ -13,13 +13,13 @@ const { Text } = Typography
*/
const PreferenceServiceTests: React.FC = () => {
const [testKey, setTestKey] = useState<string>('app.theme.mode')
const [testValue, setTestValue] = useState<string>('ThemeMode.dark')
const [testValue, setTestValue] = useState<string>(ThemeMode.dark)
const [getResult, setGetResult] = useState<any>(null)
const [loading, setLoading] = useState(false)
// Theme monitoring for visual changes
const [currentTheme] = usePreference('app.theme.mode')
const isDarkTheme = currentTheme === 'ThemeMode.dark'
const isDarkTheme = currentTheme === ThemeMode.dark
const handleGet = async () => {
try {
@ -104,7 +104,7 @@ const PreferenceServiceTests: React.FC = () => {
try {
setLoading(true)
// Use loadAll to get all preferences at once
const result = await preferenceService.loadAll()
const result = await preferenceService.preloadAll()
setGetResult(`All preferences (${Object.keys(result).length} keys):\n${JSON.stringify(result, null, 2)}`)
message.success('获取所有偏好设置成功')
} catch (error) {
@ -174,7 +174,7 @@ const PreferenceServiceTests: React.FC = () => {
size="small"
onClick={() => {
setTestKey('app.theme.mode')
setTestValue('ThemeMode.dark')
setTestValue(ThemeMode.dark)
}}>
Test: app.theme.mode
</Button>

View File

@ -11,7 +11,7 @@ import store, { useAppSelector } from '@renderer/store'
import { updateOneBlock, upsertManyBlocks, upsertOneBlock } from '@renderer/store/messageBlock'
import { newMessagesActions, selectMessagesForTopic } from '@renderer/store/newMessage'
import { cancelThrottledBlockUpdate, throttledBlockUpdate } from '@renderer/store/thunk/messageThunk'
import { ThemeMode, Topic } from '@renderer/types'
import { Topic } from '@renderer/types'
import { Chunk, ChunkType } from '@renderer/types/chunk'
import { AssistantMessageStatus, MessageBlockStatus } from '@renderer/types/newMessage'
import { abortCompletion } from '@renderer/utils/abortController'
@ -19,6 +19,7 @@ import { isAbortError } from '@renderer/utils/error'
import { createMainTextBlock, createThinkingBlock } from '@renderer/utils/messageUtils/create'
import { getMainTextContent } from '@renderer/utils/messageUtils/find'
import { defaultLanguage } from '@shared/config/constant'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { Divider } from 'antd'
import { cloneDeep, isEmpty } from 'lodash'

View File

@ -1,9 +1,8 @@
import { usePreference } from '@data/hooks/usePreference'
import { isMac } from '@renderer/config/constant'
import { useSettings } from '@renderer/hooks/useSettings'
import i18n from '@renderer/i18n'
import { defaultLanguage } from '@shared/config/constant'
import type { SelectionActionItem } from '@shared/data/types'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { Button, Slider, Tooltip } from 'antd'
import { Droplet, Minus, Pin, X } from 'lucide-react'
@ -16,8 +15,8 @@ import ActionGeneral from './components/ActionGeneral'
import ActionTranslate from './components/ActionTranslate'
const SelectionActionApp: FC = () => {
const { language, customCss } = useSettings()
const [language] = usePreference('app.language')
const [customCss] = usePreference('ui.custom_css')
const { t } = useTranslation()
const [action, setAction] = useState<SelectionActionItem | null>(null)

View File

@ -1,8 +1,8 @@
import { LoadingOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import CopyButton from '@renderer/components/CopyButton'
import { useTopicMessages } from '@renderer/hooks/useMessageOperations'
import { useSettings } from '@renderer/hooks/useSettings'
import MessageContent from '@renderer/pages/home/Messages/MessageContent'
import {
getAssistantById,
@ -13,7 +13,7 @@ import {
import { pauseTrace } from '@renderer/services/SpanManagerService'
import { Assistant, Topic } from '@renderer/types'
import { abortCompletion } from '@renderer/utils/abortController'
import type { SelectionActionItem } from '@shared/data/types'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { ChevronDown } from 'lucide-react'
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -30,7 +30,7 @@ interface Props {
const ActionGeneral: FC<Props> = React.memo(({ action, scrollToBottom }) => {
const { t } = useTranslation()
const { language } = useSettings()
const [language] = usePreference('app.language')
const [error, setError] = useState<string | null>(null)
const [showOriginal, setShowOriginal] = useState(false)
const [isContented, setIsContented] = useState(false)

View File

@ -13,7 +13,7 @@ import { Assistant, Topic, TranslateLanguage, TranslateLanguageCode } from '@ren
import { runAsyncFunction } from '@renderer/utils'
import { abortCompletion } from '@renderer/utils/abortController'
import { detectLanguage } from '@renderer/utils/translate'
import type { SelectionActionItem } from '@shared/data/types'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { Tooltip } from 'antd'
import { ArrowRightFromLine, ArrowRightToLine, ChevronDown, CircleHelp, Globe } from 'lucide-react'
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'

View File

@ -1,6 +1,7 @@
import '@renderer/assets/styles/index.scss'
import '@ant-design/v5-patch-for-react-19'
import { preferenceService } from '@data/PreferenceService'
import KeyvStorage from '@kangfenmao/keyv-storage'
import { loggerService } from '@logger'
import AntdProvider from '@renderer/context/AntdProvider'
@ -17,6 +18,17 @@ import { PersistGate } from 'redux-persist/integration/react'
import SelectionActionApp from './SelectionActionApp'
loggerService.initWindowSource('SelectionActionWindow')
await preferenceService.preload([
'app.language',
'ui.custom_css',
'app.theme.mode',
'app.theme.user.color_primary',
'feature.selection.auto_close',
'feature.selection.auto_pin',
'feature.selection.action_window_opacity'
])
/**
* fetchChatCompletion depends on this,
* which is not a good design, but we have to add it for now

View File

@ -3,11 +3,10 @@ import '@renderer/assets/styles/selection-toolbar.scss'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import { AppLogo } from '@renderer/config/env'
import { useSettings } from '@renderer/hooks/useSettings'
import { useTimer } from '@renderer/hooks/useTimer'
import i18n from '@renderer/i18n'
import { defaultLanguage } from '@shared/config/constant'
import type { SelectionActionItem } from '@shared/data/types'
import type { SelectionActionItem } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { Avatar } from 'antd'
import { ClipboardCheck, ClipboardCopy, ClipboardX, MessageSquareHeart } from 'lucide-react'
@ -99,7 +98,8 @@ const ActionIcons: FC<{
* demo is used in the settings page
*/
const SelectionToolbar: FC<{ demo?: boolean }> = ({ demo = false }) => {
const { language, customCss } = useSettings()
const [language] = usePreference('app.language')
const [customCss] = usePreference('ui.custom_css')
const [isCompact] = usePreference('feature.selection.compact')
const [actionItems] = usePreference('feature.selection.action_items')
const [animateKey, setAnimateKey] = useState(0)

View File

@ -1,30 +1,28 @@
import '@ant-design/v5-patch-for-react-19'
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { ThemeProvider } from '@renderer/context/ThemeProvider'
import storeSyncService from '@renderer/services/StoreSyncService'
import store, { persistor } from '@renderer/store'
import { FC } from 'react'
import { createRoot } from 'react-dom/client'
import { Provider } from 'react-redux'
import { PersistGate } from 'redux-persist/integration/react'
import SelectionToolbar from './SelectionToolbar'
loggerService.initWindowSource('SelectionToolbar')
//subscribe to store sync
storeSyncService.subscribe()
await preferenceService.preload([
'app.language',
'ui.custom_css',
'app.theme.mode',
'app.theme.user.color_primary',
'feature.selection.compact',
'feature.selection.action_items'
])
const App: FC = () => {
return (
<Provider store={store}>
<ThemeProvider>
<PersistGate loading={null} persistor={persistor}>
<SelectionToolbar />
</PersistGate>
</ThemeProvider>
</Provider>
<ThemeProvider>
<SelectionToolbar />
</ThemeProvider>
)
}

View File

@ -12682,18 +12682,6 @@ __metadata:
languageName: node
linkType: hard
"dts-resolver@npm:^2.1.1":
version: 2.1.1
resolution: "dts-resolver@npm:2.1.1"
peerDependencies:
oxc-resolver: ">=11.0.0"
peerDependenciesMeta:
oxc-resolver:
optional: true
checksum: 10c0/bc36d71822d39f23cfe274b6781fae4b1729bd8b0a07e4a011fe243a73c5dbbb30ea067fb0d6248fdfedc29cf4dfc0ff19f0dd38950158444409d109c1c55b7e
languageName: node
linkType: hard
"drizzle-kit@npm:^0.31.4":
version: 0.31.4
resolution: "drizzle-kit@npm:0.31.4"
@ -12803,6 +12791,18 @@ __metadata:
languageName: node
linkType: hard
"dts-resolver@npm:^2.1.1":
version: 2.1.1
resolution: "dts-resolver@npm:2.1.1"
peerDependencies:
oxc-resolver: ">=11.0.0"
peerDependenciesMeta:
oxc-resolver:
optional: true
checksum: 10c0/bc36d71822d39f23cfe274b6781fae4b1729bd8b0a07e4a011fe243a73c5dbbb30ea067fb0d6248fdfedc29cf4dfc0ff19f0dd38950158444409d109c1c55b7e
languageName: node
linkType: hard
"duck@npm:^0.1.12":
version: 0.1.12
resolution: "duck@npm:0.1.12"
@ -14732,7 +14732,7 @@ __metadata:
languageName: node
linkType: hard
"get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.7.5":
"get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.7.0, get-tsconfig@npm:^4.7.5":
version: 4.10.1
resolution: "get-tsconfig@npm:4.10.1"
dependencies: