feat: deliver P2 v0.2.0 with unified editor, search, and snapshots

Implement schema v2 migration, outline/setting/inspiration CRUD, unified TipTap editing, reference panel with mentions, FTS global search, chapter version history, writing landmarks, word frequency analysis, light theme contrast fixes, and full unit/E2E test coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-06 14:13:27 +08:00
parent 91c93954df
commit aac51bf183
72 changed files with 5790 additions and 203 deletions
+125 -1
View File
@@ -1,13 +1,24 @@
import type {
ActionId,
Bookmark,
BookMeta,
BookOpenResult,
Chapter,
CreateBookParams,
GlobalSettings,
InspirationEntry,
IpcResult,
LandmarkType,
OutlineItem,
OutlineStatus,
SearchResult,
SettingEntry,
SettingType,
Snapshot,
SnapshotType,
UpdateChapterParams,
Volume
Volume,
WordFreqResult
} from './types'
export interface ElectronAPI {
@@ -45,6 +56,119 @@ export interface ElectronAPI {
update: (params: UpdateChapterParams) => Promise<IpcResult<Chapter>>
delete: (bookId: string, chapterId: string) => Promise<IpcResult<void>>
}
outline: {
list: (bookId: string) => Promise<IpcResult<OutlineItem[]>>
create: (
bookId: string,
title: string,
parentId?: string | null,
sortOrder?: number
) => Promise<IpcResult<OutlineItem>>
update: (
bookId: string,
id: string,
patch: Partial<{
title: string
description: string
status: OutlineStatus
expectedWordCount: number | null
chapterId: string | null
sortOrder: number
}>
) => Promise<IpcResult<OutlineItem>>
delete: (bookId: string, id: string) => Promise<IpcResult<void>>
move: (
bookId: string,
id: string,
parentId: string | null,
sortOrder: number
) => Promise<IpcResult<OutlineItem>>
}
setting: {
list: (bookId: string, type?: SettingType) => Promise<IpcResult<SettingEntry[]>>
create: (bookId: string, type: SettingType, name: string) => Promise<IpcResult<SettingEntry>>
update: (
bookId: string,
id: string,
patch: Partial<{ name: string; description: string; type: SettingType; properties: Record<string, unknown> }>
) => Promise<IpcResult<SettingEntry>>
delete: (bookId: string, id: string) => Promise<IpcResult<void>>
syncRefs: (bookId: string, id: string, chapterIds: string[]) => Promise<IpcResult<SettingEntry>>
}
inspiration: {
list: (bookId: string) => Promise<IpcResult<InspirationEntry[]>>
create: (
bookId: string,
title?: string,
content?: string,
tags?: string[]
) => Promise<IpcResult<InspirationEntry>>
update: (
bookId: string,
id: string,
patch: Partial<{ title: string; content: string; tags: string[] }>
) => Promise<IpcResult<InspirationEntry>>
delete: (bookId: string, id: string) => Promise<IpcResult<void>>
convert: (
bookId: string,
id: string,
targetKind: 'outline' | 'setting',
settingType?: SettingType
) => Promise<IpcResult<OutlineItem | SettingEntry>>
}
snapshot: {
list: (bookId: string, chapterId: string) => Promise<IpcResult<Snapshot[]>>
create: (
bookId: string,
chapterId: string,
type: SnapshotType,
name?: string,
content?: string
) => Promise<IpcResult<Snapshot>>
restore: (bookId: string, chapterId: string, snapshotId: string) => Promise<IpcResult<Chapter>>
delete: (bookId: string, snapshotId: string) => Promise<IpcResult<void>>
startAutoSave: (bookId: string, chapterId: string) => Promise<IpcResult<void>>
stopAutoSave: (bookId: string, chapterId: string) => Promise<IpcResult<void>>
}
bookmark: {
list: (bookId: string, chapterId?: string, resolved?: boolean) => Promise<IpcResult<Bookmark[]>>
create: (
bookId: string,
chapterId: string,
position: number,
label: string,
landmarkType?: LandmarkType
) => Promise<IpcResult<Bookmark>>
update: (
bookId: string,
id: string,
patch: Partial<{ label: string; position: number; landmarkType: LandmarkType }>
) => Promise<IpcResult<Bookmark>>
resolve: (bookId: string, id: string, resolved: boolean) => Promise<IpcResult<Bookmark>>
delete: (bookId: string, id: string) => Promise<IpcResult<void>>
}
search: {
query: (
bookId: string,
query: string,
options?: { regex?: boolean; caseSensitive?: boolean; matchWholeWord?: boolean }
) => Promise<IpcResult<SearchResult[]>>
replace: (
bookId: string,
query: string,
replacement: string,
dryRun: boolean,
options?: { regex?: boolean; caseSensitive?: boolean }
) => Promise<IpcResult<{ count: number; previews: SearchResult[] }>>
getHistory: () => Promise<IpcResult<string[]>>
saveHistory: (query: string) => Promise<IpcResult<string[]>>
}
wordfreq: {
analyze: (
bookId: string,
scope: { kind: 'book' | 'volume' | 'chapter'; chapterId?: string; volumeId?: string }
) => Promise<IpcResult<WordFreqResult>>
}
shortcut: {
getAll: () => Promise<IpcResult<Record<ActionId, string>>>
register: (action: ActionId, accelerator: string) => Promise<IpcResult<void>>