feat: ship v1.2.0 Wave 1 foundation with book settings, chapter management, and focus mode
Deliver schema v9, book/chapter IPC extensions, BookSettingsTab, chapter meta/tags, AI session menu, focus mode and TTS, template context enrich, and Wave 1 E2E coverage. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -62,7 +62,7 @@
|
||||
- Modify: `src/shared/types.ts`
|
||||
- Modify: `src/shared/ipc-channels.ts`
|
||||
|
||||
- [ ] **Step 1: schema-v9.sql**
|
||||
- [x] **Step 1: schema-v9.sql**
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS chapter_tags (
|
||||
@@ -74,7 +74,7 @@ CREATE TABLE IF NOT EXISTS chapter_tags (
|
||||
);
|
||||
```
|
||||
|
||||
- [ ] **Step 2: migrate.ts** — `CURRENT_VERSION = 9`,新增 block:
|
||||
- [x] **Step 2: migrate.ts** — `CURRENT_VERSION = 9`,新增 block:
|
||||
|
||||
```typescript
|
||||
if (current < 9) {
|
||||
@@ -85,7 +85,7 @@ if (current < 9) {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: types.ts** — 扩展:
|
||||
- [x] **Step 3: types.ts** — 扩展:
|
||||
|
||||
```typescript
|
||||
export interface BookMeta {
|
||||
@@ -113,7 +113,7 @@ export interface ChapterTag {
|
||||
// Chapter 已有 summary?, storyTime? — 确保 chapter.update IPC 接受它们
|
||||
```
|
||||
|
||||
- [ ] **Step 4: ipc-channels.ts**
|
||||
- [x] **Step 4: ipc-channels.ts**
|
||||
|
||||
```typescript
|
||||
CHAPTER_TAG_LIST: 'chapter:tagList',
|
||||
@@ -124,7 +124,7 @@ BOOK_PREFS_SET: 'book:prefsSet',
|
||||
AI_SESSION_EXPORT: 'ai:sessionExport',
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 运行** `npm run build` — 应通过
|
||||
- [x] **Step 5: 运行** `npm run build` — 应通过
|
||||
|
||||
---
|
||||
|
||||
@@ -136,7 +136,7 @@ AI_SESSION_EXPORT: 'ai:sessionExport',
|
||||
- Modify: `src/main/services/book-registry.ts`
|
||||
- Modify: `src/main/services/cockpit.service.ts`
|
||||
|
||||
- [ ] **Step 1: ChapterTagRepository**
|
||||
- [x] **Step 1: ChapterTagRepository**
|
||||
|
||||
```typescript
|
||||
export class ChapterTagRepository {
|
||||
@@ -148,7 +148,7 @@ export class ChapterTagRepository {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: chapter.repo update** — `update()` 与 `setPublishStatus()` 支持 `summary`, `storyTime`, `wordCountThreshold`, `publishedAt`:
|
||||
- [x] **Step 2: chapter.repo update** — `update()` 与 `setPublishStatus()` 支持 `summary`, `storyTime`, `wordCountThreshold`, `publishedAt`:
|
||||
|
||||
```typescript
|
||||
setPublishStatus(id: string, publishStatus: PublishStatus): Chapter {
|
||||
@@ -160,9 +160,9 @@ setPublishStatus(id: string, publishStatus: PublishStatus): Chapter {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: book-registry.updateMeta** — 签名改为 `Partial<Pick<BookMeta, 'name' | 'category' | 'targetWordCount' | 'coverPath' | 'synopsis' | 'lastOpenedAt' | 'lastChapterId' | 'status'>>`
|
||||
- [x] **Step 3: book-registry.updateMeta** — 签名改为 `Partial<Pick<BookMeta, 'name' | 'category' | 'targetWordCount' | 'coverPath' | 'synopsis' | 'lastOpenedAt' | 'lastChapterId' | 'status'>>`
|
||||
|
||||
- [ ] **Step 4: cockpit.service**
|
||||
- [x] **Step 4: cockpit.service**
|
||||
|
||||
```typescript
|
||||
shouldShowOnOpen(): boolean {
|
||||
@@ -172,7 +172,7 @@ shouldShowOnOpen(): boolean {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: UT** `tests/main/chapter-tag.repo.test.ts` — 创建章+标签+列表
|
||||
- [x] **Step 5: UT** `tests/main/chapter-tag.repo.test.ts` — 创建章+标签+列表
|
||||
|
||||
Run: `npm run test -- --run tests/main/chapter-tag.repo.test.ts`
|
||||
|
||||
@@ -189,9 +189,9 @@ Run: `npm run test -- --run tests/main/chapter-tag.repo.test.ts`
|
||||
- Modify: `src/preload/index.ts`
|
||||
- Modify: `src/shared/electron-api.d.ts`
|
||||
|
||||
- [ ] **Step 1: book.handler** — `BOOK_UPDATE_META` 接受 `UpdateBookMetaParams`
|
||||
- [x] **Step 1: book.handler** — `BOOK_UPDATE_META` 接受 `UpdateBookMetaParams`
|
||||
|
||||
- [ ] **Step 2: chapter.handler** — 扩展 `CHAPTER_UPDATE` 接受 `summary`, `storyTime`, `wordCountThreshold`;新增:
|
||||
- [x] **Step 2: chapter.handler** — 扩展 `CHAPTER_UPDATE` 接受 `summary`, `storyTime`, `wordCountThreshold`;新增:
|
||||
|
||||
```typescript
|
||||
ipcMain.handle(IPC.CHAPTER_TAG_LIST, (_e, { bookId, chapterId }) =>
|
||||
@@ -199,7 +199,7 @@ ipcMain.handle(IPC.CHAPTER_TAG_LIST, (_e, { bookId, chapterId }) =>
|
||||
// TAG_SET / TAG_REMOVE 同理
|
||||
```
|
||||
|
||||
- [ ] **Step 3: book-prefs.handler**
|
||||
- [x] **Step 3: book-prefs.handler**
|
||||
|
||||
```typescript
|
||||
ipcMain.handle(IPC.BOOK_PREFS_GET, (_e, { bookId, key }) =>
|
||||
@@ -208,11 +208,11 @@ ipcMain.handle(IPC.BOOK_PREFS_SET, (_e, { bookId, key, value }) =>
|
||||
wrap(() => { new BookPrefsRepository(registry.getDb(bookId)).set(key, value) }))
|
||||
```
|
||||
|
||||
- [ ] **Step 4: ai.handler** — `AI_SESSION_EXPORT`:读 messages,格式化 markdown,`dialog.showSaveDialog`,写文件
|
||||
- [x] **Step 4: ai.handler** — `AI_SESSION_EXPORT`:读 messages,格式化 markdown,`dialog.showSaveDialog`,写文件
|
||||
|
||||
- [ ] **Step 5: preload** — `book.updateMeta` 扩展;`bookPrefs.get/set`;`chapterTag.list/set/remove`;`ai.session.export(bookId, sessionId, format)`
|
||||
- [x] **Step 5: preload** — `book.updateMeta` 扩展;`bookPrefs.get/set`;`chapterTag.list/set/remove`;`ai.session.export(bookId, sessionId, format)`
|
||||
|
||||
- [ ] **Step 6: build** `npm run build`
|
||||
- [x] **Step 6: build** `npm run build`
|
||||
|
||||
---
|
||||
|
||||
@@ -223,7 +223,7 @@ ipcMain.handle(IPC.BOOK_PREFS_SET, (_e, { bookId, key, value }) =>
|
||||
- Modify: `src/renderer/components/chapter/TemplateQuickCreateModal.tsx`
|
||||
- Create: `tests/main/chapter-template-context.test.ts`
|
||||
|
||||
- [ ] **Step 1: 新增 `buildTemplateContext()`**
|
||||
- [x] **Step 1: 新增 `buildTemplateContext()`**
|
||||
|
||||
```typescript
|
||||
export function buildTemplateContext(input: {
|
||||
@@ -260,9 +260,9 @@ export function buildTemplateContext(input: {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: TemplateQuickCreateModal** — 用 `buildTemplateContext` 替换内联 `buildContext`
|
||||
- [x] **Step 2: TemplateQuickCreateModal** — 用 `buildTemplateContext` 替换内联 `buildContext`
|
||||
|
||||
- [ ] **Step 3: UT**
|
||||
- [x] **Step 3: UT**
|
||||
|
||||
```typescript
|
||||
it('UT-TEMPL-05: characterList from settings', () => {
|
||||
@@ -282,15 +282,15 @@ Run: `npm run test -- --run tests/main/chapter-template-context.test.ts`
|
||||
- Create: `src/renderer/stores/useBookPrefsStore.ts`
|
||||
- Modify: `src/renderer/components/layout/RightPanel.tsx`
|
||||
|
||||
- [ ] **Step 1: BookSettingsTab** — 表单字段:
|
||||
- [x] **Step 1: BookSettingsTab** — 表单字段:
|
||||
- 书名、分类、目标字数、简介(textarea)
|
||||
- 封面:按钮选文件 → 存 `userData/covers/{bookId}.jpg` 路径到 meta.coverPath
|
||||
- 「每次打开显示驾驶舱」checkbox → `bookPrefs.set('alwaysShowCockpit', 'true'|'false')`
|
||||
- 危险区:删书 → confirm → `book.delete` → `loadBooks` → `setView('home')`
|
||||
|
||||
- [ ] **Step 2: RightPanel** — `book-settings` panel 渲染 `<BookSettingsTab />`
|
||||
- [x] **Step 2: RightPanel** — `book-settings` panel 渲染 `<BookSettingsTab />`
|
||||
|
||||
- [ ] **Step 3: i18n** — `bookSettings.*` 键(中英)
|
||||
- [x] **Step 3: i18n** — `bookSettings.*` 键(中英)
|
||||
|
||||
---
|
||||
|
||||
@@ -301,23 +301,23 @@ Run: `npm run test -- --run tests/main/chapter-template-context.test.ts`
|
||||
- Create: `src/renderer/components/chapter/ChapterMetaPanel.tsx`
|
||||
- Modify: `src/renderer/components/layout/EditorLayout.tsx`
|
||||
|
||||
- [ ] **Step 1: VolumeContextMenu** — 绑定分卷标题 `onContextMenu`:
|
||||
- [x] **Step 1: VolumeContextMenu** — 绑定分卷标题 `onContextMenu`:
|
||||
- 重命名:prompt → `volume.update`
|
||||
- 编辑简述:prompt → `volume.update({ description })`
|
||||
- 删除:若卷内无章 → `volume.delete`;有章则 toast 错误
|
||||
|
||||
- [ ] **Step 2: 章节双击改名** — `chapter-item` `onDoubleClick` → input inline → `chapter.update({ title })`
|
||||
- [x] **Step 2: 章节双击改名** — `chapter-item` `onDoubleClick` → input inline → `chapter.update({ title })`
|
||||
|
||||
- [ ] **Step 3: 章节删除** — 右键或 hover 图标 → confirm → `chapter.delete` → `refreshChapters`
|
||||
- [x] **Step 3: 章节删除** — 右键或 hover 图标 → confirm → `chapter.delete` → `refreshChapters`
|
||||
|
||||
- [ ] **Step 4: ChapterMetaPanel** — 侧栏或状态栏按钮打开:
|
||||
- [x] **Step 4: ChapterMetaPanel** — 侧栏或状态栏按钮打开:
|
||||
- summary、story_time 输入
|
||||
- 标签 chips + 添加输入
|
||||
- word_count_threshold 数字
|
||||
|
||||
- [ ] **Step 5: 发布日期** — `setPublishStatus` 后本地 chapter 显示 `published_at`(只读)
|
||||
- [x] **Step 5: 发布日期** — `setPublishStatus` 后本地 chapter 显示 `published_at`(只读)
|
||||
|
||||
- [ ] **Step 6: 字数阈值变色** — 状态栏 `wordCount.chapter` 与 `currentChapter.wordCountThreshold` 比较,超则橙色 class
|
||||
- [x] **Step 6: 字数阈值变色** — 状态栏 `wordCount.chapter` 与 `currentChapter.wordCountThreshold` 比较,超则橙色 class
|
||||
|
||||
---
|
||||
|
||||
@@ -328,15 +328,15 @@ Run: `npm run test -- --run tests/main/chapter-template-context.test.ts`
|
||||
- Modify: `src/renderer/components/ai/AiPanel.tsx`
|
||||
- Modify: `src/renderer/stores/useAiStore.ts`
|
||||
|
||||
- [ ] **Step 1: AiSessionMenu** — 会话选择器旁「⋯」按钮:
|
||||
- [x] **Step 1: AiSessionMenu** — 会话选择器旁「⋯」按钮:
|
||||
- 重命名 → `ai.session.update({ title })`
|
||||
- 归档/取消归档 → `{ archived: true/false }`
|
||||
- 导出 → 子菜单 txt/md → `ai.session.export`
|
||||
- 删除 → confirm → `ai.session.delete`
|
||||
|
||||
- [ ] **Step 2: useAiStore** — `loadSessions(bookId, true)` 加载含归档;归档会话 optgroup 或折叠区
|
||||
- [x] **Step 2: useAiStore** — `loadSessions(bookId, true)` 加载含归档;归档会话 optgroup 或折叠区
|
||||
|
||||
- [ ] **Step 3: i18n** — `ai.session.*`
|
||||
- [x] **Step 3: i18n** — `ai.session.*`
|
||||
|
||||
---
|
||||
|
||||
@@ -348,13 +348,13 @@ Run: `npm run test -- --run tests/main/chapter-template-context.test.ts`
|
||||
- Modify: `src/renderer/App.tsx`
|
||||
- Modify: `src/renderer/styles/layout.css`
|
||||
|
||||
- [ ] **Step 1: FocusModeOverlay** — `useAppStore.focusMode` boolean:
|
||||
- [x] **Step 1: FocusModeOverlay** — `useAppStore.focusMode` boolean:
|
||||
- true 时 `#app` 加 `focus-mode` class(CSS 隐藏 sidebars/right-panel/topbar 非必要按钮)
|
||||
- Esc 或按钮退出
|
||||
|
||||
- [ ] **Step 2: App.tsx** — `focusMode` 动作调用 `setFocusMode(true)`;再次按退出
|
||||
- [x] **Step 2: App.tsx** — `focusMode` 动作调用 `setFocusMode(true)`;再次按退出
|
||||
|
||||
- [ ] **Step 3: tts-controller.ts**
|
||||
- [x] **Step 3: tts-controller.ts**
|
||||
|
||||
```typescript
|
||||
export function speakText(text: string, lang = 'zh-CN'): void {
|
||||
@@ -368,9 +368,9 @@ export function stopSpeaking(): void {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: App.tsx** — `toggleTTS`:若正在朗读则 stop;否则读当前章 `editor.getText()` 或选区
|
||||
- [x] **Step 4: App.tsx** — `toggleTTS`:若正在朗读则 stop;否则读当前章 `editor.getText()` 或选区
|
||||
|
||||
- [ ] **Step 5: i18n** — `focus.*` / `tts.*`
|
||||
- [x] **Step 5: i18n** — `focus.*` / `tts.*`
|
||||
|
||||
---
|
||||
|
||||
@@ -381,13 +381,13 @@ export function stopSpeaking(): void {
|
||||
- Modify: `src/renderer/components/layout/TopBar.tsx`
|
||||
- Modify: `src/renderer/stores/useAppStore.ts`
|
||||
|
||||
- [ ] **Step 1: WordFreqPanel** — 底部按钮 `onClick` 改为 `setNamingOpen(true)` 或触发 KnowledgePanel 命名检查(与 `NamingCheckModal` 联动)
|
||||
- [x] **Step 1: WordFreqPanel** — 底部按钮 `onClick` 改为 `setNamingOpen(true)` 或触发 KnowledgePanel 命名检查(与 `NamingCheckModal` 联动)
|
||||
|
||||
- [ ] **Step 2: TopBar** — 在 `top-spacer` 前加细进度条:
|
||||
- [x] **Step 2: TopBar** — 在 `top-spacer` 前加细进度条:
|
||||
- 读 `useWritingStatsStore` + `dailyWordGoal`
|
||||
- `goalProgress` 映射 width%;≥100% 绿色,≥80% 橙色
|
||||
|
||||
- [ ] **Step 3: 全局 grep** `feature.comingSoon` — Wave 1 范围内应无功能入口残留
|
||||
- [x] **Step 3: 全局 grep** `feature.comingSoon` — Wave 1 范围内应无功能入口残留
|
||||
|
||||
---
|
||||
|
||||
@@ -401,17 +401,17 @@ export function stopSpeaking(): void {
|
||||
- Modify: `e2e/helpers.ts`
|
||||
- Modify: `package.json`, `README.md`, `SettingsPage.tsx`
|
||||
|
||||
- [ ] **Step 1: e2e/helpers.ts** — `openBookSettings(page)` helper
|
||||
- [x] **Step 1: e2e/helpers.ts** — `openBookSettings(page)` helper
|
||||
|
||||
- [ ] **Step 2: E2E-BOOK-01** — 改书名、勾选 always cockpit、删书
|
||||
- [x] **Step 2: E2E-BOOK-01** — 改书名、勾选 always cockpit、删书
|
||||
|
||||
- [ ] **Step 3: E2E-CHAP-01** — 双击改标题、删除章
|
||||
- [x] **Step 3: E2E-CHAP-01** — 双击改标题、删除章
|
||||
|
||||
- [ ] **Step 4: E2E-FOCUS-01** — Ctrl+Alt+F 进入专注,Esc 退出
|
||||
- [x] **Step 4: E2E-FOCUS-01** — Ctrl+Alt+F 进入专注,Esc 退出
|
||||
|
||||
- [ ] **Step 5: E2E-TEMPL-05** — 模板含 `{characterList}` 的自定义模板(设置页创建)→ 快速新建 → 编辑器含角色名
|
||||
- [x] **Step 5: E2E-TEMPL-05** — 模板含 `{characterList}` 的自定义模板(设置页创建)→ 快速新建 → 编辑器含角色名
|
||||
|
||||
- [ ] **Step 6: 全量验证**
|
||||
- [x] **Step 6: 全量验证**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -419,9 +419,9 @@ npm run test
|
||||
npm run test:e2e -- e2e/book-settings.spec.ts e2e/chapter-management.spec.ts e2e/focus-tts.spec.ts e2e/chapter-template.spec.ts
|
||||
```
|
||||
|
||||
- [ ] **Step 7: 版本** — `package.json` / README / Settings 关于页 → **v1.2.0**
|
||||
- [x] **Step 7: 版本** — `package.json` / README / Settings 关于页 → **v1.2.0**
|
||||
|
||||
- [ ] **Step 8: 勾选本 plan 全部 checkbox**
|
||||
- [x] **Step 8: 勾选本 plan 全部 checkbox**
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user