docs: add P2.1 and P3 design spec for v0.3.0
Covers P2 backlog (chapter drag, search replace, inspiration voice, mention jump, reference pin) and strict P3-A (LM Studio chat, context injection, offline fallback, naming check) with no-mock testing requirements. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,545 @@
|
||||
# 笔临 P2.1 + P3 设计规格
|
||||
|
||||
**版本**:1.0
|
||||
**日期**:2026-07-07
|
||||
**范围**:P2 遗留补完 + P3 AI 基础(严格 P3-A:对话模式实装,写作模式 Tab 占位)
|
||||
**前置**:P2 已交付(v0.2.0,`aac51bf`)
|
||||
**参考**:`design/readme.md` v1.1 §5.3.8 / §5.6 / §6.1–§6.2 / §6.8–§6.9、`design/ui_pc.html`
|
||||
|
||||
**目标版本**:v0.3.0
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与目标
|
||||
|
||||
### 1.1 阶段定位
|
||||
|
||||
P2.1 补齐 P2 规格中明确延期的交互能力(章节拖拽、搜索替换、灵感语音、`@` 跳转、引用钉住)。P3 在 v0.2.0 信息管理能力之上,交付 **可对话的本地 AI 助手**:会话持久化、上下文注入、快捷命令、离线降级、命名一致性检查,以及 `ai` 类型快照。
|
||||
|
||||
### 1.2 已确认决策(Brainstorming 2026-07-07)
|
||||
|
||||
| 维度 | 决策 |
|
||||
|------|------|
|
||||
| 范围 | **P2.1 全量** + **P3 严格(选项 A)** |
|
||||
| P3 写作模式 | 「对话」实装;「交互 / 自动 / 向导」Tab **仅 UI 占位** → P4 |
|
||||
| AI 后端 | **LM Studio**(OpenAI 兼容 API),开发/测试/E2E 统一配置 |
|
||||
| 服务地址 | `http://127.0.0.1:1234` |
|
||||
| 模型 | `gemma-4-e4b-it` |
|
||||
| 测试原则 | **禁止 Mock**;不得以「本地服务未启动」为由 skip 测试 |
|
||||
| 架构 | **主进程直连**(`AiClientService` + IPC 流式推送) |
|
||||
| 平台 | Windows 优先(延续 P0–P2) |
|
||||
|
||||
### 1.3 验收标准
|
||||
|
||||
用户能完成:
|
||||
|
||||
1. 左栏章节同卷/跨卷拖拽排序,重启后顺序保持
|
||||
2. 全局搜索:输入关键词 → 预览替换影响范围 → 确认后批量替换章节内容
|
||||
3. `Ctrl+Shift+I` 打开灵感捕获模态,文字/语音输入后保存到灵感列表
|
||||
4. 点击正文中 `@设定名` 跳转到对应设定编辑
|
||||
5. 引用面板钉住后保持可见,可与 AI 右栏互斥规则共存
|
||||
6. 设置页配置 LM Studio 地址与模型;右栏 AI 面板新建会话、发送消息、流式收到回复
|
||||
7. 勾选章节/大纲/设定/灵感作为上下文,点击「更新上下文」后下一轮对话携带最新摘要
|
||||
8. 使用 `/续写` 等快捷命令触发预设 prompt
|
||||
9. 云端后端配置下断网时 AI 输入禁用并显示离线横幅;LM Studio 配置下断网仍可用
|
||||
10. 知识库 Tab 运行命名一致性检查,逐条确认后可批量替换
|
||||
11. AI 内容写入章节前自动创建 `type='ai'` 快照
|
||||
|
||||
### 1.4 本规格不实现(明确排除)
|
||||
|
||||
| 项 | 归属 |
|
||||
|----|------|
|
||||
| §6.5 交互 / 自动 / 向导写作全流程 | P4 |
|
||||
| §6.3 文风学习与风格滑杆 | P4 |
|
||||
| §6.4 Skill / MCP 扩展 | P4+ |
|
||||
| §6.6 大纲生成章节骨架 | P4 |
|
||||
| §6.7 知识自动抽取与审核工作流(列表 UI) | P4 |
|
||||
| `knowledge_entries` / `timeline_events` 表 | P4/P5/P6 |
|
||||
| 导入导出(`#importModal`) | P7 |
|
||||
| 驾驶舱、章间衔接、更新计划 | P5 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构
|
||||
|
||||
### 2.1 进程模型(延续 P2)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 主进程 │
|
||||
│ · node:sqlite(每书 .sqlite,schema v3) │
|
||||
│ · AiClientService → fetch LM Studio /v1/chat/completions│
|
||||
│ · AiSessionRepository / AiContextBuilder │
|
||||
│ · NetworkMonitor → 推送 online/offline │
|
||||
│ · 既有 FTS / Search / Snapshot / Chapter 服务 │
|
||||
│ · IPC Handlers(IpcResult 信封 + ai:stream-chunk 事件) │
|
||||
└───────────────────────────┬─────────────────────────────┘
|
||||
│ contextBridge
|
||||
┌───────────────────────────▼─────────────────────────────┐
|
||||
│ 渲染进程 │
|
||||
│ · AiPanel(替换 RightPanel AI 占位) │
|
||||
│ · ContextEditorModal / NamingCheckModal │
|
||||
│ · InspirationModal(全局快捷键) │
|
||||
│ · useAiStore(会话、流式缓冲、disabled) │
|
||||
│ · SearchModal 替换区 / 章节 drag-and-drop │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**约束**(延续 P0–P2):
|
||||
|
||||
- 禁止渲染进程直接 `fetch` AI API;所有推理请求经主进程
|
||||
- API Key 仅存 `global_settings.json`(主进程读写)
|
||||
- 流式输出通过 `webContents.send(IPC.AI_STREAM_CHUNK, payload)` 推送,不阻塞 IPC invoke
|
||||
|
||||
### 2.2 AI 客户端(推荐方案)
|
||||
|
||||
主进程 `AiClientService`:
|
||||
|
||||
- 使用 Node.js 内置 `fetch` 调用 OpenAI Chat Completions 兼容端点
|
||||
- 支持 `stream: true`;解析 SSE `data: {...}` 行
|
||||
- `AbortController` 支持 `ai:abort`
|
||||
- 超时默认 60s(`GlobalSettings.aiConfig.timeoutMs`)
|
||||
- 错误映射为 `IpcError`,含 `recoverable: true`(超时/连接拒绝)
|
||||
|
||||
**默认配置**(`global_settings.json`):
|
||||
|
||||
```typescript
|
||||
interface AiConfig {
|
||||
backend: 'lmstudio' | 'openai' | 'anthropic'
|
||||
baseUrl: string // 默认 'http://127.0.0.1:1234'
|
||||
model: string // 默认 'gemma-4-e4b-it'
|
||||
apiKey?: string // openai/anthropic 时使用;lmstudio 可空
|
||||
timeoutMs: number // 默认 60_000
|
||||
maxTokens: number // 默认 2048
|
||||
}
|
||||
```
|
||||
|
||||
**环境变量覆盖**(测试/CI,可选):
|
||||
|
||||
| 变量 | 作用 |
|
||||
|------|------|
|
||||
| `BILIN_AI_BASE_URL` | 覆盖 `baseUrl` |
|
||||
| `BILIN_AI_MODEL` | 覆盖 `model` |
|
||||
|
||||
未设置时使用上述默认值;**测试不得 skip**。
|
||||
|
||||
### 2.3 里程碑
|
||||
|
||||
| 阶段 | 交付 | 周期 |
|
||||
|------|------|------|
|
||||
| **P2.1a** | 章节拖拽 + `chapter:move` IPC | ~2 天 |
|
||||
| **P2.1b** | 搜索替换 UI + E2E | ~1 天 |
|
||||
| **P2.1c** | 灵感模态 + 语音 + `@` 跳转 + 引用钉住 | ~2 天 |
|
||||
| **P3.0** | Schema v3 + AiClientService + AI 设置页 | ~2 天 |
|
||||
| **P3.1** | 会话 CRUD + 消息持久化 + 流式 IPC | ~3 天 |
|
||||
| **P3.2** | 上下文编辑器 + 快捷命令 | ~2 天 |
|
||||
| **P3.3** | 离线降级 + 命名检查 + AI 快照 | ~2 天 |
|
||||
| **P3.4** | 全量测试 + v0.3.0 | ~1 天 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据模型
|
||||
|
||||
### 3.1 Schema v3 迁移
|
||||
|
||||
`schema_version` 2 → 3。增量 `schema-v3.sql` + `migrate.ts` 扩展。
|
||||
|
||||
```sql
|
||||
CREATE TABLE ai_sessions (
|
||||
id TEXT PRIMARY KEY,
|
||||
title TEXT NOT NULL DEFAULT '',
|
||||
model TEXT NOT NULL DEFAULT '',
|
||||
archived INTEGER NOT NULL DEFAULT 0,
|
||||
context_json TEXT NOT NULL DEFAULT '{}',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
CREATE TABLE ai_messages (
|
||||
id TEXT PRIMARY KEY,
|
||||
session_id TEXT NOT NULL,
|
||||
role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
|
||||
content TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
FOREIGN KEY (session_id) REFERENCES ai_sessions(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX idx_ai_messages_session ON ai_messages(session_id, created_at);
|
||||
```
|
||||
|
||||
**说明**:`ai_sessions` 存于**每本书**的 `.sqlite`(与章节等同库),非全局。
|
||||
|
||||
**`context_json` 结构**:
|
||||
|
||||
```typescript
|
||||
interface AiContextBinding {
|
||||
chapterIds: string[]
|
||||
outlineIds: string[]
|
||||
settingIds: string[]
|
||||
inspirationIds: string[]
|
||||
}
|
||||
```
|
||||
|
||||
**全局 userData**(`global_settings.json` 扩展,非书籍 DB):
|
||||
|
||||
```typescript
|
||||
interface GlobalSettings {
|
||||
// ...existing P2 fields
|
||||
aiConfig: AiConfig
|
||||
namingIgnoreWords: string[]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 共享类型扩展(`src/shared/types.ts`)
|
||||
|
||||
```typescript
|
||||
export type AiBackend = 'lmstudio' | 'openai' | 'anthropic'
|
||||
export type AiMessageRole = 'user' | 'assistant' | 'system'
|
||||
export type AiWritingMode = 'chat' | 'interactive' | 'auto' | 'wizard' // UI only; P3 仅 chat 实装
|
||||
|
||||
export interface AiSession {
|
||||
id: string
|
||||
title: string
|
||||
model: string
|
||||
archived: boolean
|
||||
context: AiContextBinding
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface AiMessage {
|
||||
id: string
|
||||
sessionId: string
|
||||
role: AiMessageRole
|
||||
content: string
|
||||
createdAt: string
|
||||
}
|
||||
|
||||
export interface NamingConflict {
|
||||
termA: string
|
||||
termB: string
|
||||
confidence: number
|
||||
reason: string
|
||||
suggestedCanonical?: string
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. IPC 通道
|
||||
|
||||
### 4.1 新增常量(`ipc-channels.ts`)
|
||||
|
||||
```typescript
|
||||
// Chapter
|
||||
CHAPTER_MOVE: 'chapter:move',
|
||||
|
||||
// AI
|
||||
AI_SESSION_LIST: 'ai:sessionList',
|
||||
AI_SESSION_CREATE: 'ai:sessionCreate',
|
||||
AI_SESSION_UPDATE: 'ai:sessionUpdate',
|
||||
AI_SESSION_DELETE: 'ai:sessionDelete',
|
||||
AI_MESSAGE_LIST: 'ai:messageList',
|
||||
AI_CHAT: 'ai:chat',
|
||||
AI_ABORT: 'ai:abort',
|
||||
AI_BUILD_CONTEXT: 'ai:buildContext',
|
||||
AI_NAMING_CHECK: 'ai:namingCheck',
|
||||
AI_STREAM_CHUNK: 'ai:streamChunk', // main → renderer event
|
||||
AI_NETWORK_STATUS: 'ai:networkStatus', // main → renderer event
|
||||
```
|
||||
|
||||
### 4.2 Handler 契约
|
||||
|
||||
| 通道 | 参数 | 返回 |
|
||||
|------|------|------|
|
||||
| `chapter:move` | `{ bookId, chapterId, volumeId, sortOrder }` | `Chapter` |
|
||||
| `ai:sessionList` | `{ bookId, includeArchived? }` | `AiSession[]` |
|
||||
| `ai:sessionCreate` | `{ bookId, title? }` | `AiSession` |
|
||||
| `ai:sessionUpdate` | `{ bookId, sessionId, patch }` | `AiSession` |
|
||||
| `ai:sessionDelete` | `{ bookId, sessionId }` | `void` |
|
||||
| `ai:messageList` | `{ bookId, sessionId }` | `AiMessage[]` |
|
||||
| `ai:chat` | `{ bookId, sessionId, content, slashCommand? }` | `{ messageId }` |
|
||||
| `ai:abort` | `{ messageId }` | `void` |
|
||||
| `ai:buildContext` | `{ bookId, binding }` | `{ preview: ContextPreviewItem[] }` |
|
||||
| `ai:namingCheck` | `{ bookId }` | `NamingConflict[]` |
|
||||
|
||||
**`ai:chat` 流程**:
|
||||
|
||||
1. 持久化 user message
|
||||
2. `AiContextBuilder` 组装 messages 数组(system + 历史 + user)
|
||||
3. 调用 `AiClientService.chatStream(...)`
|
||||
4. 流式推送 `ai:streamChunk`;完成后持久化 assistant message
|
||||
|
||||
---
|
||||
|
||||
## 5. P2.1 功能规格
|
||||
|
||||
### 5.1 章节拖拽排序
|
||||
|
||||
**UI**:左栏章节列表项 `draggable`;拖拽指示线(同卷内)。
|
||||
|
||||
**逻辑**:
|
||||
|
||||
- 同卷:重算 `sort_order`(0..n-1),批量 `chapter:move` 或单次 `chapter.repo.move`
|
||||
- 跨卷:更新 `volume_id` + 目标卷末尾 `sort_order`
|
||||
- 拖拽结束触发 `flushEditorSave()`(若当前章节被移动)
|
||||
|
||||
**Repository**:
|
||||
|
||||
```typescript
|
||||
move(chapterId: string, volumeId: string, sortOrder: number): Chapter
|
||||
```
|
||||
|
||||
### 5.2 全局搜索替换
|
||||
|
||||
对齐 `design/readme.md` §5.6.1 底部替换区。
|
||||
|
||||
**UI**(`SearchModal` 扩展):
|
||||
|
||||
- 折叠区「替换」:替换文本输入 + 「预览变更」+ 「执行替换」
|
||||
- 预览调用 `search:replace(dryRun: true)` → 列表展示受影响条目
|
||||
- 确认后 `search:replace(dryRun: false)` → toast 显示替换数量
|
||||
- 替换后刷新 `useBookStore` 章节列表;若当前章被替换则刷新编辑器
|
||||
|
||||
**范围**:P2 已支持全书;本阶段补 UI,不扩展 scope select(可后续加)。
|
||||
|
||||
### 5.3 灵感快速捕获
|
||||
|
||||
**UI**:`#inspirationModal`(对齐 `ui_pc.html`)
|
||||
|
||||
- 标题、内容、标签(逗号分隔)
|
||||
- 语音按钮:`webkitSpeechRecognition`(`zh-CN`),实时填入内容框
|
||||
- 保存 → `inspiration:create` + 可选 `switchTarget`
|
||||
- 不打断编辑器:关闭后焦点回 `.ProseMirror`
|
||||
|
||||
**快捷键**:`Ctrl+Shift+I`(`captureInspiration` 已有 handler,改为打开模态而非直接 create)
|
||||
|
||||
### 5.4 `@提及` 反向跳转
|
||||
|
||||
`SettingMention` Mark 增加 `onClick` / `editorProps` 处理:
|
||||
|
||||
- 点击带 `data-setting-mention` 的 span → `switchTarget({ kind: 'setting', id })`
|
||||
- 光标样式 `pointer`
|
||||
|
||||
### 5.5 引用面板钉住
|
||||
|
||||
`useReferenceStore` 扩展:
|
||||
|
||||
```typescript
|
||||
pinned: boolean
|
||||
setPinned(open: boolean): void
|
||||
```
|
||||
|
||||
- 钉住按钮(📌)在 `ref-header`
|
||||
- `pinned === true` 时 `setOpen(false)` 不隐藏面板
|
||||
- 与 AI 右栏互斥:引用打开时仍隐藏 RightPanel(延续 P2);钉住引用 + 需 AI 时用户手动关闭引用
|
||||
|
||||
---
|
||||
|
||||
## 6. P3 功能规格
|
||||
|
||||
### 6.1 AI 面板 UI(`AiPanel.tsx`)
|
||||
|
||||
对齐 `ui_pc.html` `#ai-chat`:
|
||||
|
||||
| 区域 | 行为 |
|
||||
|------|------|
|
||||
| 会话栏 | `<select>` + 新建按钮;重命名(双击 inline);右键归档/删除 |
|
||||
| 模式 Tab | 对话 **active**;交互/自动/向导 → `feature.comingSoon` |
|
||||
| 上下文预览行 | 显示摘要计数;点击打开 `ContextEditorModal` |
|
||||
| 离线横幅 | `aiStore.disabled` 时显示 `#offlineBanner` |
|
||||
| 消息区 | user/assistant 气泡;assistant 流式追加 |
|
||||
| 快捷命令 | `/续写` `/扩写` `/润色` `/总结` `/纠错` chip 点击填入 |
|
||||
| 输入区 | Enter 发送;生成中禁用 + 「停止」调 `ai:abort` |
|
||||
| 插入编辑器 | assistant 消息旁「插入」→ 纯文本插入章节光标(触发 AI 快照) |
|
||||
|
||||
### 6.2 上下文注入(`AiContextBuilder`)
|
||||
|
||||
**源数据**:
|
||||
|
||||
| 类型 | 提取内容 | 单条上限 |
|
||||
|------|----------|----------|
|
||||
| chapter | `title` + `stripHtml(content)` 截断 | 2000 字符 |
|
||||
| outline | `title` + `description` 截断 | 500 字符 |
|
||||
| setting | `name` + `description` 截断 | 500 字符 |
|
||||
| inspiration | `title` + `content` 截断 | 500 字符 |
|
||||
|
||||
**总量上限**:~16KB plain text;超出时按 sort_order 新近优先截断。
|
||||
|
||||
**System prompt 模板**(i18n):
|
||||
|
||||
```
|
||||
你是笔临 AI 写作助手。作者笔名:{penName}。书籍:{bookName}。
|
||||
以下是与本书相关的上下文(由作者勾选):
|
||||
{contextBlocks}
|
||||
请用简体中文回答,尊重作者的创作主权。
|
||||
```
|
||||
|
||||
**「更新上下文」**:重新 `ai:buildContext` 并更新 `context_json` 勾选不变;仅刷新预览摘要。
|
||||
|
||||
### 6.3 快捷命令
|
||||
|
||||
| 命令 | 预设意图 |
|
||||
|------|----------|
|
||||
| `/续写` | 基于当前章节末尾续写 200–400 字 |
|
||||
| `/扩写` | 扩展选中或最近一段描写 |
|
||||
| `/润色` | 改善文笔,保持情节不变 |
|
||||
| `/总结` | 总结当前章节要点 |
|
||||
| `/纠错` | 指出语法/标点问题 |
|
||||
|
||||
实现:解析以 `/` 开头的输入 → 映射 i18n 模板 → 作为 user message 发送(UI 仍显示原命令)。
|
||||
|
||||
### 6.4 离线降级(§6.8)
|
||||
|
||||
**主进程 `NetworkMonitor`**:
|
||||
|
||||
- 监听 `net.isOnline()` + 定时探测(可选 `HEAD` 到 `https://1.1.1.1`)
|
||||
- 状态变化 → `AI_NETWORK_STATUS`
|
||||
|
||||
**渲染进程规则**:
|
||||
|
||||
```typescript
|
||||
const disabled =
|
||||
settings.aiConfig.backend !== 'lmstudio' && !networkOnline
|
||||
```
|
||||
|
||||
- `disabled` 时:输入框 `disabled`,快捷命令灰显,横幅可见
|
||||
- `lmstudio` 后端:**永不**因离线禁用
|
||||
- 网络恢复:toast `ai.networkRestored`
|
||||
|
||||
### 6.5 命名一致性检查(§5.6.2)
|
||||
|
||||
**入口**:知识库 Tab 顶部按钮「命名一致性检查」
|
||||
|
||||
**流程**:
|
||||
|
||||
1. 收集 `settings[].name` + 全书章节 plainText
|
||||
2. 构造 prompt,要求模型返回 **纯 JSON 数组** `NamingConflict[]`
|
||||
3. `NamingCheckModal` 列表:termA / termB / 置信度 / 原因
|
||||
4. 操作:忽略(加入 `namingIgnoreWords`)、替换为 canonical(调 `search:replace`)
|
||||
5. 解析失败:toast 错误,展示原始响应片段(调试用)
|
||||
|
||||
### 6.6 AI 快照(§5.4.1)
|
||||
|
||||
触发点:
|
||||
|
||||
- `AiPanel`「插入到编辑器」
|
||||
- 任意将 AI 生成内容写入 `chapters.content` 的路径
|
||||
|
||||
行为:`snapshot:create(chapterId, 'ai', 'AI 生成前', currentContent)` → 再写入新内容。
|
||||
|
||||
### 6.7 AI 设置页
|
||||
|
||||
**位置**:系统设置 → 新 Tab「AI 配置」
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| 后端类型 | select: LM Studio / OpenAI / Anthropic |
|
||||
| 服务地址 | text(LM Studio 默认 `http://127.0.0.1:1234`) |
|
||||
| 模型 | text(默认 `gemma-4-e4b-it`) |
|
||||
| API Key | password(云端后端时显示) |
|
||||
| 超时 | number,秒 |
|
||||
|
||||
**§6.9 许可提示**:选择云端后端时显示服务条款链接文案。
|
||||
|
||||
**测试连接**按钮:发送最小 chat 请求,toast 成功/失败。
|
||||
|
||||
---
|
||||
|
||||
## 7. 测试
|
||||
|
||||
### 7.1 原则
|
||||
|
||||
- **禁止 Mock** `fetch` / AI 响应
|
||||
- **禁止** `test.skipIf(!aiAvailable)` 或类似条件跳过
|
||||
- 集成/E2E 必须对 `http://127.0.0.1:1234` 发起真实请求
|
||||
- 服务未启动时测试 **失败**,迫使环境就绪
|
||||
|
||||
### 7.2 单元 / 集成
|
||||
|
||||
| 文件 | 场景 |
|
||||
|------|------|
|
||||
| `tests/main/migrate-v3.test.ts` | v2→v3 迁移 |
|
||||
| `tests/main/ai-session.repo.test.ts` | 会话/消息 CRUD |
|
||||
| `tests/main/chapter.repo.test.ts` | `move` 排序 |
|
||||
| `tests/main/ai-client.test.ts` | 真实 LM Studio 请求,响应非空 |
|
||||
| `tests/main/ai-context-builder.test.ts` | 上下文截断与拼接 |
|
||||
| `tests/main/naming-check.test.ts` | JSON 解析(mock 仅解析器,不 mock HTTP) |
|
||||
|
||||
### 7.3 E2E
|
||||
|
||||
| ID | 场景 |
|
||||
|----|------|
|
||||
| E2E-P21-DRAG-01 | 拖拽章节改变顺序,重启后保持 |
|
||||
| E2E-P21-REP-01 | 搜索 → 预览替换 → 执行 → 正文更新 |
|
||||
| E2E-P21-INSP-01 | Ctrl+Shift+I 模态保存灵感 |
|
||||
| E2E-P3-CHAT-01 | 新建会话 → 发送「你好」→ assistant 非空 |
|
||||
| E2E-P3-CTX-01 | 勾选设定 → 更新上下文 → 摘要行更新 |
|
||||
| E2E-P3-CMD-01 | 点击 `/总结` → 发送 → 收到回复 |
|
||||
| E2E-P3-NAME-01 | 命名检查模态出现 ≥1 行(需种子数据) |
|
||||
| E2E-P3-OFF-01 | 配置 openai + 模拟离线 → 输入禁用(可用 evaluate 设 `aiStore`) |
|
||||
|
||||
### 7.4 手动验收
|
||||
|
||||
1. LM Studio 运行 `gemma-4-e4b-it`,笔临设置页「测试连接」成功
|
||||
2. 右栏多轮对话,重启后会话与消息保留
|
||||
3. 上下文勾选后 AI 回答能引用设定名称
|
||||
4. 命名检查对「林远/林悦」类冲突给出建议
|
||||
|
||||
---
|
||||
|
||||
## 8. UI / CSS
|
||||
|
||||
- 新增/扩展 `layout.css`:`.ai-session-bar`、`.ai-mode-tabs`、`.chat-messages`、`.chat-msg`、`.quick-cmds`、`.offline-banner`、`.naming-modal`
|
||||
- 对齐 `design/ui_pc.html` 行 1203–2076 结构与 class 命名
|
||||
- 灵感模态对齐 `#inspirationModal`
|
||||
- i18n:`ai.*`、`naming.*`、`inspiration.voice*` 等键
|
||||
|
||||
---
|
||||
|
||||
## 9. 风险与缓解
|
||||
|
||||
| 风险 | 缓解 |
|
||||
|------|------|
|
||||
| LM Studio 未启动导致 CI 失败 | 文档明确前置条件;本地开发脚本检测端口 |
|
||||
| 流式 IPC 消息乱序 | `messageId` 关联;渲染进程按 id 缓冲 |
|
||||
| 大上下文超 token | 硬限制 16KB + 截断策略 |
|
||||
| 命名检查 JSON 解析失败 | 重试一次;失败 toast + 日志 |
|
||||
| Web Speech API 不可用 | 语音按钮 disabled + tooltip;E2E 不测语音 |
|
||||
| `gemma-4-e4b-it` 模型名变更 | 环境变量 `BILIN_AI_MODEL` 覆盖 |
|
||||
|
||||
---
|
||||
|
||||
## 10. Definition of Done(v0.3.0)
|
||||
|
||||
- [ ] P2.1a–c + P3.0–3 全部里程碑完成
|
||||
- [ ] §7 测试全部 PASS(含真实 LM Studio 集成测试)
|
||||
- [ ] `npm run build` 无 error
|
||||
- [ ] 交互/自动/向导 Tab 为占位,不 crash
|
||||
- [ ] Windows 手动验收 §7.4 四项通过
|
||||
|
||||
---
|
||||
|
||||
## Spec Self-Review
|
||||
|
||||
| 检查项 | 结果 |
|
||||
|--------|------|
|
||||
| Placeholder 扫描 | 交互/自动/向导/知识库列表明确 P4;无 TBD |
|
||||
| 内部一致性 | AI 主进程直连贯穿 IPC/测试;context_json 与 AiContextBinding 对齐 |
|
||||
| 范围 | 单规格可支撑一份 implementation plan;P2.1 与 P3 边界清晰 |
|
||||
| 歧义 | P3-A 已确认;LM Studio 默认值与环境变量覆盖已写明 |
|
||||
| 测试 | 禁止 Mock/skip 已作为硬性原则写入 §7.1 |
|
||||
|
||||
---
|
||||
|
||||
## 附录:与 P2 规格差异
|
||||
|
||||
| 项 | P2 规格 | 本规格 |
|
||||
|----|---------|--------|
|
||||
| 搜索替换 UI | P2.6 规划 | P2.1b 实装 |
|
||||
| 灵感语音 | P2.1 占位 | P2.1c 实装 |
|
||||
| 章节拖拽 | P2.1 | P2.1a 实装 |
|
||||
| AI 命名检查 | P3 占位 | P3.3 实装 |
|
||||
| ai 快照 | P3 | P3.3 实装 |
|
||||
| ai_sessions 表 | P3/P5/P6 | P3.0 实装(每书 DB) |
|
||||
| 写作模式 | — | P3 UI 占位,P4 实装 |
|
||||
Reference in New Issue
Block a user