feat(preferences): auto-generate preferences configuration from classification.json

This commit introduces an auto-generated preferences configuration file, replacing the previous manual definitions. The new structure is based on the latest classification.json and includes comprehensive settings for various application features. The auto-generation process ensures that the preferences are up-to-date and consistent with the defined classifications.
This commit is contained in:
fullex 2025-08-09 16:44:35 +08:00
parent 21e40db086
commit 4e3f8a8f76
6 changed files with 2055 additions and 18 deletions

View File

@ -1,35 +1,510 @@
/** /**
* we should use sorted object keys * Auto-generated preferences configuration
* use `eslint --fix` to auto sort keys * Generated at: 2025-08-09T07:25:38.982Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
* node .claude/data-classify/scripts/generate-preferences.js
*
* === AUTO-GENERATED CONTENT START ===
*/ */
/* eslint @typescript-eslint/member-ordering: ["error", { /* eslint @typescript-eslint/member-ordering: ["error", {
"interfaces": { "order": "alphabetically" }, "interfaces": { "order": "alphabetically" },
"typeLiterals": { "order": "alphabetically" } "typeLiterals": { "order": "alphabetically" }
}] */ }] */
export interface PreferencesType { export interface PreferencesType {
default: { default: {
'app.test5': { // redux/settings/enableDeveloperMode
content1: string 'app.developer_mode.enabled': boolean
content2: number // redux/settings/disableHardwareAcceleration
} 'app.disable_hardware_acceleration': boolean
'sys.a.test3': boolean // redux/settings/autoCheckUpdate
'sys.a.test4': string[] 'app.dist.auto_update.enabled': boolean
'ui.a.test1': string // redux/settings/testChannel
'ui.b.test2': number 'app.dist.test_plan.channel': string
// redux/settings/testPlan
'app.dist.test_plan.enabled': boolean
// electronStore/Language/Language
'app.language': unknown | null
// redux/settings/launchOnBoot
'app.launch_on_boot': boolean
// redux/settings/notification.assistant
'app.notification.assistant.enabled': boolean
// redux/settings/notification.backup
'app.notification.backup.enabled': boolean
// redux/settings/notification.knowledge
'app.notification.knowledge.enabled': boolean
// redux/settings/enableDataCollection
'app.privacy.data_collection.enabled': boolean
// redux/settings/proxyBypassRules
'app.proxy.bypass_rules': string | null
// redux/settings/proxyMode
'app.proxy.mode': string
// redux/settings/proxyUrl
'app.proxy.url': string | null
// redux/settings/enableSpellCheck
'app.spell_check.enabled': boolean
// redux/settings/spellCheckLanguages
'app.spell_check.languages': unknown[]
// redux/settings/theme
'app.theme.mode': string
// redux/settings/userTheme
'app.theme.user_defined': Record<string, unknown>
// redux/settings/windowStyle
'app.theme.window_style': string
// redux/settings/tray
'app.tray.enabled': boolean
// redux/settings/trayOnClose
'app.tray.on_close': boolean
// redux/settings/launchToTray
'app.tray.on_launch': boolean
// redux/settings/userId
'app.user.id': string
// redux/settings/userName
'app.user.name': string
// electronStore/ZoomFactor/ZoomFactor
'app.zoom_factor': unknown | null
// redux/settings/codeCollapsible
'chat.code.collapsible': boolean
// redux/settings/codeEditor.autocompletion
'chat.code.editor.autocompletion': boolean
// redux/settings/codeEditor.foldGutter
'chat.code.editor.fold_gutter': boolean
// redux/settings/codeEditor.enabled
'chat.code.editor.highlight_active_line': boolean
// redux/settings/codeEditor.keymap
'chat.code.editor.keymap': boolean
// redux/settings/codeEditor.themeDark
'chat.code.editor.theme_dark': string
// redux/settings/codeEditor.themeLight
'chat.code.editor.theme_light': string
// redux/settings/codeExecution.enabled
'chat.code.execution.enabled': boolean
// redux/settings/codeExecution.timeoutMinutes
'chat.code.execution.timeout_minutes': number
// redux/settings/codeImageTools
'chat.code.image_tools': boolean
// redux/settings/codePreview.themeDark
'chat.code.preview.theme_dark': string
// redux/settings/codePreview.themeLight
'chat.code.preview.theme_light': string
// redux/settings/codeShowLineNumbers
'chat.code.show_line_numbers': boolean
// redux/settings/codeViewer.themeDark
'chat.code.viewer.theme_dark': string
// redux/settings/codeViewer.themeLight
'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
'chat.input.paste_long_text_threshold': number
// redux/settings/enableQuickPanelTriggers
'chat.input.quick_panel.triggers_enabled': boolean
// redux/settings/sendMessageShortcut
'chat.input.send_message_shortcut': string
// redux/settings/showInputEstimatedTokens
'chat.input.show_estimated_tokens': boolean
// redux/settings/autoTranslateWithSpace
'chat.input.translate.auto_translate_with_space': boolean
// redux/settings/showTranslateConfirm
'chat.input.translate.show_confirm': boolean
// redux/settings/messageFont
'chat.message.font': string
// redux/settings/fontSize
'chat.message.font_size': number
// redux/settings/mathEngine
'chat.message.math_engine': string
// redux/settings/foldDisplayMode
'chat.message.multi_model.fold_display_mode': string
// redux/settings/gridColumns
'chat.message.multi_model.grid_columns': number
// redux/settings/gridPopoverTrigger
'chat.message.multi_model.grid_popover_trigger': string
// redux/settings/multiModelMessageStyle
'chat.message.multi_model.style': string
// redux/settings/messageNavigation
'chat.message.navigation_mode': string
// redux/settings/showMessageDivider
'chat.message.show_divider': boolean
// redux/settings/showPrompt
'chat.message.show_prompt': boolean
// redux/settings/showTokens
'chat.message.show_tokens': boolean
// redux/settings/messageStyle
'chat.message.style': string
// redux/settings/thoughtAutoCollapse
'chat.message.thought.auto_collapse': boolean
// redux/settings/narrowMode
'chat.narrow_mode': boolean
// redux/settings/skipBackupFile
'data.backup.general.skip_backup_file': boolean
// redux/settings/localBackupAutoSync
'data.backup.local.auto_sync': boolean
// redux/settings/localBackupDir
'data.backup.local.dir': string
// redux/settings/localBackupMaxBackups
'data.backup.local.max_backups': number
// redux/settings/localBackupSkipBackupFile
'data.backup.local.skip_backup_file': boolean
// redux/settings/localBackupSyncInterval
'data.backup.local.sync_interval': number
// redux/nutstore/nutstoreAutoSync
'data.backup.nutstore.auto_sync': boolean | null
// redux/nutstore/nutstorePath
'data.backup.nutstore.path': string | null
// redux/nutstore/nutstoreSkipBackupFile
'data.backup.nutstore.skip_backup_file': boolean | null
// redux/nutstore/nutstoreSyncInterval
'data.backup.nutstore.sync_interval': number | null
// redux/nutstore/nutstoreSyncState
'data.backup.nutstore.sync_state': Record<string, unknown> | null
// redux/nutstore/nutstoreToken
'data.backup.nutstore.token': string | null
// redux/settings/s3.accessKeyId
'data.backup.s3.access_key_id': string
// redux/settings/s3.autoSync
'data.backup.s3.auto_sync': boolean
// redux/settings/s3.bucket
'data.backup.s3.bucket': string
// redux/settings/s3.endpoint
'data.backup.s3.endpoint': string
// redux/settings/s3.maxBackups
'data.backup.s3.max_backups': number
// redux/settings/s3.region
'data.backup.s3.region': string
// redux/settings/s3.root
'data.backup.s3.root': string
// redux/settings/s3.secretAccessKey
'data.backup.s3.secret_access_key': string
// redux/settings/s3.skipBackupFile
'data.backup.s3.skip_backup_file': boolean
// redux/settings/s3.syncInterval
'data.backup.s3.sync_interval': number
// redux/settings/webdavAutoSync
'data.backup.webdav.auto_sync': boolean
// redux/settings/webdavDisableStream
'data.backup.webdav.disable_stream': boolean
// redux/settings/webdavHost
'data.backup.webdav.host': string
// redux/settings/webdavMaxBackups
'data.backup.webdav.max_backups': number
// redux/settings/webdavPass
'data.backup.webdav.pass': string
// redux/settings/webdavPath
'data.backup.webdav.path': string
// redux/settings/webdavSkipBackupFile
'data.backup.webdav.skip_backup_file': boolean
// redux/settings/webdavSyncInterval
'data.backup.webdav.sync_interval': number
// redux/settings/webdavUser
'data.backup.webdav.user': string
// redux/settings/excludeCitationsInExport
'data.export.markdown.exclude_citations': boolean
// redux/settings/forceDollarMathInMarkdown
'data.export.markdown.force_dollar_math': boolean
// redux/settings/markdownExportPath
'data.export.markdown.path': string | null
// redux/settings/showModelNameInMarkdown
'data.export.markdown.show_model_name': boolean
// redux/settings/showModelProviderInMarkdown
'data.export.markdown.show_model_provider': boolean
// redux/settings/standardizeCitationsInExport
'data.export.markdown.standardize_citations': boolean
// redux/settings/useTopicNamingForMessageTitle
'data.export.markdown.use_topic_naming_for_message_title': boolean
// redux/settings/exportMenuOptions.docx
'data.export.menus.docx': boolean
// redux/settings/exportMenuOptions.image
'data.export.menus.image': boolean
// redux/settings/exportMenuOptions.joplin
'data.export.menus.joplin': boolean
// redux/settings/exportMenuOptions.markdown
'data.export.menus.markdown': boolean
// redux/settings/exportMenuOptions.markdown_reason
'data.export.menus.markdown_reason': boolean
// redux/settings/exportMenuOptions.notion
'data.export.menus.notion': boolean
// redux/settings/exportMenuOptions.obsidian
'data.export.menus.obsidian': boolean
// redux/settings/exportMenuOptions.plain_text
'data.export.menus.plain_text': boolean
// redux/settings/exportMenuOptions.siyuan
'data.export.menus.siyuan': boolean
// redux/settings/exportMenuOptions.yuque
'data.export.menus.yuque': boolean
// redux/settings/joplinExportReasoning
'data.integration.joplin.export_reasoning': boolean
// redux/settings/joplinToken
'data.integration.joplin.token': string
// redux/settings/joplinUrl
'data.integration.joplin.url': string
// redux/settings/notionApiKey
'data.integration.notion.api_key': string
// redux/settings/notionDatabaseID
'data.integration.notion.database_id': string
// redux/settings/notionExportReasoning
'data.integration.notion.export_reasoning': boolean
// redux/settings/notionPageNameKey
'data.integration.notion.page_name_key': string
// redux/settings/defaultObsidianVault
'data.integration.obsidian.default_vault': string | null
// redux/settings/siyuanApiUrl
'data.integration.siyuan.api_url': string | null
// redux/settings/siyuanBoxId
'data.integration.siyuan.box_id': string | null
// redux/settings/siyuanRootPath
'data.integration.siyuan.root_path': string | null
// redux/settings/siyuanToken
'data.integration.siyuan.token': string | null
// redux/settings/yuqueRepoId
'data.integration.yuque.repo_id': string
// redux/settings/yuqueToken
'data.integration.yuque.token': string
// redux/settings/yuqueUrl
'data.integration.yuque.url': string
// redux/settings/apiServer.apiKey
'feature.csaas.api_key': string
// redux/settings/apiServer.enabled
'feature.csaas.enabled': boolean
// redux/settings/apiServer.host
'feature.csaas.host': string
// redux/settings/apiServer.port
'feature.csaas.port': number
// redux/settings/maxKeepAliveMinapps
'feature.minapp.max_keep_alive': number
// redux/settings/minappsOpenLinkExternal
'feature.minapp.open_link_external': boolean
// redux/settings/showOpenedMinappsInSidebar
'feature.minapp.show_opened_in_sidebar': boolean
// redux/settings/clickTrayToShowQuickAssistant
'feature.quick_assistant.click_tray_to_show': boolean
// redux/settings/enableQuickAssistant
'feature.quick_assistant.enabled': boolean
// redux/settings/readClipboardAtStartup
'feature.quick_assistant.read_clipboard_at_startup': boolean
// redux/selectionStore/actionItems
'feature.selection.action_items': unknown[]
// redux/selectionStore/actionWindowOpacity
'feature.selection.action_window_opacity': number
// redux/selectionStore/selectionEnabled
'feature.selection.enabled': boolean
// redux/selectionStore/filterList
'feature.selection.filter_list': unknown[]
// redux/selectionStore/filterMode
'feature.selection.filter_mode': string
// electronStore/SelectionAssistantFollowToolbar/SelectionAssistantFollowToolbar
'feature.selection.follow_toolbar': unknown | null
// redux/selectionStore/isAutoClose
'feature.selection.is_auto_close': boolean
// redux/selectionStore/isAutoPin
'feature.selection.is_auto_pin': boolean
// redux/selectionStore/isCompact
'feature.selection.is_compact': boolean
// redux/selectionStore/isFollowToolbar
'feature.selection.is_follow_toolbar': boolean
// redux/selectionStore/isRemeberWinSize
'feature.selection.is_remeber_win_size': boolean
// electronStore/SelectionAssistantRemeberWinSize/SelectionAssistantRemeberWinSize
'feature.selection.remember_win_size': unknown | null
// redux/selectionStore/triggerMode
'feature.selection.trigger_mode': string
// redux/settings/enableTopicNaming
'topic.naming.enabled': boolean
// redux/settings/topicNamingPrompt
'topic.naming.prompt': string
// redux/settings/pinTopicsToTop
'topic.pin_to_top': boolean
// redux/settings/topicPosition
'topic.position': string
// redux/settings/showTopicTime
'topic.show_time': boolean
// redux/settings/assistantIconType
'ui.assistant_icon_type': string
// redux/settings/clickAssistantToShowTopic
'ui.click_assistant_to_show_topic': boolean
// redux/settings/customCss
'ui.custom_css': string
// redux/settings/navbarPosition
'ui.navbar.position': string
} }
} }
/* eslint sort-keys: ["error", "asc", {"caseSensitive": true, "natural": false}] */ /* eslint sort-keys: ["error", "asc", {"caseSensitive": true, "natural": false}] */
export const defaultPreferences: PreferencesType = { export const defaultPreferences: PreferencesType = {
default: { default: {
'app.test5': { 'app.developer_mode.enabled': false,
content1: 'test5-1', 'app.disable_hardware_acceleration': false,
content2: 2 'app.dist.auto_update.enabled': true,
}, 'app.dist.test_plan.channel': 'UpgradeChannel.LATEST',
'sys.a.test3': true, 'app.dist.test_plan.enabled': false,
'sys.a.test4': ['test4-1', 'test4-2'], 'app.language': null,
'ui.a.test1': 'test1', 'app.launch_on_boot': false,
'ui.b.test2': 1 'app.notification.assistant.enabled': false,
'app.notification.backup.enabled': false,
'app.notification.knowledge.enabled': false,
'app.privacy.data_collection.enabled': false,
'app.proxy.bypass_rules': null,
'app.proxy.mode': 'system',
'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.window_style': 'opaque',
'app.tray.enabled': true,
'app.tray.on_close': true,
'app.tray.on_launch': false,
'app.user.id': 'uuid()',
'app.user.name': '',
'app.zoom_factor': null,
'chat.code.collapsible': false,
'chat.code.editor.autocompletion': true,
'chat.code.editor.fold_gutter': false,
'chat.code.editor.highlight_active_line': false,
'chat.code.editor.keymap': false,
'chat.code.editor.theme_dark': 'auto',
'chat.code.editor.theme_light': 'auto',
'chat.code.execution.enabled': false,
'chat.code.execution.timeout_minutes': 1,
'chat.code.image_tools': false,
'chat.code.preview.theme_dark': 'auto',
'chat.code.preview.theme_light': 'auto',
'chat.code.show_line_numbers': false,
'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,
'chat.input.send_message_shortcut': 'Enter',
'chat.input.show_estimated_tokens': false,
'chat.input.translate.auto_translate_with_space': false,
'chat.input.translate.show_confirm': true,
'chat.message.font': 'system',
'chat.message.font_size': 14,
'chat.message.math_engine': 'KaTeX',
'chat.message.multi_model.fold_display_mode': 'expanded',
'chat.message.multi_model.grid_columns': 2,
'chat.message.multi_model.grid_popover_trigger': 'click',
'chat.message.multi_model.style': 'horizontal',
'chat.message.navigation_mode': 'none',
'chat.message.show_divider': true,
'chat.message.show_prompt': true,
'chat.message.show_tokens': true,
'chat.message.style': 'plain',
'chat.message.thought.auto_collapse': true,
'chat.narrow_mode': false,
'data.backup.general.skip_backup_file': false,
'data.backup.local.auto_sync': false,
'data.backup.local.dir': '',
'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.s3.access_key_id': '',
'data.backup.s3.auto_sync': false,
'data.backup.s3.bucket': '',
'data.backup.s3.endpoint': '',
'data.backup.s3.max_backups': 0,
'data.backup.s3.region': '',
'data.backup.s3.root': '',
'data.backup.s3.secret_access_key': '',
'data.backup.s3.skip_backup_file': false,
'data.backup.s3.sync_interval': 0,
'data.backup.webdav.auto_sync': false,
'data.backup.webdav.disable_stream': false,
'data.backup.webdav.host': '',
'data.backup.webdav.max_backups': 0,
'data.backup.webdav.pass': '',
'data.backup.webdav.path': '/cherry-studio',
'data.backup.webdav.skip_backup_file': false,
'data.backup.webdav.sync_interval': 0,
'data.backup.webdav.user': '',
'data.export.markdown.exclude_citations': false,
'data.export.markdown.force_dollar_math': false,
'data.export.markdown.path': null,
'data.export.markdown.show_model_name': false,
'data.export.markdown.show_model_provider': false,
'data.export.markdown.standardize_citations': false,
'data.export.markdown.use_topic_naming_for_message_title': false,
'data.export.menus.docx': true,
'data.export.menus.image': true,
'data.export.menus.joplin': true,
'data.export.menus.markdown': true,
'data.export.menus.markdown_reason': true,
'data.export.menus.notion': true,
'data.export.menus.obsidian': true,
'data.export.menus.plain_text': true,
'data.export.menus.siyuan': true,
'data.export.menus.yuque': true,
'data.integration.joplin.export_reasoning': false,
'data.integration.joplin.token': '',
'data.integration.joplin.url': '',
'data.integration.notion.api_key': '',
'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.siyuan.api_url': null,
'data.integration.siyuan.box_id': null,
'data.integration.siyuan.root_path': null,
'data.integration.siyuan.token': null,
'data.integration.yuque.repo_id': '',
'data.integration.yuque.token': '',
'data.integration.yuque.url': '',
'feature.csaas.api_key': '`cs-sk-${uuid()}`',
'feature.csaas.enabled': false,
'feature.csaas.host': 'localhost',
'feature.csaas.port': 23333,
'feature.minapp.max_keep_alive': 3,
'feature.minapp.open_link_external': false,
'feature.minapp.show_opened_in_sidebar': true,
'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': [],
'feature.selection.action_window_opacity': 100,
'feature.selection.enabled': false,
'feature.selection.filter_list': [],
'feature.selection.filter_mode': 'default',
'feature.selection.follow_toolbar': null,
'feature.selection.is_auto_close': false,
'feature.selection.is_auto_pin': false,
'feature.selection.is_compact': false,
'feature.selection.is_follow_toolbar': true,
'feature.selection.is_remeber_win_size': false,
'feature.selection.remember_win_size': null,
'feature.selection.trigger_mode': 'selected',
'topic.naming.enabled': true,
'topic.naming.prompt': '',
'topic.pin_to_top': false,
'topic.position': 'left',
'topic.show_time': false,
'ui.assistant_icon_type': 'emoji',
'ui.click_assistant_to_show_topic': true,
'ui.custom_css': '',
'ui.navbar.position': 'top'
} }
} }
// === AUTO-GENERATED CONTENT END ===
/**
* :
* - 总配置项: 158
* - electronStore项: 4
* - redux项: 154
* - localStorage项: 0
*/

View File

@ -0,0 +1,152 @@
/**
* Auto-generated ElectronStore to Preferences migration
* Generated at: 2025-08-09T07:20:05.910Z
*
* === AUTO-GENERATED CONTENT START ===
*/
import dbService from '@data/db/DbService'
import { loggerService } from '@logger'
import { configManager } from '@main/services/ConfigManager'
import type { MigrationResult } from './index'
import { TypeConverter } from './utils/typeConverters'
const logger = loggerService.withContext('ElectronStoreMigrator')
// 键映射表
const KEY_MAPPINGS = [
{
originalKey: 'Language',
targetKey: 'app.language',
sourceCategory: 'Language',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'SelectionAssistantFollowToolbar',
targetKey: 'feature.selection.follow_toolbar',
sourceCategory: 'SelectionAssistantFollowToolbar',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'SelectionAssistantRemeberWinSize',
targetKey: 'feature.selection.remember_win_size',
sourceCategory: 'SelectionAssistantRemeberWinSize',
type: 'unknown',
defaultValue: null
},
{
originalKey: 'ZoomFactor',
targetKey: 'app.zoom_factor',
sourceCategory: 'ZoomFactor',
type: 'unknown',
defaultValue: null
}
] as const
export class ElectronStoreMigrator {
private typeConverter: TypeConverter
constructor() {
this.typeConverter = new TypeConverter()
}
/**
* ElectronStore到preferences的迁移
*/
async migrate(): Promise<MigrationResult> {
logger.info('开始ElectronStore迁移', { totalItems: KEY_MAPPINGS.length })
const result: MigrationResult = {
success: true,
migratedCount: 0,
errors: [],
source: 'electronStore'
}
for (const mapping of KEY_MAPPINGS) {
try {
await this.migrateItem(mapping)
result.migratedCount++
} catch (error) {
logger.error('迁移单项失败', { mapping, error })
result.errors.push({
key: mapping.originalKey,
error: error instanceof Error ? error.message : String(error)
})
result.success = false
}
}
logger.info('ElectronStore迁移完成', result)
return result
}
/**
*
*/
private async migrateItem(mapping: (typeof KEY_MAPPINGS)[0]): Promise<void> {
const { originalKey, targetKey, type, defaultValue } = mapping
// 从ElectronStore读取原始值
const originalValue = configManager.get(originalKey)
if (originalValue === undefined || originalValue === null) {
// 如果原始值不存在,使用默认值
if (defaultValue !== null && defaultValue !== undefined) {
const convertedValue = this.typeConverter.convert(defaultValue, type)
await dbService.setPreference('default', targetKey, convertedValue)
logger.debug('使用默认值迁移', { originalKey, targetKey, defaultValue: convertedValue })
}
return
}
// 类型转换
const convertedValue = this.typeConverter.convert(originalValue, type)
// 写入preferences表
await dbService.setPreference('default', targetKey, convertedValue)
logger.debug('成功迁移配置项', {
originalKey,
targetKey,
originalValue,
convertedValue
})
}
/**
*
*/
async validateMigration(): Promise<boolean> {
logger.info('开始验证ElectronStore迁移结果')
for (const mapping of KEY_MAPPINGS) {
const { targetKey } = mapping
try {
const value = await dbService.getPreference('default', targetKey)
if (value === null) {
logger.error('验证失败:配置项不存在', { targetKey })
return false
}
} catch (error) {
logger.error('验证失败:读取配置项错误', { targetKey, error })
return false
}
}
logger.info('ElectronStore迁移验证成功')
return true
}
}
// === AUTO-GENERATED CONTENT END ===
/**
* :
* - ElectronStore配置项: 4
* - 包含的原始键: Language, SelectionAssistantFollowToolbar, SelectionAssistantRemeberWinSize, ZoomFactor
*/

View File

@ -0,0 +1,132 @@
/**
* Auto-generated migration index
* Generated at: 2025-08-09T07:20:05.909Z
*
* This file is automatically generated from classification.json
* To update this file, modify classification.json and run:
* node .claude/data-classify/scripts/generate-migration.js
*
* === AUTO-GENERATED CONTENT START ===
*/
import { ElectronStoreMigrator } from './electronStoreToPreferences'
import { ReduxMigrator } from './reduxToPreferences'
import { loggerService } from '@logger'
const logger = loggerService.withContext('MigrationManager')
export interface MigrationResult {
success: boolean
migratedCount: number
errors: Array<{
key: string
error: string
}>
source: 'electronStore' | 'redux'
}
export interface MigrationSummary {
totalItems: number
successCount: number
errorCount: number
electronStore: MigrationResult
redux: MigrationResult
}
export class MigrationManager {
private electronStoreMigrator: ElectronStoreMigrator
private reduxMigrator: ReduxMigrator
constructor() {
this.electronStoreMigrator = new ElectronStoreMigrator()
this.reduxMigrator = new ReduxMigrator()
}
/**
* preferences迁移
* @returns
*/
async migrateAllPreferences(): Promise<MigrationSummary> {
logger.info('开始完整preferences迁移')
try {
// 并行执行两个迁移器
const [electronStoreResult, reduxResult] = await Promise.all([
this.electronStoreMigrator.migrate(),
this.reduxMigrator.migrate()
])
const summary: MigrationSummary = {
totalItems: 158,
successCount: electronStoreResult.migratedCount + reduxResult.migratedCount,
errorCount: electronStoreResult.errors.length + reduxResult.errors.length,
electronStore: electronStoreResult,
redux: reduxResult
}
if (summary.errorCount > 0) {
logger.warn('迁移完成但有错误', { summary })
} else {
logger.info('迁移完全成功', { summary })
}
return summary
} catch (error) {
logger.error('迁移过程中发生致命错误', error)
throw error
}
}
/**
*
* @param summary
* @returns
*/
async validateMigration(summary: MigrationSummary): Promise<boolean> {
logger.info('开始验证迁移结果')
// 基本验证:检查成功率
const successRate = summary.successCount / summary.totalItems
if (successRate < 0.95) { // 要求95%以上成功率
logger.error('迁移成功率过低', { successRate, summary })
return false
}
// 验证关键配置项是否存在
const criticalKeys = [
'app.theme.mode',
'app.language',
'app.user.id',
'feature.quick_assistant.enabled',
'chat.message.font_size'
]
try {
const dbServiceModule = await import('@main/db/DbService')
const dbService = dbServiceModule.default
for (const key of criticalKeys) {
const result = await dbService.getPreference('default', key)
if (result === null) {
logger.error('关键配置项迁移失败', { key })
return false
}
}
logger.info('迁移验证成功')
return true
} catch (error) {
logger.error('验证过程中发生错误', error)
return false
}
}
}
// === AUTO-GENERATED CONTENT END ===
/**
* :
* - 总迁移项: 158
* - ElectronStore项: 4
* - Redux项: 154
*/

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
/**
* Migration helper utilities
* Generated at: 2025-08-09T07:20:05.912Z
*/
import { loggerService } from '@logger'
const logger = loggerService.withContext('MigrationHelpers')
export interface BackupInfo {
timestamp: string
version: string
dataSize: number
backupPath: string
}
export class MigrationHelpers {
/**
*
*/
static async createBackup(): Promise<BackupInfo> {
logger.info('开始创建数据备份')
// 实现备份逻辑
const timestamp = new Date().toISOString()
const backupInfo: BackupInfo = {
timestamp,
version: process.env.npm_package_version || 'unknown',
dataSize: 0,
backupPath: ''
}
// TODO: 实现具体的备份逻辑
logger.info('数据备份完成', backupInfo)
return backupInfo
}
/**
*
*/
static async validateBackup(backupInfo: BackupInfo): Promise<boolean> {
logger.info('验证备份完整性', backupInfo)
// TODO: 实现备份验证逻辑
return true
}
/**
*
*/
static async restoreBackup(backupInfo: BackupInfo): Promise<boolean> {
logger.info('开始恢复备份', backupInfo)
// TODO: 实现备份恢复逻辑
logger.info('备份恢复完成')
return true
}
/**
*
*/
static async cleanup(): Promise<void> {
logger.info('清理迁移临时文件')
// TODO: 实现清理逻辑
logger.info('清理完成')
}
}

View File

@ -0,0 +1,125 @@
/**
* Type conversion utilities for migration
* Generated at: 2025-08-09T07:20:05.912Z
*/
import { loggerService } from '@logger'
const logger = loggerService.withContext('TypeConverter')
export class TypeConverter {
/**
*
*/
convert(value: any, targetType: string): any {
if (value === null || value === undefined) {
return null
}
try {
switch (targetType) {
case 'boolean':
return this.toBoolean(value)
case 'string':
return this.toString(value)
case 'number':
return this.toNumber(value)
case 'array':
case 'unknown[]':
return this.toArray(value)
case 'object':
case 'Record<string, unknown>':
return this.toObject(value)
default:
// 未知类型,保持原样
logger.debug('未知类型,保持原值', { targetType, value })
return value
}
} catch (error) {
logger.error('类型转换失败', { value, targetType, error })
return value
}
}
private toBoolean(value: any): boolean {
if (typeof value === 'boolean') {
return value
}
if (typeof value === 'string') {
const lower = value.toLowerCase()
return lower === 'true' || lower === '1' || lower === 'yes'
}
if (typeof value === 'number') {
return value !== 0
}
return Boolean(value)
}
private toString(value: any): string {
if (typeof value === 'string') {
return value
}
if (typeof value === 'number' || typeof value === 'boolean') {
return String(value)
}
if (typeof value === 'object') {
return JSON.stringify(value)
}
return String(value)
}
private toNumber(value: any): number {
if (typeof value === 'number') {
return value
}
if (typeof value === 'string') {
const parsed = parseFloat(value)
if (isNaN(parsed)) {
logger.warn('字符串无法转换为数字', { value })
return 0
}
return parsed
}
if (typeof value === 'boolean') {
return value ? 1 : 0
}
return 0
}
private toArray(value: any): any[] {
if (Array.isArray(value)) {
return value
}
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value)
return Array.isArray(parsed) ? parsed : [value]
} catch {
return [value]
}
}
return [value]
}
private toObject(value: any): Record<string, any> {
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return value
}
if (typeof value === 'string') {
try {
const parsed = JSON.parse(value)
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
? parsed
: { value }
} catch {
return { value }
}
}
return { value }
}
}