adf877861d
Implement chapter templates, txt/md/docx import, submission export presets, and global inspiration shortcut. Split import handlers into a separate main bundle and fix EditorLayout setTarget loop that broke E2E. Co-authored-by: Cursor <cursoragent@cursor.com>
92 lines
3.1 KiB
TypeScript
92 lines
3.1 KiB
TypeScript
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,
|
|
knowledgeAutoExtract: true,
|
|
knowledgeExtractConfidenceThreshold: 0.8,
|
|
pomodoroDurationMinutes: 25,
|
|
enableSystemNotifications: false,
|
|
enableGoalNotifications: true,
|
|
chapterTemplates: [],
|
|
submissionPresets: [],
|
|
defaultSubmissionPresetId: 'builtin-webnovel'
|
|
}
|
|
}
|
|
|
|
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<GlobalSettings>
|
|
return {
|
|
...defaults(),
|
|
...raw,
|
|
updateSchedule: raw.updateSchedule ?? 'none',
|
|
stockBufferThreshold: raw.stockBufferThreshold ?? 3,
|
|
knowledgeAutoSuggest: raw.knowledgeAutoSuggest ?? true,
|
|
knowledgeSuggestTopN: raw.knowledgeSuggestTopN ?? 8,
|
|
knowledgeAutoExtract: raw.knowledgeAutoExtract ?? true,
|
|
knowledgeExtractConfidenceThreshold: raw.knowledgeExtractConfidenceThreshold ?? 0.8,
|
|
pomodoroDurationMinutes: raw.pomodoroDurationMinutes ?? 25,
|
|
enableSystemNotifications: raw.enableSystemNotifications ?? false,
|
|
enableGoalNotifications: raw.enableGoalNotifications ?? true,
|
|
dailyGoalNotifiedDate: raw.dailyGoalNotifiedDate,
|
|
chapterTemplates: raw.chapterTemplates ?? [],
|
|
submissionPresets: raw.submissionPresets ?? [],
|
|
defaultSubmissionPresetId: raw.defaultSubmissionPresetId ?? 'builtin-webnovel',
|
|
shortcuts: { ...DEFAULT_SHORTCUTS, ...raw.shortcuts },
|
|
aiConfig: { ...DEFAULT_AI_CONFIG, ...raw.aiConfig }
|
|
}
|
|
}
|
|
|
|
update(partial: Partial<GlobalSettings>): 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
|
|
}
|
|
}
|