diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index 334b08e1c3..9e2605cfa3 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -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', diff --git a/packages/shared/data/pending_default_values.ts b/packages/shared/data/pending_default_values.ts index 5a8a18dfd0..9a1db3ccb6 100644 --- a/packages/shared/data/pending_default_values.ts +++ b/packages/shared/data/pending_default_values.ts @@ -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' }, diff --git a/packages/shared/data/types.d.ts b/packages/shared/data/preferenceTypes.ts similarity index 80% rename from packages/shared/data/types.d.ts rename to packages/shared/data/preferenceTypes.ts index 5e46a39222..eba2aefa5e 100644 --- a/packages/shared/data/types.d.ts +++ b/packages/shared/data/preferenceTypes.ts @@ -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' +} diff --git a/packages/shared/data/preferences.ts b/packages/shared/data/preferences.ts index 06bc24c08a..916ce50049 100644 --- a/packages/shared/data/preferences.ts +++ b/packages/shared/data/preferences.ts @@ -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 + '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 + // redux/shortcuts/shortcuts.search_message + 'shortcut.app.search_message': Record + // redux/shortcuts/shortcuts.show_app + 'shortcut.app.show_main_window': Record + // redux/shortcuts/shortcuts.mini_window + 'shortcut.app.show_mini_window': Record + // redux/shortcuts/shortcuts.show_settings + 'shortcut.app.show_settings': Record + // redux/shortcuts/shortcuts.toggle_show_assistants + 'shortcut.app.toggle_show_assistants': Record + // redux/shortcuts/shortcuts.zoom_in + 'shortcut.app.zoom_in': Record + // redux/shortcuts/shortcuts.zoom_out + 'shortcut.app.zoom_out': Record + // redux/shortcuts/shortcuts.zoom_reset + 'shortcut.app.zoom_reset': Record // redux/shortcuts/shortcuts.clear_topic 'shortcut.chat.clear': Record // redux/shortcuts/shortcuts.copy_last_message @@ -320,26 +337,12 @@ export interface PreferencesType { 'shortcut.chat.search_message': Record // redux/shortcuts/shortcuts.toggle_new_context 'shortcut.chat.toggle_new_context': Record - // redux/shortcuts/shortcuts.exit_fullscreen - 'shortcut.exit_fullscreen': Record - // redux/shortcuts/shortcuts.search_message - 'shortcut.global.search_message': Record - // redux/shortcuts/shortcuts.show_app - 'shortcut.main_window.show': Record - // redux/shortcuts/shortcuts.mini_window - 'shortcut.mini_window.show': Record - // redux/shortcuts/shortcuts.show_settings - 'shortcut.show_settings': Record - // redux/shortcuts/shortcuts.toggle_show_assistants - 'shortcut.toggle_show_assistants': Record + // redux/shortcuts/shortcuts.selection_assistant_select_text + 'shortcut.selection.get_text': Record + // redux/shortcuts/shortcuts.selection_assistant_toggle + 'shortcut.selection.toggle_enabled': Record // redux/shortcuts/shortcuts.new_topic 'shortcut.topic.new': Record - // redux/shortcuts/shortcuts.zoom_in - 'shortcut.zoom_in': Record - // redux/shortcuts/shortcuts.zoom_out - 'shortcut.zoom_out': Record - // redux/shortcuts/shortcuts.zoom_reset - 'shortcut.zoom_reset': Record // 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 */ diff --git a/src/main/data/PreferenceService.ts b/src/main/data/PreferenceService.ts index 74ed0e4b1f..5c6b9f6c14 100644 --- a/src/main/data/PreferenceService.ts +++ b/src/main/data/PreferenceService.ts @@ -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' diff --git a/src/main/data/migrate/dataRefactor/migrators/PreferencesMappings.ts b/src/main/data/migrate/dataRefactor/migrators/PreferencesMappings.ts index 7fd87325dd..2061b4cc07 100644 --- a/src/main/data/migrate/dataRefactor/migrators/PreferencesMappings.ts +++ b/src/main/data/migrate/dataRefactor/migrators/PreferencesMappings.ts @@ -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) diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 3f303dca71..4638f81250 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -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() diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index 5f5be2c723..d587b9b59e 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -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) diff --git a/src/main/services/SelectionService.ts b/src/main/services/SelectionService.ts index 91a8ef828e..cc193c8bff 100644 --- a/src/main/services/SelectionService.ts +++ b/src/main/services/SelectionService.ts @@ -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) diff --git a/src/main/services/ThemeService.ts b/src/main/services/ThemeService.ts index a56b559357..2ce090f0f6 100644 --- a/src/main/services/ThemeService.ts +++ b/src/main/services/ThemeService.ts @@ -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() diff --git a/src/preload/index.ts b/src/preload/index.ts index 2821702440..12ab55a591 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -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), diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 3395f55067..fb34a13a26 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -17,7 +17,7 @@ import Router from './Router' const logger = loggerService.withContext('App.tsx') -preferenceService.loadAll() +preferenceService.preloadAll() // 创建 React Query 客户端 const queryClient = new QueryClient({ diff --git a/src/renderer/src/Router.tsx b/src/renderer/src/Router.tsx index 8985a1a41d..92d3263f3c 100644 --- a/src/renderer/src/Router.tsx +++ b/src/renderer/src/Router.tsx @@ -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' diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx index 13d13c55a9..f9c364eb64 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx @@ -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' diff --git a/src/renderer/src/components/MinApp/MinApp.tsx b/src/renderer/src/components/MinApp/MinApp.tsx index c9b6a5b55b..b7de0e2553 100644 --- a/src/renderer/src/components/MinApp/MinApp.tsx +++ b/src/renderer/src/components/MinApp/MinApp.tsx @@ -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' diff --git a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx index 226598dc57..08ee6db52a 100644 --- a/src/renderer/src/components/MinApp/MinappPopupContainer.tsx +++ b/src/renderer/src/components/MinApp/MinappPopupContainer.tsx @@ -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' diff --git a/src/renderer/src/components/MinApp/WebviewContainer.tsx b/src/renderer/src/components/MinApp/WebviewContainer.tsx index 361bd39696..645f09dff3 100644 --- a/src/renderer/src/components/MinApp/WebviewContainer.tsx +++ b/src/renderer/src/components/MinApp/WebviewContainer.tsx @@ -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' diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index 66e62dca6f..c34580b742 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -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, diff --git a/src/renderer/src/components/app/Navbar.tsx b/src/renderer/src/components/app/Navbar.tsx index 0d0204eb59..1f0143bb46 100644 --- a/src/renderer/src/components/app/Navbar.tsx +++ b/src/renderer/src/components/app/Navbar.tsx @@ -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' diff --git a/src/renderer/src/components/app/PinnedMinapps.tsx b/src/renderer/src/components/app/PinnedMinapps.tsx index 213a1e163c..36ca6f9f7b 100644 --- a/src/renderer/src/components/app/PinnedMinapps.tsx +++ b/src/renderer/src/components/app/PinnedMinapps.tsx @@ -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' diff --git a/src/renderer/src/components/app/Sidebar.tsx b/src/renderer/src/components/app/Sidebar.tsx index 7b0069a025..1eacf6d480 100644 --- a/src/renderer/src/components/app/Sidebar.tsx +++ b/src/renderer/src/components/app/Sidebar.tsx @@ -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, diff --git a/src/renderer/src/context/CodeStyleProvider.tsx b/src/renderer/src/context/CodeStyleProvider.tsx index 1ef41d1eba..93214182de 100644 --- a/src/renderer/src/context/CodeStyleProvider.tsx +++ b/src/renderer/src/context/CodeStyleProvider.tsx @@ -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' diff --git a/src/renderer/src/context/ThemeProvider.tsx b/src/renderer/src/context/ThemeProvider.tsx index 71755a0dc2..6e5aca07e8 100644 --- a/src/renderer/src/context/ThemeProvider.tsx +++ b/src/renderer/src/context/ThemeProvider.tsx @@ -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 = ({ children }) => { // 用户设置的主题 - const { theme: settedTheme, setTheme: setSettedTheme } = useSettings() + // const { theme: settedTheme, setTheme: setSettedTheme } = useSettings() + + const [settedTheme, setSettedTheme] = usePreference('app.theme.mode') + const [actualTheme, setActualTheme] = useState( window.matchMedia('(prefers-color-scheme: dark)').matches ? ThemeMode.dark : ThemeMode.light ) @@ -56,18 +59,14 @@ export const ThemeProvider: React.FC = ({ 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 ( - + {children} ) diff --git a/src/renderer/src/data/PreferenceService.ts b/src/renderer/src/data/PreferenceService.ts index 30213d6452..050caad102 100644 --- a/src/renderer/src/data/PreferenceService.ts +++ b/src/renderer/src/data/PreferenceService.ts @@ -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 { + public async preloadAll(): Promise { 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 { + public async preload(keys: PreferenceKeyType[]): Promise { const uncachedKeys = keys.filter((key) => !this.isCached(key)) if (uncachedKeys.length > 0) { diff --git a/src/renderer/src/data/hooks/usePreference.ts b/src/renderer/src/data/hooks/usePreference.ts index de161a5a26..f8b8ba5461 100644 --- a/src/renderer/src/data/hooks/usePreference.ts +++ b/src/renderer/src/data/hooks/usePreference.ts @@ -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') diff --git a/src/renderer/src/hooks/useMermaid.ts b/src/renderer/src/hooks/useMermaid.ts index 1ef9b43069..5c382c5ed9 100644 --- a/src/renderer/src/hooks/useMermaid.ts +++ b/src/renderer/src/hooks/useMermaid.ts @@ -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 模块状态,单例模式 diff --git a/src/renderer/src/hooks/useNavbar.ts b/src/renderer/src/hooks/useNavbar.ts new file mode 100644 index 0000000000..845d911edd --- /dev/null +++ b/src/renderer/src/hooks/useNavbar.ts @@ -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) + } +} diff --git a/src/renderer/src/hooks/useSettings.ts b/src/renderer/src/hooks/useSettings.ts index c68873d435..553b230f98 100644 --- a/src/renderer/src/hooks/useSettings.ts +++ b/src/renderer/src/hooks/useSettings.ts @@ -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)) - } -} diff --git a/src/renderer/src/hooks/useUserTheme.ts b/src/renderer/src/hooks/useUserTheme.ts index 815903da73..57e4505e0a 100644 --- a/src/renderer/src/hooks/useUserTheme.ts +++ b/src/renderer/src/hooks/useUserTheme.ts @@ -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) } } diff --git a/src/renderer/src/pages/agents/AgentsPage.tsx b/src/renderer/src/pages/agents/AgentsPage.tsx index c0c9093e7c..16f1dcd4f3 100644 --- a/src/renderer/src/pages/agents/AgentsPage.tsx +++ b/src/renderer/src/pages/agents/AgentsPage.tsx @@ -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' diff --git a/src/renderer/src/pages/home/Chat.tsx b/src/renderer/src/pages/home/Chat.tsx index 1618d5e633..41106d938b 100644 --- a/src/renderer/src/pages/home/Chat.tsx +++ b/src/renderer/src/pages/home/Chat.tsx @@ -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' diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index a32eff2bb1..372bc5d149 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -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' diff --git a/src/renderer/src/pages/home/Messages/NewTopicButton.tsx b/src/renderer/src/pages/home/Messages/NewTopicButton.tsx index 350822053e..4c956dbddf 100644 --- a/src/renderer/src/pages/home/Messages/NewTopicButton.tsx +++ b/src/renderer/src/pages/home/Messages/NewTopicButton.tsx @@ -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' diff --git a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx index bbeb31985a..3731c5b33c 100644 --- a/src/renderer/src/pages/home/Tabs/SettingsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/SettingsTab.tsx @@ -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' diff --git a/src/renderer/src/pages/home/Tabs/index.tsx b/src/renderer/src/pages/home/Tabs/index.tsx index 4fba643703..843f734e70 100644 --- a/src/renderer/src/pages/home/Tabs/index.tsx +++ b/src/renderer/src/pages/home/Tabs/index.tsx @@ -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' diff --git a/src/renderer/src/pages/minapps/MinAppsPage.tsx b/src/renderer/src/pages/minapps/MinAppsPage.tsx index db82bf43a3..ac738595b4 100644 --- a/src/renderer/src/pages/minapps/MinAppsPage.tsx +++ b/src/renderer/src/pages/minapps/MinAppsPage.tsx @@ -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' diff --git a/src/renderer/src/pages/settings/AboutSettings.tsx b/src/renderer/src/pages/settings/AboutSettings.tsx index 72a545f4b7..303de662ba 100644 --- a/src/renderer/src/pages/settings/AboutSettings.tsx +++ b/src/renderer/src/pages/settings/AboutSettings.tsx @@ -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' diff --git a/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx b/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx index 2bedfbbd8b..60c87982a3 100644 --- a/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx +++ b/src/renderer/src/pages/settings/DisplaySettings/DisplaySettings.tsx @@ -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 || []) diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx index 06303e52eb..ede9583c2b 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/SelectionAssistantSettings.tsx @@ -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' diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsList.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsList.tsx index 7532232b4c..bab7711582 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsList.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsList.tsx @@ -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 } diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsListItem.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsListItem.tsx index 64f11f447e..608aafbe9a 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsListItem.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/ActionsListItem.tsx @@ -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 } diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionSearchModal.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionSearchModal.tsx index dbc6ef75fd..caacf02209 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionSearchModal.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionSearchModal.tsx @@ -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 = ({ diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionUserModal.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionUserModal.tsx index e1673e7cf4..c51da3fec4 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionUserModal.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionUserModal.tsx @@ -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 = ({ const { assistants: userPredefinedAssistants } = useAssistants() const { defaultAssistant } = useDefaultAssistant() - const [formData, setFormData] = useState>({}) - const [errors, setErrors] = useState>>({}) + const [formData, setFormData] = useState>({}) + const [errors, setErrors] = useState>>({}) useEffect(() => { if (isModalOpen) { @@ -46,7 +46,7 @@ const SelectionActionUserModal: FC = ({ }, [isModalOpen, editingAction]) const validateForm = (): boolean => { - const newErrors: Partial> = {} + const newErrors: Partial> = {} if (!formData.name?.trim()) { newErrors.name = t('selection.settings.user_modal.name.hint') @@ -66,7 +66,7 @@ const SelectionActionUserModal: FC = ({ } // 构建完整的 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 = ({ 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]) { diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionsList.tsx b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionsList.tsx index 3925dfbb5e..9323211e0a 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionsList.tsx +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/components/SelectionActionsList.tsx @@ -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 = ({ actionItems, setActionItems }) => { @@ -49,7 +49,7 @@ const SelectionActionsList: FC = ({ actionItems, setA const { theme } = useTheme() if (!actionItems || actionItems.length === 0) { - setActionItems(defaultActionItems) + setActionItems(DefaultPreferences.default['feature.selection.action_items']) } return ( diff --git a/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts b/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts index eaaa55806e..b825a3312f 100644 --- a/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts +++ b/src/renderer/src/pages/settings/SelectionAssistantSettings/hooks/useSettingsActionsList.ts @@ -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(null) + const [userEditingAction, setUserEditingAction] = useState(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]) } }) } diff --git a/src/renderer/src/pages/settings/SettingGroup.tsx b/src/renderer/src/pages/settings/SettingGroup.tsx index bd5efc70cc..813c87310e 100644 --- a/src/renderer/src/pages/settings/SettingGroup.tsx +++ b/src/renderer/src/pages/settings/SettingGroup.tsx @@ -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' diff --git a/src/renderer/src/pages/settings/index.tsx b/src/renderer/src/pages/settings/index.tsx index ae9697189b..ec2d70e1e4 100644 --- a/src/renderer/src/pages/settings/index.tsx +++ b/src/renderer/src/pages/settings/index.tsx @@ -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' diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index a481026547..faad488cf7 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -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, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index 05cbd3c9c3..30a3e29875 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -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 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] } /** diff --git a/src/renderer/src/store/selectionStore.ts b/src/renderer/src/store/selectionStore.ts index 0eaecb25fa..8ef85d1409 100644 --- a/src/renderer/src/store/selectionStore.ts +++ b/src/renderer/src/store/selectionStore.ts @@ -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) => { - state.selectionEnabled = action.payload - }, - setTriggerMode: (state, action: PayloadAction) => { - state.triggerMode = action.payload - }, - setIsCompact: (state, action: PayloadAction) => { - state.isCompact = action.payload - }, - setIsAutoClose: (state, action: PayloadAction) => { - state.isAutoClose = action.payload - }, - setIsAutoPin: (state, action: PayloadAction) => { - state.isAutoPin = action.payload - }, - setIsFollowToolbar: (state, action: PayloadAction) => { - state.isFollowToolbar = action.payload - }, - setIsRemeberWinSize: (state, action: PayloadAction) => { - state.isRemeberWinSize = action.payload - }, - setFilterMode: (state, action: PayloadAction) => { - state.filterMode = action.payload - }, - setFilterList: (state, action: PayloadAction) => { - state.filterList = action.payload - }, - setActionWindowOpacity: (state, action: PayloadAction) => { - state.actionWindowOpacity = action.payload - }, - setActionItems: (state, action: PayloadAction) => { - state.actionItems = action.payload - } - } -}) +// const selectionSlice = createSlice({ +// name: 'selectionStore', +// initialState, +// reducers: { +// setSelectionEnabled: (state, action: PayloadAction) => { +// state.selectionEnabled = action.payload +// }, +// setTriggerMode: (state, action: PayloadAction) => { +// state.triggerMode = action.payload +// }, +// setIsCompact: (state, action: PayloadAction) => { +// state.isCompact = action.payload +// }, +// setIsAutoClose: (state, action: PayloadAction) => { +// state.isAutoClose = action.payload +// }, +// setIsAutoPin: (state, action: PayloadAction) => { +// state.isAutoPin = action.payload +// }, +// setIsFollowToolbar: (state, action: PayloadAction) => { +// state.isFollowToolbar = action.payload +// }, +// setIsRemeberWinSize: (state, action: PayloadAction) => { +// state.isRemeberWinSize = action.payload +// }, +// setFilterMode: (state, action: PayloadAction) => { +// state.filterMode = action.payload +// }, +// setFilterList: (state, action: PayloadAction) => { +// state.filterList = action.payload +// }, +// setActionWindowOpacity: (state, action: PayloadAction) => { +// state.actionWindowOpacity = action.payload +// }, +// setActionItems: (state, action: PayloadAction) => { +// 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 diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 446a69ec7f..a74ff45bb8 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -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' diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index bbefd622f2..f0ce6b9897 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -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' diff --git a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx index 5c7488aca4..9fbecf351d 100644 --- a/src/renderer/src/windows/dataRefactorTest/TestApp.tsx +++ b/src/renderer/src/windows/dataRefactorTest/TestApp.tsx @@ -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 = () => { diff --git a/src/renderer/src/windows/mini/home/HomeWindow.tsx b/src/renderer/src/windows/mini/home/HomeWindow.tsx index a8df8a6387..43c4d22975 100644 --- a/src/renderer/src/windows/mini/home/HomeWindow.tsx +++ b/src/renderer/src/windows/mini/home/HomeWindow.tsx @@ -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' diff --git a/src/renderer/src/windows/selection/action/SelectionActionApp.tsx b/src/renderer/src/windows/selection/action/SelectionActionApp.tsx index 4d45fc26f5..1de48e9acb 100644 --- a/src/renderer/src/windows/selection/action/SelectionActionApp.tsx +++ b/src/renderer/src/windows/selection/action/SelectionActionApp.tsx @@ -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(null) diff --git a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx index 03a84472e5..0659db6273 100644 --- a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx +++ b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx @@ -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 = React.memo(({ action, scrollToBottom }) => { const { t } = useTranslation() - const { language } = useSettings() + const [language] = usePreference('app.language') const [error, setError] = useState(null) const [showOriginal, setShowOriginal] = useState(false) const [isContented, setIsContented] = useState(false) diff --git a/src/renderer/src/windows/selection/action/components/ActionTranslate.tsx b/src/renderer/src/windows/selection/action/components/ActionTranslate.tsx index 7dab7f4e2b..93c7160edf 100644 --- a/src/renderer/src/windows/selection/action/components/ActionTranslate.tsx +++ b/src/renderer/src/windows/selection/action/components/ActionTranslate.tsx @@ -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' diff --git a/src/renderer/src/windows/selection/action/entryPoint.tsx b/src/renderer/src/windows/selection/action/entryPoint.tsx index 4de3eab1b6..826e3eec5e 100644 --- a/src/renderer/src/windows/selection/action/entryPoint.tsx +++ b/src/renderer/src/windows/selection/action/entryPoint.tsx @@ -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 diff --git a/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx b/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx index 4d3571c253..59f5bd1204 100644 --- a/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx +++ b/src/renderer/src/windows/selection/toolbar/SelectionToolbar.tsx @@ -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) diff --git a/src/renderer/src/windows/selection/toolbar/entryPoint.tsx b/src/renderer/src/windows/selection/toolbar/entryPoint.tsx index 02d68607ee..05cffc199c 100644 --- a/src/renderer/src/windows/selection/toolbar/entryPoint.tsx +++ b/src/renderer/src/windows/selection/toolbar/entryPoint.tsx @@ -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 ( - - - - - - - + + + ) } diff --git a/yarn.lock b/yarn.lock index 730a922c16..40dd70bb9f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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: