Files
bilin/docs/superpowers/specs/2026-07-08-bilin-v11-writer-toolkit-design.md
bing 027d7c7559 docs: add v1.1 writer toolkit design spec for templates, import, and export
Capture chapter templates, txt/md/docx import, submission presets, and global inspiration capture for the daily writing workflow release.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 11:05:06 +08:00

400 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 笔临 v1.1 作家日更工具包 设计规格
**版本**1.0
**日期**2026-07-08
**范围**:§10 章节模板 + §11.1 书籍导入(txt/md/docx+ §20.2 投稿导出预设 + §20.6 全局灵感捕获增强
**前置**v1.0.0 已交付(`4adafa1`:P7 角色关系图谱 + P9 写作分析)
**参考**`design/readme.md` v1.1 §10 / §11.1 / §20.2 / §20.6、`design/ui_pc.html` `#templateModal` / `#importModal` / `#exportModal` / `#inspirationModal`
**目标版本**v1.1.0
---
## 1. 背景与目标
### 1.1 阶段定位
v1.0.0 已完成「看数据」能力(关系图谱、写作分析、POV)。`design/readme.md` §20.7 审查清单仍有多项 **有 UI 原型、无真实实现** 的作家刚需:
- 主页「导入书籍」为 `comingSoon`
- 章节模板 / 快速新建章仅有 `#templateModal` 静态原型
- 导出模态「复制到剪贴板(网文投稿格式)」无后端
- 灵感捕获 `Ctrl+Shift+I` 仅编辑器内可用,保存后会跳转侧栏打断心流
本阶段(v1.1.0)交付 **作家日更工具包**,形成「写得更快 → 发得更顺 → 灵感不漏」闭环。
### 1.2 已确认决策(Brainstorming 2026-07-08
| 维度 | 决策 |
|------|------|
| 方向 | **方案 B**:作家日更工具包 |
| MVP 边界 | **方案 A(完整四模块)**:模板 + txt/md/docx 导入 + 投稿预设 + 灵感增强 |
| 模板存储 | `GlobalSettings.chapterTemplates[]`;内置模板代码常量合并展示 |
| 模板解析 | **渲染进程** `chapter-template.ts` 替换变量后 `chapter.create` + `chapter.update` |
| docx 解析 | **mammoth**docx → HTML,保留 bold/italic |
| md 解析 | **marked**md → HTML |
| 导入分章 | 自动正则 / 双换行 / 自定义正则 三档 |
| 大文件 | >20MB 导入前二次确认;进度条 IPC 事件 |
| 投稿导出 | **单章**复制剪贴板 + 另存 `.txt`;内置「通用网文」预设 |
| 投稿预设存储 | `GlobalSettings.submissionPresets[]` |
| 灵感增强 | 快捷键全局可用;未开书时模态内选书;保存后仅 toast、不跳转 |
| 置顶 | 灵感模态最高 z-index;不新建独立 BrowserWindow |
### 1.3 验收标准
用户能完成:
1. 在章节列表点击「快速新建」,选择模板并输入标题,创建章节且编辑器自动填入模板正文(含变量替换)
2. 在设置页创建/编辑/删除自定义章节模板;内置模板只读展示
3. 主页「导入书籍」选择 `.txt` / `.md` / `.docx`,预览分章结果,确认后创建新书并导入全部章节
4. 导入 >20MB 文件前看到确认提示;导入过程显示进度
5. 编辑器内 `Ctrl+Shift+E` 打开导出模态,选择「通用网文」预设,**复制到剪贴板** 得到段首空两格 + `# 第N章 标题` 格式纯文本
6. 设置页自定义投稿预设(标题格式、段首缩进、段落间距),导出时应用
7. 在主页按 `Ctrl+Shift+I` 唤起灵感捕获,选择书籍后保存,仅 toast 确认、焦点不打断原界面
8. 编辑器内灵感捕获行为与 7 一致(不再自动跳转灵感面板)
### 1.4 本规格不实现(明确排除)
| 项 | 归属 |
|----|------|
| `.novel` / `.novel-all` 项目包导入导出 | v1.2+ |
| PDF / Word 全书导出、分卷批量投稿复制 | v1.2+ |
| §11.2 完整导出矩阵(docx 目录、PDF 排版) | v1.2+ |
| §20.5 敏感词合规、预设内敏感词替换规则 | v1.2+ |
| §20.3 角色弧线简报 | v1.2+(图谱深化线) |
| §5.7 故事时间线、§5.8 连续阅读模式 | v1.2+ |
| 导入 `.novel` 包 | v1.2+ |
| 平台 API 直连投稿 | 永久 YAGNI |
| 模板变量 `{outlineItem}` 自动关联当前章大纲 | v1.1 留空字符串(无关联时);手动关联后续 |
---
## 2. 架构
### 2.1 进程模型
```
┌─────────────────────────────────────────────────────────────────────┐
│ 渲染进程 │
│ · TemplateQuickCreateModal / TemplateSettingsTab │
│ · ImportBookModal │
│ · ExportModal + SubmissionPresetSettingsTab │
│ · InspirationModal(增强:全局唤起、选书、无跳转) │
│ · chapter-template.ts / submission-formatter.ts(纯函数) │
└──────────────────────────────┬──────────────────────────────────────┘
│ IPC
┌────────────────────────────▼────────────────────────────────────────┐
│ 主进程 │
│ · ImportService(读文件、分章、创建书+卷+章批量写入) │
│ · ExportService(HTML→纯文本、应用投稿预设、写剪贴板/文件) │
│ · BookRegistryService(已有 create/open
│ · GlobalSettingsService(扩展 chapterTemplates / submissionPresets)│
│ · dialog.showOpenDialog / clipboard │
└─────────────────────────────────────────────────────────────────────┘
```
### 2.2 模块边界
| 模块 | 职责 | 依赖 |
|------|------|------|
| `chapter-template.ts` | 变量替换、章节号计算 | `GlobalSettings`, 书籍 volumes/chapters/settings |
| `ImportService` | 解析文件、分章、创建书 | `BookRegistry`, `ChapterRepository`, mammoth, marked |
| `ExportService` | 单章格式化、剪贴板、存盘 | `ChapterRepository`, `GlobalSettings` |
| `SubmissionFormatter` | HTML 剥离、段首缩进、标题行 | 纯函数,主/渲染均可调用 |
| `TemplateQuickCreateModal` | 选模板 → IPC 创建章 | `chapter.create/update` |
| `ImportBookModal` | 选文件 → preview → execute | `import.*` |
| `ExportModal` | 选预设 → 复制/存盘 | `export.*` |
**原则**
- 导入/导出重 IO 与解析放主进程;模板变量替换可在渲染进程(减少 IPC 往返)
- 导入失败整事务回滚:书未创建或创建后写章失败则 `registry.delete(bookId)`
- 剪贴板写入仅主进程(Electron `clipboard` API
---
## 3. 数据模型
### 3.1 GlobalSettings 扩展
```typescript
export interface ChapterTemplate {
id: string
name: string
body: string
defaultStatus?: ChapterStatus
builtin?: boolean
}
export interface SubmissionPreset {
id: string
name: string
titleFormat: string // 默认 "# 第{chapterNumber}章 {chapterTitle}"
indentParagraphs: boolean // 段首两全角空格
includeAuthorsNote: boolean // 保留「作者的话」段(检测 —— 分隔)
paragraphGap: 'single' | 'double'
}
// GlobalSettings 追加:
chapterTemplates?: ChapterTemplate[]
submissionPresets?: SubmissionPreset[]
defaultSubmissionPresetId?: string
```
**内置模板**`src/shared/builtin-templates.ts`,不写入 JSON):
| id | name | body 摘要 |
|----|------|-----------|
| `builtin-generic` | 通用模板 | `## 第{chapterNumber}章 {chapterTitle}\n\n` |
| `builtin-webnovel` | 网文常用 | 正文 + `\n\n——\n作者的话:\n` |
**内置投稿预设**
| id | name | 规则 |
|----|------|------|
| `builtin-webnovel` | 通用网文 | `titleFormat: "# 第{chapterNumber}章 {chapterTitle}"`, `indentParagraphs: true`, `paragraphGap: 'double'` |
`GlobalSettingsService` 读取时合并内置项;用户不可删除内置项。
### 3.2 模板变量
| 变量 | 来源 |
|------|------|
| `{chapterNumber}` | 目标卷 `chapters.length + 1` |
| `{chapterTitle}` | 用户输入 |
| `{penName}` | `settings.penName` |
| `{date}` | `localDateString()` |
| `{volumeName}` | 当前卷 `name` |
| `{previousChapterTitle}` | 同卷上一章 `title`,首章 `""` |
| `{outlineItem}` | v1.1 固定 `""`(章节创建时无 outline 关联) |
| `{characterList}` | 当前卷内 `settings.type=character``chapterIds` 与卷内任一章有交集的 `name`,逗号连接 |
### 3.3 导入分章
```typescript
export type ImportSplitMode = 'auto' | 'paragraph' | 'regex'
export interface ImportChapterPreview {
title: string
contentHtml: string
wordCount: number
}
export interface ImportPreviewResult {
fileName: string
fileSizeBytes: number
chapters: ImportChapterPreview[]
warnings: string[]
}
export interface ImportExecuteParams {
filePath: string
bookName: string
category: string
splitMode: ImportSplitMode
customRegex?: string
}
```
**自动分章正则**(依次尝试,命中即用):
1. `/^#\s*第.+章[^\n]*/gm`Markdown 标题)
2. `/^第[一二三四五六七八九十百千零〇\d]+章[^\n]*/gm`(纯文本章标题行)
**paragraph 模式**:按 `\n\s*\n` 分段;标题取段首非空行,截断 50 字。
**首章前导内容**:合并入第一章 `warnings` 提示,不丢弃。
---
## 4. IPC
### 4.1 通道
```typescript
IMPORT_PICK_FILE: 'import:pickFile'
IMPORT_PREVIEW: 'import:preview'
IMPORT_EXECUTE: 'import:execute'
IMPORT_PROGRESS: 'import:progress' // main → renderer event
EXPORT_FORMAT_CHAPTER: 'export:formatChapter'
EXPORT_COPY_CLIPBOARD: 'export:copyClipboard'
EXPORT_SAVE_TXT: 'export:saveTxt'
```
### 4.2 载荷
```typescript
// import:pickFile → { canceled, filePath? }
// import:preview → { filePath, splitMode, customRegex? } → ImportPreviewResult
// import:execute → ImportExecuteParams → { bookId }
// import:progress → { phase: 'parsing'|'writing', current, total }
// export:formatChapter → { bookId, chapterId, presetId } → { text }
// export:copyClipboard → { text } → void
// export:saveTxt → { text, defaultName } → { savedPath? }
```
---
## 5. UI 规格
### 5.1 TemplateQuickCreateModal
- 入口:章节列表底部 `data-testid="chapter-quick-new"`
- 布局:参考 `#templateModal` 卡片网格 + 标题输入
- 流程:选模板 → 输入标题 → 创建 → `switchTarget` 到新章
### 5.2 设置页 — 章节模板 Tab
- `data-testid="settings-chapter-templates"`
- 列表:内置 + 自定义;自定义可编辑 body(textarea)、删除
- 新建:`setting-template-add`
### 5.3 ImportBookModal
- 入口:主页 `home.importBook`(替换 comingSoon
- `data-testid="import-book-modal"`
- 字段:文件路径(浏览)、书名、分类、分章规则下拉、自定义正则输入(regex 模式显示)
- 预览区:章数 + 前 5 章标题列表
- >20MB`import-large-file-confirm` 对话框
- 进度:`import-progress-bar`
### 5.4 ExportModal
- 入口:编辑器工具栏 `data-testid="export-chapter"` + 快捷键 `exportBook`
- `data-testid="export-modal"`
- 折叠区「投稿预设」:`export-preset-select`
- 操作:`export-copy-clipboard` / `export-save-txt`
- 预览:格式化后前 500 字纯文本
### 5.5 设置页 — 投稿预设 Tab
- `data-testid="settings-submission-presets"`
- 字段:名称、标题格式、段首缩进 checkbox、段落间距 select
### 5.6 InspirationModal 增强
- `App.tsx``captureInspiration` 移除 `view !== 'editor'` 限制
- 未开书:显示 `inspiration-book-select`(书籍列表下拉,默认 `lastOpenedAt` 最新)
- 保存后:删除 `switchTarget` + `setSidebarPanel('inspiration')`;仅 `showToast` + `setOpen(false)`
- 样式:`inspiration-modal--capture` 提升 z-index(高于 TopBar
---
## 6. 依赖
| 包 | 用途 |
|----|------|
| `mammoth` | docx → HTML |
| `marked` | md → HTML |
均在主进程 `ImportService` 使用;electron-vite 打包时 external 或 bundle 按现有惯例。
---
## 7. 测试
| 用例 ID | 层级 | 场景 | 预期 |
|---------|------|------|------|
| UT-TEMPL-01 | 单元 | 解析含 `{chapterNumber}` `{chapterTitle}` | 正确替换 |
| UT-TEMPL-02 | 单元 | `{volumeName}` 替换 | 卷名正确 |
| UT-TEMPL-03 | 单元 | `{characterList}` 卷内角色 | 逗号列表 |
| IT-TEMPL-01 | 集成 | 模板快速新建写入 DB | content 含替换后正文 |
| UT-IMPORT-01 | 单元 | txt 自动分章 3 章 | `chapters.length === 3` |
| UT-IMPORT-02 | 单元 | md 转 HTML | 含 `<p>` |
| UT-IMPORT-03 | 单元 | docx mammoth | 非空 HTML |
| IT-IMPORT-01 | 集成 | execute 创建书+章 | `registry.open` 章数匹配 |
| UT-EXPORT-01 | 单元 | 段首空两格 | 每段 `  ` 前缀 |
| UT-EXPORT-02 | 单元 | 标题格式 | `# 第1章 标题` |
| IT-EXPORT-01 | 集成 | copyClipboard | 剪贴板非空 |
| E2E-TEMPL-01 | E2E | 快速新建章 | 编辑器有模板内容 |
| E2E-IMPORT-01 | E2E | 导入 txt | 新书可见多章 |
| E2E-EXPORT-01 | E2E | 复制投稿格式 | 剪贴板含章标题 |
| E2E-INSP-01 | E2E | 主页 Ctrl+Shift+I 保存 | toast,不跳编辑器 |
---
## 8. i18n 键(节选)
```json
"template.title": "章节模板",
"template.quickNew": "模板新建章",
"template.builtin": "内置",
"template.custom": "自定义",
"import.title": "导入书籍",
"import.pickFile": "浏览文件",
"import.splitAuto": "自动识别章节",
"import.splitParagraph": "双换行分段",
"import.splitRegex": "自定义正则",
"import.largeFileConfirm": "文件较大({{size}}),导入可能需要 30 秒至 2 分钟,是否继续?",
"import.progress": "正在导入 {{current}}/{{total}}",
"export.title": "导出章节",
"export.submissionPresets": "投稿预设",
"export.copyClipboard": "复制到剪贴板",
"export.saveTxt": "另存为文本",
"export.copied": "已复制到剪贴板",
"submission.title": "投稿预设",
"submission.titleFormat": "标题格式",
"submission.indent": "段首空两格",
"inspiration.selectBook": "关联书籍",
"inspiration.saved": "灵感已保存"
```
---
## 9. 文件清单
| 文件 | 职责 |
|------|------|
| `src/shared/types.ts` | `ChapterTemplate`, `SubmissionPreset`, Import/Export DTO |
| `src/shared/builtin-templates.ts` | 内置模板与投稿预设 |
| `src/shared/ipc-channels.ts` | `import.*` / `export.*` |
| `src/main/services/import.service.ts` | 解析 + 分章 + 创建书 |
| `src/main/services/export.service.ts` | 格式化 + 剪贴板/存盘 |
| `src/main/lib/submission-formatter.ts` | HTML → 投稿纯文本 |
| `src/main/lib/chapter-splitter.ts` | 分章逻辑 |
| `src/main/ipc/handlers/import.handler.ts` | 导入 IPC |
| `src/main/ipc/handlers/export.handler.ts` | 导出 IPC |
| `src/renderer/lib/chapter-template.ts` | 模板变量替换 |
| `src/renderer/components/chapter/TemplateQuickCreateModal.tsx` | 快速新建 |
| `src/renderer/components/import/ImportBookModal.tsx` | 导入 UI |
| `src/renderer/components/export/ExportModal.tsx` | 导出 UI |
| `src/renderer/components/settings/TemplateSettingsTab.tsx` | 模板管理 |
| `src/renderer/components/settings/SubmissionPresetTab.tsx` | 投稿预设管理 |
| `src/renderer/components/inspiration/InspirationModal.tsx` | 增强 |
| `src/renderer/components/home/HomePage.tsx` | 接入导入 |
| `src/renderer/components/layout/EditorLayout.tsx` | 导出入口 |
| `tests/main/chapter-template.test.ts` | UT-TEMPL-* |
| `tests/main/chapter-splitter.test.ts` | UT-IMPORT-01 |
| `tests/main/submission-formatter.test.ts` | UT-EXPORT-* |
| `tests/main/import.service.test.ts` | IT-IMPORT-01 |
| `e2e/chapter-template.spec.ts` | E2E-TEMPL-01 |
| `e2e/import-book.spec.ts` | E2E-IMPORT-01 |
| `e2e/export-submission.spec.ts` | E2E-EXPORT-01 |
| `e2e/inspiration-capture.spec.ts` | E2E-INSP-01 |
---
## 10. 实现顺序建议
1. 类型 + 内置常量 + `chapter-template.ts` + 快速新建 UI
2. `SubmissionFormatter` + ExportService + ExportModal + 设置 Tab
3. InspirationModal 增强(独立、改动小)
4. ImportService + ImportBookModal(依赖 mammoth/marked,放最后)
**目标版本号**:实现完成后 `package.json` / README / 关于页 → **v1.1.0**
---
## 11. Spec 自检
| 检查项 | 状态 |
|--------|------|
| 无 TBD/TODO 占位 | ✅ |
| 与方案 A 四模块一致 | ✅ |
| `.novel` 明确排除 | ✅ |
| 单章投稿导出范围明确 | ✅ |
| IPC/数据模型无矛盾 | ✅ |
| 测试用例覆盖四模块 | ✅ |