import { BrowserWindow, ipcMain } from 'electron' import type { ConfigKeys, IConfig } from '@lib/types' import { CONFIG_KEYS, IPC_EVENTS } from '@lib/constants' import { debounce } from '@lib/utils' import logManager from '@electron/service/logger' const DEFAULT_CONFIG: IConfig = { [CONFIG_KEYS.THEME_MODE]: 'system', [CONFIG_KEYS.PRIMARY_COLOR]: '#BB5BE7', [CONFIG_KEYS.LANGUAGE]: 'zh', [CONFIG_KEYS.FONT_SIZE]: 14, [CONFIG_KEYS.MINIMIZE_TO_TRAY]: false, [CONFIG_KEYS.PROVIDER]: '', [CONFIG_KEYS.DEFAULT_MODEL]: null, [CONFIG_KEYS.SELECTED_CHANNELS]: [], [CONFIG_KEYS.IMAGE_CACHE]: [], } export class ConfigService { private static _instance: ConfigService; private _store: any; private _defaultConfig: IConfig = DEFAULT_CONFIG; private _listeners: Array<(config: IConfig) => void> = []; private constructor() { // store 通过 init() 异步初始化,避免 ESM 模块在 CJS 打包环境中同步导入的问题 } public async init() { if (this._store) return; const { default: Store } = await import('electron-store'); this._store = new Store({ name: 'config', defaults: DEFAULT_CONFIG, }); this._setupIpcEvents(); logManager.info('ConfigService initialized successfully.') } private _ensureStore() { if (!this._store) { throw new Error('ConfigService has not been initialized. Call init() first.'); } } private _setupIpcEvents() { const duration = 200; const handelUpdate = debounce((val) => this.update(val), duration); ipcMain.handle(IPC_EVENTS.GET_CONFIG, (_, key) => this.get(key)); ipcMain.handle(IPC_EVENTS.SET_CONFIG, (_, key, val) => { this.set(key, val); return { success: true }; }); ipcMain.on(IPC_EVENTS.UPDATE_CONFIG, (_, updates) => handelUpdate(updates)); } public static getInstance(): ConfigService { if (!this._instance) { this._instance = new ConfigService(); } return this._instance; } private _notifyListeners(): void { this._ensureStore(); BrowserWindow.getAllWindows().forEach(win => win.webContents.send(IPC_EVENTS.CONFIG_UPDATED, this._store.store)); this._listeners.forEach(listener => listener({ ...this._store.store })); } public getConfig(): IConfig { if (!this._store) { return { ...this._defaultConfig }; } return this._store.store; } public get(key: ConfigKeys): T { if (!this._store) { return this._defaultConfig[key] as T; } return this._store.get(key) as T } public set(key: ConfigKeys, value: unknown, autoSave: boolean = true): void { this._ensureStore(); const oldValue = this._store.get(key); if (oldValue === value) return; this._store.set(key, value); logManager.info(`Config set: ${key} = ${JSON.stringify(value)}`); logManager.info(`Config file path: ${this._store.path}`); autoSave && this._notifyListeners(); } public update(updates: Partial, autoSave: boolean = true): void { this._ensureStore(); (Object.keys(updates) as ConfigKeys[]).forEach((key) => { this._store.set(key, updates[key]); }); autoSave && this._notifyListeners(); } public resetToDefault(): void { this._ensureStore(); (Object.keys(this._defaultConfig) as ConfigKeys[]).forEach((key) => { this._store.set(key, this._defaultConfig[key]); }); logManager.info('Config reset to default.'); this._notifyListeners(); } public onConfigChange(listener: ((config: IConfig) => void)): () => void { this._listeners.push(listener); return () => this._listeners = this._listeners.filter(l => l !== listener); } } export const configManager = ConfigService.getInstance(); export default configManager;