import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs' import { join } from 'path' import { DEFAULT_SHORTCUTS } from '../../shared/default-shortcuts' import { DEFAULT_AI_CONFIG, type GlobalSettings } from '../../shared/types' const FILE = 'global_settings.json' function defaultAiConfig() { return { ...DEFAULT_AI_CONFIG, baseUrl: process.env.BILIN_AI_BASE_URL ?? DEFAULT_AI_CONFIG.baseUrl, model: process.env.BILIN_AI_MODEL ?? DEFAULT_AI_CONFIG.model } } function defaults(): GlobalSettings { return { penName: '未命名作者', onboardingCompleted: false, theme: 'default', language: 'zh-CN', dailyWordGoal: 0, updateSchedule: 'none', stockBufferThreshold: 3, shortcuts: { ...DEFAULT_SHORTCUTS }, snapshotIntervalMs: 300_000, snapshotMaxAuto: 20, snapshotMaxPersist: 3, searchHistory: [], aiConfig: defaultAiConfig(), namingIgnoreWords: [], knowledgeAutoSuggest: true, knowledgeSuggestTopN: 8 } } export class GlobalSettingsService { constructor(private userDataDir: string) { if (!existsSync(userDataDir)) mkdirSync(userDataDir, { recursive: true }) } private filePath(): string { return join(this.userDataDir, FILE) } get(): GlobalSettings { if (!existsSync(this.filePath())) return defaults() const raw = JSON.parse(readFileSync(this.filePath(), 'utf-8')) as Partial return { ...defaults(), ...raw, updateSchedule: raw.updateSchedule ?? 'none', stockBufferThreshold: raw.stockBufferThreshold ?? 3, knowledgeAutoSuggest: raw.knowledgeAutoSuggest ?? true, knowledgeSuggestTopN: raw.knowledgeSuggestTopN ?? 8, shortcuts: { ...DEFAULT_SHORTCUTS, ...raw.shortcuts }, aiConfig: { ...DEFAULT_AI_CONFIG, ...raw.aiConfig } } } update(partial: Partial): GlobalSettings { const current = this.get() const next: GlobalSettings = { ...current, ...partial, shortcuts: partial.shortcuts ? { ...current.shortcuts, ...partial.shortcuts } : current.shortcuts, aiConfig: partial.aiConfig ? { ...current.aiConfig, ...partial.aiConfig } : current.aiConfig } writeFileSync(this.filePath(), JSON.stringify(next, null, 2), 'utf-8') return next } }