refactor: update UpgradeChannel handling and improve preference integration

- Commented out the UpgradeChannel enum in constant.ts and moved it to preferenceTypes.ts for better organization.
- Updated various components to utilize the new UpgradeChannel type from preferenceTypes, enhancing type safety and consistency.
- Refactored preference handling across multiple files to streamline the use of the usePreference hook, replacing previous settings references.
- Cleaned up unused code and comments related to previous settings for improved maintainability.
- Updated auto-generated preference mappings to reflect recent changes in preference structure.
This commit is contained in:
fullex 2025-09-03 14:31:15 +08:00
parent 1b57ffeb56
commit 68cd87e069
20 changed files with 241 additions and 262 deletions

View File

@ -197,11 +197,11 @@ export enum FeedUrl {
GITHUB_LATEST = 'https://github.com/CherryHQ/cherry-studio/releases/latest/download'
}
export enum UpgradeChannel {
LATEST = 'latest', // 最新稳定版本
RC = 'rc', // 公测版本
BETA = 'beta' // 预览版本
}
// export enum UpgradeChannel {
// LATEST = 'latest', // 最新稳定版本
// RC = 'rc', // 公测版本
// BETA = 'beta' // 预览版本
// }
export const defaultTimeout = 10 * 1000 * 60

View File

@ -59,3 +59,9 @@ export type AssistantIconType = 'model' | 'emoji' | 'none'
export type ProxyMode = 'system' | 'custom' | 'none'
export type MultiModelFoldDisplayMode = 'expanded' | 'compact'
export enum UpgradeChannel {
LATEST = 'latest', // 最新稳定版本
RC = 'rc', // 公测版本
BETA = 'beta' // 预览版本
}

View File

@ -1,6 +1,6 @@
/**
* Auto-generated preferences configuration
* Generated at: 2025-09-02T11:59:32.955Z
* Generated at: 2025-09-03T04:46:03.708Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
@ -22,7 +22,7 @@ import type {
SidebarIcon,
WindowStyle
} from '@shared/data/preferenceTypes'
import { LanguageVarious, ThemeMode } from '@shared/data/preferenceTypes'
import { LanguageVarious, ThemeMode, UpgradeChannel } from '@shared/data/preferenceTypes'
/* eslint @typescript-eslint/member-ordering: ["error", {
"interfaces": { "order": "alphabetically" },
@ -38,7 +38,7 @@ export interface PreferencesType {
// redux/settings/autoCheckUpdate
'app.dist.auto_update.enabled': boolean
// redux/settings/testChannel
'app.dist.test_plan.channel': string
'app.dist.test_plan.channel': UpgradeChannel
// redux/settings/testPlan
'app.dist.test_plan.enabled': boolean
// redux/settings/language
@ -180,17 +180,17 @@ export interface PreferencesType {
// redux/settings/localBackupSyncInterval
'data.backup.local.sync_interval': number
// redux/nutstore/nutstoreAutoSync
'data.backup.nutstore.auto_sync': boolean | null
'data.backup.nutstore.auto_sync': boolean
// redux/nutstore/nutstoreMaxBackups
'data.backup.nutstore.max_backups': number
// redux/nutstore/nutstorePath
'data.backup.nutstore.path': string | null
'data.backup.nutstore.path': string
// redux/nutstore/nutstoreSkipBackupFile
'data.backup.nutstore.skip_backup_file': boolean | null
'data.backup.nutstore.skip_backup_file': boolean
// redux/nutstore/nutstoreSyncInterval
'data.backup.nutstore.sync_interval': number | null
// redux/nutstore/nutstoreSyncState
'data.backup.nutstore.sync_state': Record<string, unknown> | null
'data.backup.nutstore.sync_interval': number
// redux/nutstore/nutstoreToken
'data.backup.nutstore.token': string | null
'data.backup.nutstore.token': string
// redux/settings/s3.accessKeyId
'data.backup.s3.access_key_id': string
// redux/settings/s3.autoSync
@ -278,7 +278,7 @@ export interface PreferencesType {
// redux/settings/notionPageNameKey
'data.integration.notion.page_name_key': string
// redux/settings/defaultObsidianVault
'data.integration.obsidian.default_vault': string | null
'data.integration.obsidian.default_vault': string
// redux/settings/siyuanApiUrl
'data.integration.siyuan.api_url': string | null
// redux/settings/siyuanBoxId
@ -408,7 +408,7 @@ export const DefaultPreferences: PreferencesType = {
'app.developer_mode.enabled': false,
'app.disable_hardware_acceleration': false,
'app.dist.auto_update.enabled': true,
'app.dist.test_plan.channel': 'UpgradeChannel.LATEST',
'app.dist.test_plan.channel': UpgradeChannel.LATEST,
'app.dist.test_plan.enabled': false,
'app.language': null,
'app.launch_on_boot': false,
@ -479,12 +479,12 @@ export const DefaultPreferences: PreferencesType = {
'data.backup.local.max_backups': 0,
'data.backup.local.skip_backup_file': false,
'data.backup.local.sync_interval': 0,
'data.backup.nutstore.auto_sync': null,
'data.backup.nutstore.path': null,
'data.backup.nutstore.skip_backup_file': null,
'data.backup.nutstore.sync_interval': null,
'data.backup.nutstore.sync_state': null,
'data.backup.nutstore.token': null,
'data.backup.nutstore.auto_sync': false,
'data.backup.nutstore.max_backups': 0,
'data.backup.nutstore.path': '/cherry-studio',
'data.backup.nutstore.skip_backup_file': false,
'data.backup.nutstore.sync_interval': 0,
'data.backup.nutstore.token': '',
'data.backup.s3.access_key_id': '',
'data.backup.s3.auto_sync': false,
'data.backup.s3.bucket': '',
@ -528,7 +528,7 @@ export const DefaultPreferences: PreferencesType = {
'data.integration.notion.database_id': '',
'data.integration.notion.export_reasoning': false,
'data.integration.notion.page_name_key': 'Name',
'data.integration.obsidian.default_vault': null,
'data.integration.obsidian.default_vault': '',
'data.integration.siyuan.api_url': null,
'data.integration.siyuan.box_id': null,
'data.integration.siyuan.root_path': null,

View File

@ -157,6 +157,8 @@ if (!app.requestSingleInstanceLock()) {
return
}
// DATA REFACTOR USE
// TODO: remove when data refactor is stable
//************FOR TESTING ONLY START****************/
await preferenceService.initialize()
@ -177,7 +179,7 @@ if (!app.requestSingleInstanceLock()) {
electronApp.setAppUserModelId(import.meta.env.VITE_MAIN_BUNDLE_ID || 'com.kangfenmao.CherryStudio')
// Mac: Hide dock icon before window creation when launch to tray is set
const isLaunchToTray = configManager.getLaunchToTray()
const isLaunchToTray = preferenceService.get('app.tray.on_launch')
if (isLaunchToTray) {
app.dock?.hide()
}

View File

@ -3,13 +3,15 @@ import { arch } from 'node:os'
import path from 'node:path'
import { PreferenceService } from '@data/PreferenceService'
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { isLinux, isMac, isPortable, isWin } from '@main/constant'
import { generateSignature } from '@main/integration/cherryin'
import { getBinaryPath, isBinaryExists, runInstallScript } from '@main/utils/process'
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 { MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH } from '@shared/config/constant'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { FileMetadata, Provider, Shortcut } from '@types'
import { BrowserWindow, dialog, ipcMain, ProxyConfig, session, shell, systemPreferences, webContents } from 'electron'
@ -159,40 +161,38 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
appService.setAppLaunchOnBoot(isLaunchOnBoot)
})
// launch to tray
ipcMain.handle(IpcChannel.App_SetLaunchToTray, (_, isActive: boolean) => {
configManager.setLaunchToTray(isActive)
})
// // launch to tray
// ipcMain.handle(IpcChannel.App_SetLaunchToTray, (_, isActive: boolean) => {
// configManager.setLaunchToTray(isActive)
// })
// tray
ipcMain.handle(IpcChannel.App_SetTray, (_, isActive: boolean) => {
configManager.setTray(isActive)
})
// // tray
// ipcMain.handle(IpcChannel.App_SetTray, (_, isActive: boolean) => {
// configManager.setTray(isActive)
// })
// to tray on close
ipcMain.handle(IpcChannel.App_SetTrayOnClose, (_, isActive: boolean) => {
configManager.setTrayOnClose(isActive)
})
// // to tray on close
// ipcMain.handle(IpcChannel.App_SetTrayOnClose, (_, isActive: boolean) => {
// configManager.setTrayOnClose(isActive)
// })
// auto update
ipcMain.handle(IpcChannel.App_SetAutoUpdate, (_, isActive: boolean) => {
appUpdater.setAutoUpdate(isActive)
configManager.setAutoUpdate(isActive)
})
// // auto update
// ipcMain.handle(IpcChannel.App_SetAutoUpdate, (_, isActive: boolean) => {
// appUpdater.setAutoUpdate(isActive)
// configManager.setAutoUpdate(isActive)
// })
ipcMain.handle(IpcChannel.App_SetTestPlan, async (_, isActive: boolean) => {
logger.info(`set test plan: ${isActive}`)
if (isActive !== configManager.getTestPlan()) {
if (isActive !== preferenceService.get('app.dist.test_plan.enabled')) {
appUpdater.cancelDownload()
configManager.setTestPlan(isActive)
}
})
ipcMain.handle(IpcChannel.App_SetTestChannel, async (_, channel: UpgradeChannel) => {
logger.info(`set test channel: ${channel}`)
if (channel !== configManager.getTestChannel()) {
if (channel !== preferenceService.get('app.dist.test_plan.channel')) {
appUpdater.cancelDownload()
configManager.setTestChannel(channel)
}
})

View File

@ -1,9 +1,11 @@
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { isWin } from '@main/constant'
import { getIpCountry } from '@main/utils/ipService'
import { getI18n } from '@main/utils/language'
import { generateUserAgent } from '@main/utils/systemInfo'
import { FeedUrl, UpgradeChannel } from '@shared/config/constant'
import { FeedUrl } from '@shared/config/constant'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import { CancellationToken, UpdateInfo } from 'builder-util-runtime'
import { app, BrowserWindow, dialog, net } from 'electron'
@ -12,7 +14,6 @@ import path from 'path'
import semver from 'semver'
import icon from '../../../build/icon.png?asset'
import { configManager } from './ConfigManager'
import { windowService } from './WindowService'
const logger = loggerService.withContext('AppUpdater')
@ -26,8 +27,8 @@ export default class AppUpdater {
constructor() {
autoUpdater.logger = logger as Logger
autoUpdater.forceDevUpdateConfig = !app.isPackaged
autoUpdater.autoDownload = configManager.getAutoUpdate()
autoUpdater.autoInstallOnAppQuit = configManager.getAutoUpdate()
autoUpdater.autoDownload = preferenceService.get('app.dist.auto_update.enabled')
autoUpdater.autoInstallOnAppQuit = preferenceService.get('app.dist.auto_update.enabled')
autoUpdater.requestHeaders = {
...autoUpdater.requestHeaders,
'User-Agent': generateUserAgent()
@ -139,7 +140,7 @@ export default class AppUpdater {
private _getTestChannel() {
const currentChannel = this._getChannelByVersion(app.getVersion())
const savedChannel = configManager.getTestChannel()
const savedChannel = preferenceService.get('app.dist.test_plan.channel')
if (currentChannel === UpgradeChannel.LATEST) {
return savedChannel || UpgradeChannel.RC
@ -164,7 +165,7 @@ export default class AppUpdater {
}
private async _setFeedUrl() {
const testPlan = configManager.getTestPlan()
const testPlan = preferenceService.get('app.dist.test_plan.enabled')
if (testPlan) {
const channel = this._getTestChannel()

View File

@ -1,4 +1,4 @@
import { UpgradeChannel, ZOOM_SHORTCUTS } from '@shared/config/constant'
import { ZOOM_SHORTCUTS } from '@shared/config/constant'
import { Shortcut } from '@types'
import Store from 'electron-store'
@ -52,29 +52,29 @@ export class ConfigManager {
// this.set(ConfigKeys.Theme, theme)
// }
getLaunchToTray(): boolean {
return !!this.get(ConfigKeys.LaunchToTray, false)
}
// getLaunchToTray(): boolean {
// return !!this.get(ConfigKeys.LaunchToTray, false)
// }
setLaunchToTray(value: boolean) {
this.set(ConfigKeys.LaunchToTray, value)
}
// setLaunchToTray(value: boolean) {
// this.set(ConfigKeys.LaunchToTray, value)
// }
getTray(): boolean {
return !!this.get(ConfigKeys.Tray, true)
}
// getTray(): boolean {
// return !!this.get(ConfigKeys.Tray, true)
// }
setTray(value: boolean) {
this.setAndNotify(ConfigKeys.Tray, value)
}
// setTray(value: boolean) {
// this.setAndNotify(ConfigKeys.Tray, value)
// }
getTrayOnClose(): boolean {
return !!this.get(ConfigKeys.TrayOnClose, true)
}
// getTrayOnClose(): boolean {
// return !!this.get(ConfigKeys.TrayOnClose, true)
// }
setTrayOnClose(value: boolean) {
this.set(ConfigKeys.TrayOnClose, value)
}
// setTrayOnClose(value: boolean) {
// this.set(ConfigKeys.TrayOnClose, value)
// }
getZoomFactor(): number {
return this.get<number>(ConfigKeys.ZoomFactor, 1)
@ -119,104 +119,104 @@ export class ConfigManager {
)
}
getClickTrayToShowQuickAssistant(): boolean {
return this.get<boolean>(ConfigKeys.ClickTrayToShowQuickAssistant, false)
}
// getClickTrayToShowQuickAssistant(): boolean {
// return this.get<boolean>(ConfigKeys.ClickTrayToShowQuickAssistant, false)
// }
setClickTrayToShowQuickAssistant(value: boolean) {
this.set(ConfigKeys.ClickTrayToShowQuickAssistant, value)
}
// setClickTrayToShowQuickAssistant(value: boolean) {
// this.set(ConfigKeys.ClickTrayToShowQuickAssistant, value)
// }
getEnableQuickAssistant(): boolean {
return this.get(ConfigKeys.EnableQuickAssistant, false)
}
// getEnableQuickAssistant(): boolean {
// return this.get(ConfigKeys.EnableQuickAssistant, false)
// }
setEnableQuickAssistant(value: boolean) {
this.setAndNotify(ConfigKeys.EnableQuickAssistant, value)
}
// setEnableQuickAssistant(value: boolean) {
// this.setAndNotify(ConfigKeys.EnableQuickAssistant, value)
// }
getAutoUpdate(): boolean {
return this.get<boolean>(ConfigKeys.AutoUpdate, true)
}
// getAutoUpdate(): boolean {
// return this.get<boolean>(ConfigKeys.AutoUpdate, true)
// }
setAutoUpdate(value: boolean) {
this.set(ConfigKeys.AutoUpdate, value)
}
// setAutoUpdate(value: boolean) {
// this.set(ConfigKeys.AutoUpdate, value)
// }
getTestPlan(): boolean {
return this.get<boolean>(ConfigKeys.TestPlan, false)
}
// getTestPlan(): boolean {
// return this.get<boolean>(ConfigKeys.TestPlan, false)
// }
setTestPlan(value: boolean) {
this.set(ConfigKeys.TestPlan, value)
}
// setTestPlan(value: boolean) {
// this.set(ConfigKeys.TestPlan, value)
// }
getTestChannel(): UpgradeChannel {
return this.get<UpgradeChannel>(ConfigKeys.TestChannel)
}
// getTestChannel(): UpgradeChannel {
// return this.get<UpgradeChannel>(ConfigKeys.TestChannel)
// }
setTestChannel(value: UpgradeChannel) {
this.set(ConfigKeys.TestChannel, value)
}
// setTestChannel(value: UpgradeChannel) {
// this.set(ConfigKeys.TestChannel, value)
// }
getEnableDataCollection(): boolean {
return this.get<boolean>(ConfigKeys.EnableDataCollection, true)
}
// getEnableDataCollection(): boolean {
// return this.get<boolean>(ConfigKeys.EnableDataCollection, true)
// }
setEnableDataCollection(value: boolean) {
this.set(ConfigKeys.EnableDataCollection, value)
}
// setEnableDataCollection(value: boolean) {
// this.set(ConfigKeys.EnableDataCollection, value)
// }
// Selection Assistant: is enabled the selection assistant
getSelectionAssistantEnabled(): boolean {
return this.get<boolean>(ConfigKeys.SelectionAssistantEnabled, false)
}
// // Selection Assistant: is enabled the selection assistant
// getSelectionAssistantEnabled(): boolean {
// return this.get<boolean>(ConfigKeys.SelectionAssistantEnabled, false)
// }
setSelectionAssistantEnabled(value: boolean) {
this.setAndNotify(ConfigKeys.SelectionAssistantEnabled, value)
}
// setSelectionAssistantEnabled(value: boolean) {
// this.setAndNotify(ConfigKeys.SelectionAssistantEnabled, value)
// }
// Selection Assistant: trigger mode (selected, ctrlkey)
getSelectionAssistantTriggerMode(): string {
return this.get<string>(ConfigKeys.SelectionAssistantTriggerMode, 'selected')
}
// // Selection Assistant: trigger mode (selected, ctrlkey)
// getSelectionAssistantTriggerMode(): string {
// return this.get<string>(ConfigKeys.SelectionAssistantTriggerMode, 'selected')
// }
setSelectionAssistantTriggerMode(value: string) {
this.setAndNotify(ConfigKeys.SelectionAssistantTriggerMode, value)
}
// setSelectionAssistantTriggerMode(value: string) {
// this.setAndNotify(ConfigKeys.SelectionAssistantTriggerMode, value)
// }
// Selection Assistant: if action window position follow toolbar
getSelectionAssistantFollowToolbar(): boolean {
return this.get<boolean>(ConfigKeys.SelectionAssistantFollowToolbar, true)
}
// // Selection Assistant: if action window position follow toolbar
// getSelectionAssistantFollowToolbar(): boolean {
// return this.get<boolean>(ConfigKeys.SelectionAssistantFollowToolbar, true)
// }
setSelectionAssistantFollowToolbar(value: boolean) {
this.setAndNotify(ConfigKeys.SelectionAssistantFollowToolbar, value)
}
// setSelectionAssistantFollowToolbar(value: boolean) {
// this.setAndNotify(ConfigKeys.SelectionAssistantFollowToolbar, value)
// }
getSelectionAssistantRemeberWinSize(): boolean {
return this.get<boolean>(ConfigKeys.SelectionAssistantRemeberWinSize, false)
}
// getSelectionAssistantRemeberWinSize(): boolean {
// return this.get<boolean>(ConfigKeys.SelectionAssistantRemeberWinSize, false)
// }
setSelectionAssistantRemeberWinSize(value: boolean) {
this.setAndNotify(ConfigKeys.SelectionAssistantRemeberWinSize, value)
}
// setSelectionAssistantRemeberWinSize(value: boolean) {
// this.setAndNotify(ConfigKeys.SelectionAssistantRemeberWinSize, value)
// }
getSelectionAssistantFilterMode(): string {
return this.get<string>(ConfigKeys.SelectionAssistantFilterMode, 'default')
}
// getSelectionAssistantFilterMode(): string {
// return this.get<string>(ConfigKeys.SelectionAssistantFilterMode, 'default')
// }
setSelectionAssistantFilterMode(value: string) {
this.setAndNotify(ConfigKeys.SelectionAssistantFilterMode, value)
}
// setSelectionAssistantFilterMode(value: string) {
// this.setAndNotify(ConfigKeys.SelectionAssistantFilterMode, value)
// }
getSelectionAssistantFilterList(): string[] {
return this.get<string[]>(ConfigKeys.SelectionAssistantFilterList, [])
}
// getSelectionAssistantFilterList(): string[] {
// return this.get<string[]>(ConfigKeys.SelectionAssistantFilterList, [])
// }
setSelectionAssistantFilterList(value: string[]) {
this.setAndNotify(ConfigKeys.SelectionAssistantFilterList, value)
}
// setSelectionAssistantFilterList(value: string[]) {
// this.setAndNotify(ConfigKeys.SelectionAssistantFilterList, value)
// }
getDisableHardwareAcceleration(): boolean {
return this.get<boolean>(ConfigKeys.DisableHardwareAcceleration, false)
@ -230,13 +230,13 @@ export class ConfigManager {
this.set(key, value, true)
}
getEnableDeveloperMode(): boolean {
return this.get<boolean>(ConfigKeys.EnableDeveloperMode, false)
}
// getEnableDeveloperMode(): boolean {
// return this.get<boolean>(ConfigKeys.EnableDeveloperMode, false)
// }
setEnableDeveloperMode(value: boolean) {
this.set(ConfigKeys.EnableDeveloperMode, value)
}
// setEnableDeveloperMode(value: boolean) {
// this.set(ConfigKeys.EnableDeveloperMode, value)
// }
set(key: string, value: unknown, isNotify: boolean = false) {
this.store.set(key, value)

View File

@ -1,3 +1,4 @@
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { handleZoomFactor } from '@main/utils/zoom'
import { Shortcut } from '@types'
@ -6,7 +7,6 @@ import { BrowserWindow, globalShortcut } from 'electron'
import { configManager } from './ConfigManager'
import selectionService from './SelectionService'
import { windowService } from './WindowService'
const logger = loggerService.withContext('ShortcutService')
let showAppAccelerator: string | null = null
@ -137,7 +137,7 @@ const convertShortcutFormat = (shortcut: string | string[]): string => {
export function registerShortcuts(window: BrowserWindow) {
if (isRegisterOnBoot) {
window.once('ready-to-show', () => {
if (configManager.getLaunchToTray()) {
if (preferenceService.get('app.tray.on_launch')) {
registerOnlyUniversalShortcuts()
}
})
@ -190,7 +190,7 @@ export function registerShortcuts(window: BrowserWindow) {
case 'mini_window':
//available only when QuickAssistant enabled
if (!configManager.getEnableQuickAssistant()) {
if (!preferenceService.get('feature.quick_assistant.enabled')) {
return
}
showMiniWindowAccelerator = formatShortcutKey(shortcut.shortcut)

View File

@ -1,3 +1,4 @@
import { preferenceService } from '@data/PreferenceService'
import { loggerService } from '@logger'
import { Attributes, convertSpanToSpanEntity, SpanEntity, TokenUsage, TraceCache } from '@mcp-trace/trace-core'
import { SpanStatusCode } from '@opentelemetry/api'
@ -6,8 +7,6 @@ import fs from 'fs/promises'
import * as os from 'os'
import * as path from 'path'
import { configManager } from './ConfigManager'
const logger = loggerService.withContext('SpanCacheService')
class SpanCacheService implements TraceCache {
@ -21,7 +20,7 @@ class SpanCacheService implements TraceCache {
}
createSpan: (span: ReadableSpan) => void = (span: ReadableSpan) => {
if (!configManager.getEnableDeveloperMode()) {
if (!preferenceService.get('app.developer_mode.enabled')) {
return
}
const spanEntity = convertSpanToSpanEntity(span)
@ -31,7 +30,7 @@ class SpanCacheService implements TraceCache {
}
endSpan: (span: ReadableSpan) => void = (span: ReadableSpan) => {
if (!configManager.getEnableDeveloperMode()) {
if (!preferenceService.get('app.developer_mode.enabled')) {
return
}
const spanId = span.spanContext().spanId
@ -87,7 +86,7 @@ class SpanCacheService implements TraceCache {
}
async saveSpans(topicId: string) {
if (!configManager.getEnableDeveloperMode()) {
if (!preferenceService.get('app.developer_mode.enabled')) {
return
}
let traceId: string | undefined
@ -138,7 +137,7 @@ class SpanCacheService implements TraceCache {
}
saveEntity(entity: SpanEntity) {
if (!configManager.getEnableDeveloperMode()) {
if (!preferenceService.get('app.developer_mode.enabled')) {
return
}
if (this.cache.has(entity.id)) {

View File

@ -1,6 +1,7 @@
// just import the themeService to ensure the theme is initialized
import './ThemeService'
import { preferenceService } from '@data/PreferenceService'
import { is } from '@electron-toolkit/utils'
import { loggerService } from '@logger'
import { isDev, isLinux, isMac, isWin } from '@main/constant'
@ -86,7 +87,7 @@ export class WindowService {
this.setupMainWindow(this.mainWindow, mainWindowState)
//preload miniWindow to resolve series of issues about miniWindow in Mac
const enableQuickAssistant = configManager.getEnableQuickAssistant()
const enableQuickAssistant = preferenceService.get('feature.quick_assistant.enabled')
if (enableQuickAssistant && !this.miniWindow) {
this.miniWindow = this.createMiniWindow(true)
}
@ -141,7 +142,7 @@ export class WindowService {
private setupMaximize(mainWindow: BrowserWindow, isMaximized: boolean) {
if (isMaximized) {
// 如果是从托盘启动,则需要延迟最大化,否则显示的就不是重启前的最大化窗口了
configManager.getLaunchToTray()
preferenceService.get('app.tray.on_launch')
? mainWindow.once('show', () => {
mainWindow.maximize()
})
@ -169,7 +170,7 @@ export class WindowService {
mainWindow.webContents.setZoomFactor(configManager.getZoomFactor())
// show window only when laucn to tray not set
const isLaunchToTray = configManager.getLaunchToTray()
const isLaunchToTray = preferenceService.get('app.tray.on_launch')
if (!isLaunchToTray) {
//[mac]hacky-fix: miniWindow set visibleOnFullScreen:true will cause dock icon disappeared
app.dock?.show()
@ -342,8 +343,8 @@ export class WindowService {
}
// 托盘及关闭行为设置
const isShowTray = configManager.getTray()
const isTrayOnClose = configManager.getTrayOnClose()
const isShowTray = preferenceService.get('app.tray.enabled')
const isTrayOnClose = preferenceService.get('app.tray.on_close')
// 没有开启托盘,或者开启了托盘,但设置了直接关闭,应执行直接退出
if (!isShowTray || (isShowTray && !isTrayOnClose)) {
@ -444,7 +445,7 @@ export class WindowService {
if (this.mainWindow && !this.mainWindow.isDestroyed() && this.mainWindow.isVisible()) {
if (this.mainWindow.isFocused()) {
// if tray is enabled, hide the main window, else do nothing
if (configManager.getTray()) {
if (preferenceService.get('app.tray.on_close')) {
this.mainWindow.hide()
app.dock?.hide()
}
@ -547,7 +548,7 @@ export class WindowService {
}
public showMiniWindow() {
const enableQuickAssistant = configManager.getEnableQuickAssistant()
const enableQuickAssistant = preferenceService.get('feature.quick_assistant.enabled')
if (!enableQuickAssistant) {
return

View File

@ -2,10 +2,10 @@ import type { ExtractChunkData } from '@cherrystudio/embedjs-interfaces'
import { electronAPI } from '@electron-toolkit/preload'
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 { FileChangeEvent } from '@shared/config/types'
import type { PreferenceDefaultScopeType, PreferenceKeyType, SelectionActionItem } from '@shared/data/preferenceTypes'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel'
import {
AddMemoryOptions,

View File

@ -10,7 +10,7 @@ import i18n from '@renderer/i18n'
import { handleSaveData, useAppDispatch } from '@renderer/store'
import { setUpdateState } from '@renderer/store/runtime'
import { runAsyncFunction } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { Avatar, Button, Progress, Radio, Row, Switch, Tag, Tooltip } from 'antd'
import { debounce } from 'lodash'

View File

@ -5,6 +5,7 @@ import {
LoadingOutlined,
YuqueOutlined
} from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import DividerWithText from '@renderer/components/DividerWithText'
import { NutstoreIcon } from '@renderer/components/Icons/NutstoreIcons'
import { HStack } from '@renderer/components/Layout'
@ -15,8 +16,6 @@ import { useTheme } from '@renderer/context/ThemeProvider'
import { useKnowledgeFiles } from '@renderer/hooks/useKnowledgeFiles'
import { useTimer } from '@renderer/hooks/useTimer'
import { reset } from '@renderer/services/BackupService'
import store, { useAppDispatch } from '@renderer/store'
import { setSkipBackupFile as _setSkipBackupFile } from '@renderer/store/settings'
import { AppInfo } from '@renderer/types'
import { formatFileSize } from '@renderer/utils'
import { occupiedDirs } from '@shared/config/constant'
@ -49,6 +48,8 @@ import WebDavSettings from './WebDavSettings'
import YuqueSettings from './YuqueSettings'
const DataSettings: FC = () => {
const [skipBackupFile, setSkipBackupFile] = usePreference('data.backup.general.skip_backup_file')
const { t } = useTranslation()
const [appInfo, setAppInfo] = useState<AppInfo>()
const [cacheSize, setCacheSize] = useState<string>('')
@ -57,11 +58,6 @@ const DataSettings: FC = () => {
const [menu, setMenu] = useState<string>('data')
const { setTimeoutTimer } = useTimer()
const _skipBackupFile = store.getState().settings.skipBackupFile
const [skipBackupFile, setSkipBackupFile] = useState<boolean>(_skipBackupFile)
const dispatch = useAppDispatch()
//joplin icon needs to be updated into iconfont
const JoplinIcon = () => (
<svg viewBox="0 0 24 24" width="16" height="16" fill="var(--color-icon)" xmlns="http://www.w3.org/2000/svg">
@ -578,7 +574,6 @@ const DataSettings: FC = () => {
const onSkipBackupFilesChange = (value: boolean) => {
setSkipBackupFile(value)
dispatch(_setSkipBackupFile(value))
}
return (

View File

@ -1,4 +1,5 @@
import { CheckOutlined, FolderOutlined, LoadingOutlined, SyncOutlined, WarningOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import { HStack } from '@renderer/components/Layout'
import NutstorePathPopup from '@renderer/components/Popups/NutsorePathPopup'
import Selector from '@renderer/components/Selector'
@ -15,15 +16,7 @@ import {
startNutstoreAutoSync,
stopNutstoreAutoSync
} from '@renderer/services/NutstoreService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
setNutstoreAutoSync,
setNutstoreMaxBackups,
setNutstorePath,
setNutstoreSkipBackupFile,
setNutstoreSyncInterval,
setNutstoreToken
} from '@renderer/store/nutstore'
import { useAppSelector } from '@renderer/store'
import { modalConfirm } from '@renderer/utils'
import { NUTSTORE_HOST } from '@shared/config/nutstore'
import { Button, Input, Switch, Tooltip, Typography } from 'antd'
@ -37,25 +30,24 @@ import { SettingDivider, SettingGroup, SettingHelpText, SettingRow, SettingRowTi
const NutstoreSettings: FC = () => {
const { theme } = useTheme()
const { t } = useTranslation()
const {
nutstoreToken,
nutstorePath,
nutstoreSyncInterval,
nutstoreAutoSync,
nutstoreSyncState,
nutstoreSkipBackupFile,
nutstoreMaxBackups
} = useAppSelector((state) => state.nutstore)
const { nutstoreSyncState } = useAppSelector((state) => state.nutstore)
const dispatch = useAppDispatch()
const [nutstoreAutoSync, setNutstoreAutoSync] = usePreference('data.backup.nutstore.auto_sync')
const [nutstoreMaxBackups, setNutstoreMaxBackups] = usePreference('data.backup.nutstore.max_backups')
const [nutstorePath, setNutstorePath] = usePreference('data.backup.nutstore.path')
const [nutstoreSkipBackupFile, setNutstoreSkipBackupFile] = usePreference('data.backup.nutstore.skip_backup_file')
const [nutstoreSyncInterval, setNutstoreSyncInterval] = usePreference('data.backup.nutstore.sync_interval')
const [nutstoreToken, setNutstoreToken] = usePreference('data.backup.nutstore.token')
const [nutstoreUsername, setNutstoreUsername] = useState<string | undefined>(undefined)
const [nutstorePass, setNutstorePass] = useState<string | undefined>(undefined)
const [storagePath, setStoragePath] = useState<string | undefined>(nutstorePath)
// const [storagePath, setStoragePath] = useState<string | undefined>(nutstorePath)
const [checkConnectionLoading, setCheckConnectionLoading] = useState(false)
const [nsConnected, setNsConnected] = useState<boolean>(false)
const [syncInterval, setSyncInterval] = useState<number>(nutstoreSyncInterval)
const [nutSkipBackupFile, setNutSkipBackupFile] = useState<boolean>(nutstoreSkipBackupFile)
// const [syncInterval, setSyncInterval] = useState<number>(nutstoreSyncInterval)
// const [nutSkipBackupFile, setNutSkipBackupFile] = useState<boolean>(nutstoreSkipBackupFile)
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
const nutstoreSSOHandler = useNutstoreSSO()
@ -66,8 +58,8 @@ const NutstoreSettings: FC = () => {
window.open(ssoUrl, '_blank')
const nutstoreToken = await nutstoreSSOHandler()
dispatch(setNutstoreToken(nutstoreToken))
}, [dispatch, nutstoreSSOHandler])
setNutstoreToken(nutstoreToken)
}, [nutstoreSSOHandler, setNutstoreToken])
useEffect(() => {
async function decryptTokenEffect() {
@ -78,14 +70,14 @@ const NutstoreSettings: FC = () => {
setNutstoreUsername(decrypted.username)
setNutstorePass(decrypted.access_token)
if (!nutstorePath) {
dispatch(setNutstorePath('/cherry-studio'))
setStoragePath('/cherry-studio')
setNutstorePath('/cherry-studio')
// setStoragePath('/cherry-studio')
}
}
}
}
decryptTokenEffect()
}, [nutstoreToken, dispatch, nutstorePath])
}, [nutstoreToken, setNutstorePath, nutstorePath])
const handleLayout = useCallback(async () => {
const confirmedLogout = await modalConfirm({
@ -93,13 +85,12 @@ const NutstoreSettings: FC = () => {
content: t('settings.data.nutstore.logout.content')
})
if (confirmedLogout) {
dispatch(setNutstoreToken(''))
dispatch(setNutstorePath(''))
dispatch(setNutstoreAutoSync(false))
setNutstoreToken('')
setNutstorePath('')
setNutstoreAutoSync(false)
setNutstoreUsername('')
setStoragePath(undefined)
}
}, [dispatch, t])
}, [setNutstorePath, setNutstoreToken, setNutstoreAutoSync, t])
const handleCheckConnection = async () => {
if (!nutstoreToken) return
@ -126,25 +117,23 @@ const NutstoreSettings: FC = () => {
backupMethod: backupToNutstore
})
const onSyncIntervalChange = (value: number) => {
setSyncInterval(value)
dispatch(setNutstoreSyncInterval(value))
const onSyncIntervalChange = async (value: number) => {
await setNutstoreSyncInterval(value)
if (value === 0) {
dispatch(setNutstoreAutoSync(false))
await setNutstoreAutoSync(false)
stopNutstoreAutoSync()
} else {
dispatch(setNutstoreAutoSync(true))
await setNutstoreAutoSync(true)
startNutstoreAutoSync()
}
}
const onSkipBackupFilesChange = (value: boolean) => {
setNutSkipBackupFile(value)
dispatch(setNutstoreSkipBackupFile(value))
setNutstoreSkipBackupFile(value)
}
const onMaxBackupsChange = (value: number) => {
dispatch(setNutstoreMaxBackups(value))
setNutstoreMaxBackups(value)
}
const handleClickPathChange = async () => {
@ -174,8 +163,7 @@ const NutstoreSettings: FC = () => {
return
}
setStoragePath(targetPath)
dispatch(setNutstorePath(targetPath))
setNutstorePath(targetPath)
}
const renderSyncStatus = () => {
@ -260,8 +248,7 @@ const NutstoreSettings: FC = () => {
style={{ width: 250 }}
value={nutstorePath}
onChange={(e) => {
setStoragePath(e.target.value)
dispatch(setNutstorePath(e.target.value))
setNutstorePath(e.target.value)
}}
/>
<Button type="default" onClick={handleClickPathChange}>
@ -286,7 +273,7 @@ const NutstoreSettings: FC = () => {
<SettingRowTitle>{t('settings.data.webdav.autoSync.label')}</SettingRowTitle>
<Selector
size={14}
value={syncInterval}
value={nutstoreSyncInterval}
onChange={onSyncIntervalChange}
options={[
{ label: t('settings.data.webdav.autoSync.off'), value: 0 },
@ -302,7 +289,7 @@ const NutstoreSettings: FC = () => {
]}
/>
</SettingRow>
{nutstoreAutoSync && syncInterval > 0 && (
{nutstoreAutoSync && nutstoreSyncInterval > 0 && (
<>
<SettingDivider />
<SettingRow>
@ -333,7 +320,7 @@ const NutstoreSettings: FC = () => {
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.backup.skip_file_data_title')}</SettingRowTitle>
<Switch checked={nutSkipBackupFile} onChange={onSkipBackupFilesChange} />
<Switch checked={nutstoreSkipBackupFile} onChange={onSkipBackupFilesChange} />
</SettingRow>
<SettingRow>
<SettingHelpText>{t('settings.data.backup.skip_file_data_help')}</SettingHelpText>
@ -361,7 +348,7 @@ const NutstoreSettings: FC = () => {
webdavHost: NUTSTORE_HOST,
webdavUser: nutstoreUsername,
webdavPass: nutstorePass,
webdavPath: storagePath
webdavPath: nutstorePath
}}
restoreMethod={restoreFromNutstore}
customLabels={{

View File

@ -1,8 +1,6 @@
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import { HStack } from '@renderer/components/Layout'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'
import { setDefaultObsidianVault } from '@renderer/store/settings'
import { Empty, Select, Spin } from 'antd'
import { FC, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -15,8 +13,8 @@ const { Option } = Select
const ObsidianSettings: FC = () => {
const { t } = useTranslation()
const { defaultObsidianVault } = useSettings()
const dispatch = useAppDispatch()
const [defaultObsidianVault, setDefaultObsidianVault] = usePreference('data.integration.obsidian.default_vault')
const [vaults, setVaults] = useState<Array<{ path: string; name: string }>>([])
const [loading, setLoading] = useState<boolean>(false)
@ -40,7 +38,7 @@ const ObsidianSettings: FC = () => {
// 如果没有设置默认vault则选择第一个
if (!defaultObsidianVault && vaultsData.length > 0) {
dispatch(setDefaultObsidianVault(vaultsData[0].name))
setDefaultObsidianVault(vaultsData[0].name)
}
} catch (error) {
logger.error('获取Obsidian Vault失败:', error as Error)
@ -51,10 +49,10 @@ const ObsidianSettings: FC = () => {
}
fetchVaults()
}, [dispatch, defaultObsidianVault, t])
}, [defaultObsidianVault, setDefaultObsidianVault, t])
const handleChange = (value: string) => {
dispatch(setDefaultObsidianVault(value))
setDefaultObsidianVault(value)
}
return (

View File

@ -8,8 +8,6 @@ import {
DropResult
} from '@hello-pangea/dnd'
import { getSidebarIconLabel } from '@renderer/i18n/label'
// import { useAppDispatch } from '@renderer/store'
// import { setSidebarIcons } from '@renderer/store/settings'
import { SidebarIcon } from '@shared/data/preferenceTypes'
import { message } from 'antd'
import {
@ -42,8 +40,6 @@ const SidebarIconsManager: FC<SidebarIconsManagerProps> = ({
}) => {
const { t } = useTranslation()
// const dispatch = useAppDispatch()
const onDragEnd = useCallback(
(result: DropResult) => {
if (!result.destination) return

View File

@ -1,4 +1,5 @@
import { RedoOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import InfoTooltip from '@renderer/components/InfoTooltip'
import { HStack } from '@renderer/components/Layout'
import ModelSelector from '@renderer/components/ModelSelector'
@ -6,10 +7,7 @@ import { isEmbeddingModel, isRerankModel, isTextToImageModel } from '@renderer/c
import { useTheme } from '@renderer/context/ThemeProvider'
import { useDefaultModel } from '@renderer/hooks/useAssistant'
import { useProviders } from '@renderer/hooks/useProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { getModelUniqId, hasModel } from '@renderer/services/ModelService'
import { useAppDispatch } from '@renderer/store'
import { setTranslateModelPrompt } from '@renderer/store/settings'
import { Model } from '@renderer/types'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { Button, Tooltip } from 'antd'
@ -30,9 +28,8 @@ const ModelSettings: FC = () => {
const allModels = providers.map((p) => p.models).flat()
const { theme } = useTheme()
const { t } = useTranslation()
const { translateModelPrompt } = useSettings()
const dispatch = useAppDispatch()
const [translateModelPrompt, setTranslateModelPrompt] = usePreference('feature.translate.model_prompt')
const modelPredicate = useCallback(
(m: Model) => !isEmbeddingModel(m) && !isRerankModel(m) && !isTextToImageModel(m),
@ -52,7 +49,7 @@ const ModelSettings: FC = () => {
)
const onResetTranslatePrompt = () => {
dispatch(setTranslateModelPrompt(TRANSLATE_PROMPT))
setTranslateModelPrompt(TRANSLATE_PROMPT)
}
return (

View File

@ -1,9 +1,7 @@
import { RedoOutlined } from '@ant-design/icons'
import { usePreference } from '@data/hooks/usePreference'
import { HStack } from '@renderer/components/Layout'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { useAppDispatch } from '@renderer/store'
import { setTranslateModelPrompt } from '@renderer/store/settings'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { Input, Tooltip } from 'antd'
import { useState } from 'react'
@ -15,15 +13,13 @@ import { SettingGroup, SettingTitle } from '..'
const TranslatePromptSettings = () => {
const { t } = useTranslation()
const { theme } = useTheme()
const { translateModelPrompt } = useSettings()
const [translateModelPrompt, setTranslateModelPrompt] = usePreference('feature.translate.model_prompt')
const [localPrompt, setLocalPrompt] = useState(translateModelPrompt)
const dispatch = useAppDispatch()
const onResetTranslatePrompt = () => {
setLocalPrompt(TRANSLATE_PROMPT)
dispatch(setTranslateModelPrompt(TRANSLATE_PROMPT))
setTranslateModelPrompt(TRANSLATE_PROMPT)
}
return (
@ -43,7 +39,7 @@ const TranslatePromptSettings = () => {
<Input.TextArea
value={localPrompt}
onChange={(e) => setLocalPrompt(e.target.value)}
onBlur={(e) => dispatch(setTranslateModelPrompt(e.target.value))}
onBlur={(e) => setTranslateModelPrompt(e.target.value)}
autoSize={{ minRows: 4, maxRows: 10 }}
placeholder={t('settings.models.translate_model_prompt_message')}
/>

View File

@ -31,9 +31,10 @@ import {
WebSearchProvider
} from '@renderer/types'
import { getDefaultGroupName, getLeadingEmoji, runAsyncFunction, uuid } from '@renderer/utils'
import { defaultByPassRules, UpgradeChannel } from '@shared/config/constant'
import { defaultByPassRules } from '@shared/config/constant'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { DefaultPreferences } from '@shared/data/preferences'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { isEmpty } from 'lodash'
import { createMigrate } from 'redux-persist'

View File

@ -11,7 +11,6 @@ import {
TranslateLanguageCode
} from '@renderer/types'
import { uuid } from '@renderer/utils'
import { UpgradeChannel } from '@shared/config/constant'
import { TRANSLATE_PROMPT } from '@shared/config/prompts'
import { DefaultPreferences } from '@shared/data/preferences'
import type {
@ -21,6 +20,7 @@ import type {
SendMessageShortcut,
SidebarIcon
} from '@shared/data/preferenceTypes'
import { UpgradeChannel } from '@shared/data/preferenceTypes'
import { ThemeMode } from '@shared/data/preferenceTypes'
import { OpenAIVerbosity } from '@types'
@ -899,7 +899,7 @@ export const {
// setTestChannel,
// setRenderInputMessageAsMarkdown,
// setClickAssistantToShowTopic,
setSkipBackupFile,
// setSkipBackupFile,
// setWebdavHost,
// setWebdavUser,
// setWebdavPass,
@ -922,7 +922,7 @@ export const {
// setGridColumns,
// setGridPopoverTrigger,
// setMessageStyle,
setTranslateModelPrompt,
// setTranslateModelPrompt,
// setAutoTranslateWithSpace,
// setShowTranslateConfirm,
// setEnableTopicNaming,
@ -951,8 +951,8 @@ export const {
// setJoplinToken,
// setJoplinUrl,
// setJoplinExportReasoning,
setMessageNavigation,
setDefaultObsidianVault,
// setMessageNavigation,
// setDefaultObsidianVault,
setDefaultAgent,
// setSiyuanApiUrl,
// setSiyuanToken,
@ -986,9 +986,9 @@ export const {
// setNavbarPosition,
// setShowMessageOutline,
// API Server actions
setApiServerEnabled,
setApiServerPort,
setApiServerApiKey,
// setApiServerEnabled,
// setApiServerPort,
// setApiServerApiKey,
// setShowWorkspace,
toggleShowWorkspace
} = settingsSlice.actions