feat: ship v1.1.0 writer toolkit with templates, import, export, and inspiration capture
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>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# 笔临 v1.1 作家日更工具包 Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking.
|
||||
|
||||
**Goal:** 交付 v1.1.0:§10 章节模板 + 快速新建、§11.1 txt/md/docx 导入、§20.2 投稿预设单章导出、§20.6 全局灵感捕获增强。
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
- Modify: `src/main/services/global-settings.ts`
|
||||
- Modify: `src/renderer/stores/useSettingsStore.ts`
|
||||
|
||||
- [ ] **Step 1: 扩展 types**
|
||||
- [x] **Step 1: 扩展 types**
|
||||
|
||||
```typescript
|
||||
export interface ChapterTemplate {
|
||||
@@ -109,7 +109,7 @@ submissionPresets?: SubmissionPreset[]
|
||||
defaultSubmissionPresetId?: string
|
||||
```
|
||||
|
||||
- [ ] **Step 2: builtin-templates.ts**
|
||||
- [x] **Step 2: builtin-templates.ts**
|
||||
|
||||
```typescript
|
||||
import type { ChapterTemplate, SubmissionPreset } from './types'
|
||||
@@ -149,13 +149,13 @@ export function mergeSubmissionPresets(custom: SubmissionPreset[] = []): Submiss
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: global-settings defaults**
|
||||
- [x] **Step 3: global-settings defaults**
|
||||
|
||||
在 `defaults()` 追加 `chapterTemplates: []`, `submissionPresets: []`, `defaultSubmissionPresetId: 'builtin-webnovel'`。
|
||||
|
||||
- [ ] **Step 4: useSettingsStore 初始字段对齐**
|
||||
- [x] **Step 4: useSettingsStore 初始字段对齐**
|
||||
|
||||
- [ ] **Step 5: build**
|
||||
- [x] **Step 5: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -169,7 +169,7 @@ npm run build
|
||||
- Create: `src/renderer/lib/chapter-template.ts`
|
||||
- Create: `tests/main/chapter-template.test.ts`
|
||||
|
||||
- [ ] **Step 1: 实现 replaceChapterTemplate**
|
||||
- [x] **Step 1: 实现 replaceChapterTemplate**
|
||||
|
||||
```typescript
|
||||
export interface TemplateContext {
|
||||
@@ -204,7 +204,7 @@ export function plainTextToEditorHtml(text: string): string {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: UT-TEMPL-01~03**
|
||||
- [x] **Step 2: UT-TEMPL-01~03**
|
||||
|
||||
```typescript
|
||||
it('UT-TEMPL-01: replaces chapterNumber and chapterTitle', () => {
|
||||
@@ -217,7 +217,7 @@ it('UT-TEMPL-01: replaces chapterNumber and chapterTitle', () => {
|
||||
})
|
||||
```
|
||||
|
||||
- [ ] **Step 3: 运行**
|
||||
- [x] **Step 3: 运行**
|
||||
|
||||
```bash
|
||||
npm run test -- tests/main/chapter-template.test.ts
|
||||
@@ -231,7 +231,7 @@ npm run test -- tests/main/chapter-template.test.ts
|
||||
- Create: `src/renderer/components/chapter/TemplateQuickCreateModal.tsx`
|
||||
- Modify: `src/renderer/components/layout/EditorLayout.tsx`
|
||||
|
||||
- [ ] **Step 1: TemplateQuickCreateModal**
|
||||
- [x] **Step 1: TemplateQuickCreateModal**
|
||||
|
||||
- `data-testid="chapter-quick-new"` 打开模态
|
||||
- 从 `useSettingsStore` + `mergeChapterTemplates` 展示模板卡片
|
||||
@@ -239,11 +239,11 @@ npm run test -- tests/main/chapter-template.test.ts
|
||||
- 若模板 `defaultStatus` 设置则 `chapter.update({ status })`
|
||||
- `switchTarget` 到新章
|
||||
|
||||
- [ ] **Step 2: EditorLayout**
|
||||
- [x] **Step 2: EditorLayout**
|
||||
|
||||
在章节面板底部保留原「+ 新章」按钮,新增「模板新建」按钮 `data-testid="chapter-quick-new"`。
|
||||
|
||||
- [ ] **Step 3: build**
|
||||
- [x] **Step 3: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -257,18 +257,18 @@ npm run build
|
||||
- Create: `src/renderer/components/settings/TemplateSettingsTab.tsx`
|
||||
- Modify: `src/renderer/components/settings/SettingsPage.tsx`
|
||||
|
||||
- [ ] **Step 1: TemplateSettingsTab**
|
||||
- [x] **Step 1: TemplateSettingsTab**
|
||||
|
||||
- `data-testid="settings-chapter-templates"`
|
||||
- 内置模板只读展示
|
||||
- 自定义:`setting-template-add` 新建;编辑 name/body;删除(uuid id)
|
||||
- 保存:`settings.update({ chapterTemplates: customOnly })`
|
||||
|
||||
- [ ] **Step 2: SettingsPage 导航**
|
||||
- [x] **Step 2: SettingsPage 导航**
|
||||
|
||||
新增 section `'templates'`,侧栏项 `settings.templates`。
|
||||
|
||||
- [ ] **Step 3: build**
|
||||
- [x] **Step 3: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -282,7 +282,7 @@ npm run build
|
||||
- Create: `src/main/lib/submission-formatter.ts`
|
||||
- Create: `tests/main/submission-formatter.test.ts`
|
||||
|
||||
- [ ] **Step 1: 实现**
|
||||
- [x] **Step 1: 实现**
|
||||
|
||||
```typescript
|
||||
import { stripHtml } from '../services/word-count'
|
||||
@@ -312,9 +312,9 @@ export function formatChapterForSubmission(input: FormatChapterInput): string {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: UT-EXPORT-01 / UT-EXPORT-02**
|
||||
- [x] **Step 2: UT-EXPORT-01 / UT-EXPORT-02**
|
||||
|
||||
- [ ] **Step 3: 运行**
|
||||
- [x] **Step 3: 运行**
|
||||
|
||||
```bash
|
||||
npm run test -- tests/main/submission-formatter.test.ts
|
||||
@@ -332,7 +332,7 @@ npm run test -- tests/main/submission-formatter.test.ts
|
||||
- Modify: `src/preload/index.ts`
|
||||
- Modify: `src/shared/electron-api.d.ts`
|
||||
|
||||
- [ ] **Step 1: ExportService**
|
||||
- [x] **Step 1: ExportService**
|
||||
|
||||
```typescript
|
||||
export class ExportService {
|
||||
@@ -371,7 +371,7 @@ export class ExportService {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: export.handler + register + preload**
|
||||
- [x] **Step 2: export.handler + register + preload**
|
||||
|
||||
```typescript
|
||||
EXPORT_FORMAT_CHAPTER: 'export:formatChapter',
|
||||
@@ -379,7 +379,7 @@ EXPORT_COPY_CLIPBOARD: 'export:copyClipboard',
|
||||
EXPORT_SAVE_TXT: 'export:saveTxt',
|
||||
```
|
||||
|
||||
- [ ] **Step 3: build**
|
||||
- [x] **Step 3: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -396,19 +396,19 @@ npm run build
|
||||
- Modify: `src/renderer/components/layout/EditorLayout.tsx`
|
||||
- Modify: `src/renderer/App.tsx`
|
||||
|
||||
- [ ] **Step 1: ExportModal**
|
||||
- [x] **Step 1: ExportModal**
|
||||
|
||||
- `data-testid="export-modal"`
|
||||
- `export-preset-select` 下拉(mergeSubmissionPresets)
|
||||
- 预览区:调用 `export.formatChapter` 显示前 500 字
|
||||
- `export-copy-clipboard` / `export-save-txt`
|
||||
|
||||
- [ ] **Step 2: SubmissionPresetTab**
|
||||
- [x] **Step 2: SubmissionPresetTab**
|
||||
|
||||
- `data-testid="settings-submission-presets"`
|
||||
- CRUD 自定义预设(不可删 builtin-webnovel)
|
||||
|
||||
- [ ] **Step 3: EditorLayout 工具栏 + App exportBook 快捷键**
|
||||
- [x] **Step 3: EditorLayout 工具栏 + App exportBook 快捷键**
|
||||
|
||||
```typescript
|
||||
if (action === 'exportBook') {
|
||||
@@ -420,7 +420,7 @@ if (action === 'exportBook') {
|
||||
|
||||
创建 `useExportStore`(open/close,类似 useGraphStore)。
|
||||
|
||||
- [ ] **Step 4: build**
|
||||
- [x] **Step 4: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -435,7 +435,7 @@ npm run build
|
||||
- Modify: `src/renderer/App.tsx`
|
||||
- Modify: `src/renderer/styles/layout.css`
|
||||
|
||||
- [ ] **Step 1: App.tsx 移除 view 限制**
|
||||
- [x] **Step 1: App.tsx 移除 view 限制**
|
||||
|
||||
```typescript
|
||||
if (action === 'captureInspiration') {
|
||||
@@ -444,13 +444,13 @@ if (action === 'captureInspiration') {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: InspirationModal**
|
||||
- [x] **Step 2: InspirationModal**
|
||||
|
||||
- 无 `bookId` 时显示 `inspiration-book-select`(`useBookStore.books` 按 `lastOpenedAt` 排序)
|
||||
- `handleSave` 删除 `switchTarget` / `setSidebarPanel`;改为 `showToast(t('inspiration.saved'))`
|
||||
- 添加 `inspiration-modal--capture` class
|
||||
|
||||
- [ ] **Step 3: E2E 预备 build**
|
||||
- [x] **Step 3: E2E 预备 build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -464,7 +464,7 @@ npm run build
|
||||
- Create: `src/main/lib/chapter-splitter.ts`
|
||||
- Create: `tests/main/chapter-splitter.test.ts`
|
||||
|
||||
- [ ] **Step 1: splitPlainText**
|
||||
- [x] **Step 1: splitPlainText**
|
||||
|
||||
```typescript
|
||||
export function splitPlainText(
|
||||
@@ -478,9 +478,9 @@ export function splitPlainText(
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: UT-IMPORT-01** — 样本文本 3 章标题
|
||||
- [x] **Step 2: UT-IMPORT-01** — 样本文本 3 章标题
|
||||
|
||||
- [ ] **Step 3: 运行**
|
||||
- [x] **Step 3: 运行**
|
||||
|
||||
```bash
|
||||
npm run test -- tests/main/chapter-splitter.test.ts
|
||||
@@ -498,14 +498,14 @@ npm run test -- tests/main/chapter-splitter.test.ts
|
||||
- Modify: `src/preload/index.ts`
|
||||
- Create: `tests/main/import.service.test.ts`
|
||||
|
||||
- [ ] **Step 1: 安装依赖**
|
||||
- [x] **Step 1: 安装依赖**
|
||||
|
||||
```bash
|
||||
npm install mammoth marked --legacy-peer-deps
|
||||
npm install -D @types/marked --legacy-peer-deps
|
||||
```
|
||||
|
||||
- [ ] **Step 2: ImportService**
|
||||
- [x] **Step 2: ImportService**
|
||||
|
||||
```typescript
|
||||
export class ImportService {
|
||||
@@ -543,14 +543,14 @@ export class ImportService {
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: import.handler**
|
||||
- [x] **Step 3: import.handler**
|
||||
|
||||
- `IMPORT_PICK_FILE` / `IMPORT_PREVIEW` / `IMPORT_EXECUTE`
|
||||
- progress:`BrowserWindow.getAllWindows()[0]?.webContents.send(IPC.IMPORT_PROGRESS, { current, total })`
|
||||
|
||||
- [ ] **Step 4: IT-IMPORT-01**
|
||||
- [x] **Step 4: IT-IMPORT-01**
|
||||
|
||||
- [ ] **Step 5: 运行**
|
||||
- [x] **Step 5: 运行**
|
||||
|
||||
```bash
|
||||
npm run test -- tests/main/import.service.test.ts
|
||||
@@ -565,7 +565,7 @@ npm run test -- tests/main/import.service.test.ts
|
||||
- Modify: `src/renderer/components/home/HomePage.tsx`
|
||||
- Modify: `src/renderer/App.tsx`(挂载 ImportBookModal)
|
||||
|
||||
- [ ] **Step 1: ImportBookModal**
|
||||
- [x] **Step 1: ImportBookModal**
|
||||
|
||||
- `data-testid="import-book-modal"`
|
||||
- 浏览 → `import.pickFile`
|
||||
@@ -575,13 +575,13 @@ npm run test -- tests/main/import.service.test.ts
|
||||
- 执行 → 监听 `onImportProgress` → `import-progress-bar`
|
||||
- 成功 → `openBook(bookId)` + 切 editor
|
||||
|
||||
- [ ] **Step 2: HomePage 替换 comingSoon**
|
||||
- [x] **Step 2: HomePage 替换 comingSoon**
|
||||
|
||||
```typescript
|
||||
onClick={() => setImportOpen(true)}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: build**
|
||||
- [x] **Step 3: build**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -603,9 +603,9 @@ npm run build
|
||||
- Modify: `README.md`
|
||||
- Modify: `src/renderer/components/settings/SettingsPage.tsx`(关于页版本)
|
||||
|
||||
- [ ] **Step 1: i18n 键**(spec §8 全部)
|
||||
- [x] **Step 1: i18n 键**(spec §8 全部)
|
||||
|
||||
- [ ] **Step 2: CSS**
|
||||
- [x] **Step 2: CSS**
|
||||
|
||||
```css
|
||||
.template-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; }
|
||||
@@ -614,7 +614,7 @@ npm run build
|
||||
.inspiration-modal--capture { z-index: 10000; }
|
||||
```
|
||||
|
||||
- [ ] **Step 3: E2E**
|
||||
- [x] **Step 3: E2E**
|
||||
|
||||
```typescript
|
||||
// E2E-TEMPL-01: 模板新建章 → ProseMirror 含模板正文
|
||||
@@ -623,7 +623,7 @@ npm run build
|
||||
// E2E-INSP-01: 主页 Ctrl+Shift+I → toast,仍在 home
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 全量验证**
|
||||
- [x] **Step 4: 全量验证**
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
@@ -631,11 +631,11 @@ npm run test
|
||||
npm run test:e2e -- e2e/chapter-template.spec.ts e2e/import-book.spec.ts e2e/export-submission.spec.ts e2e/inspiration-capture.spec.ts
|
||||
```
|
||||
|
||||
- [ ] **Step 5: 版本 1.1.0**
|
||||
- [x] **Step 5: 版本 1.1.0**
|
||||
|
||||
`package.json` / `README.md` / Settings 关于页 → `v1.1.0`
|
||||
|
||||
- [ ] **Step 6: 勾选本 plan 全部 checkbox**
|
||||
- [x] **Step 6: 勾选本 plan 全部 checkbox**
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user