Skip to content

Commit 9866c50

Browse files
committed
docs(version): v0.11.7 图文会话设计 spec 与版本文档落位
1 parent 11d79d7 commit 9866c50

4 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
# v0.11.7 设计:图文会话——截屏导入 + 跨链路采集体验同步
2+
3+
> 状态:已批准(用户确认方案 A + kind 字段 + 改进采纳,2026-08-22)
4+
> 依据:课堂助手现有采集动线(实时捕获 / 视频文件导入 / 学习素材)· ADR-004(会话数据模型)· ADR-008(文件导入)· ADR-015(屏卡体系)
5+
> 痛点:图文类内容(网页/PDF/图文教程)无采集路径——视频导入要求视频文件、实时捕获面向视频窗口,图文场景只能靠"选图片"素材走无会话流水线,无法形成会话、无法事后复盘
6+
> 范围:v0.11.7(排期于 v0.11.6 AI 平台化之后)
7+
> 版本规划:本 spec 配套 [v0.11.7 版本文档](../../versions/v0.11.7.md) 与 [v0.11 系列总览](../../versions/v0.11.md) 更新
8+
9+
## 一、总览
10+
11+
课堂助手新增第三采集动线「图文采集」:用户连续框选截屏 → 组成一个 `kind=photo` 的图文会话(无音频)→ OCR 全图识别 → 会话列表可见、可转笔记。同步两项跨链路采集体验改进到视频导入(完成后直达会话页 + OCR 进度携带识别块数)。
12+
13+
```
14+
图文采集面板(课堂助手左栏第三面板)
15+
▶ 开始图文采集 → start_photo_session(创建 recording 会话)
16+
📷 截屏 → capture_screen_snapshot(全屏快照 base64)
17+
→ ScreenSelectOverlay 全屏遮罩框选 → canvas 裁剪 → PNG base64
18+
→ save_photo_capture(存图 + OCR + 落库,返回识别预览)
19+
✅ 完成采集 → finish_photo_session(零截图删会话;否则 finish)
20+
```
21+
22+
硬约束(写入每层实现):
23+
24+
- **数据主权**:截屏内容全部本地处理(GDI 捕获 → 本地 OCR → 本地存储),不出本机
25+
- **三链路 OCR 口径统一**:图文链路与实时/导入链路同口径——OCR 输入缩至 960px 宽、bbox 反算回原图坐标系、低分过滤(score<0.5)
26+
- **截图不过 UI 垃圾黑名单**:用户显式框选的内容即意图(与自动采帧链路不同),`is_ui_junk` 源头过滤不应用于图文截图
27+
- **互斥**:图文采集与实时捕获互斥(共享屏幕内容动线,避免状态混乱)
28+
- **不留空壳**:完成采集时零截图 → 删除会话;崩溃残留(recording 超 24h)→ 列表加载时清扫为 failed
29+
30+
## 二、数据模型(kind 字段)
31+
32+
`sessions` 表新增 `kind` 列,标识会话类型:
33+
34+
| 值 | 含义 |
35+
|----|------|
36+
| `NULL` | 旧数据 / 实时捕获 / 视频导入(默认,零回归) |
37+
| `'photo'` | 图文会话(截屏采集) |
38+
39+
- **迁移**:`ensure_column(conn, "sessions", "kind", "ALTER TABLE sessions ADD COLUMN kind TEXT")`(幂等,`profile` 列同模式);建表 SQL 同步加列
40+
- **类型穿透**:Rust `Session.kind: Option<String>` / `NewSession.kind`;前端 `Session.kind: string | null`
41+
- `create_session` 签名扩展:`kind: Option<&str>`(既有调用方传 `None`,零回归)
42+
43+
## 三、后端命令(commands_photo.rs + photo_capture.rs)
44+
45+
| 命令 | 职责 | 关键逻辑 |
46+
|------|------|----------|
47+
| `start_photo_session(title?)` | 创建图文会话 → 返回 session_id | 互斥检查(`live_session.active` / 已有图文采集中 → Err);默认标题「图文会话 MM-DD HH:mm」 |
48+
| `capture_screen_snapshot()` | 全屏快照 → base64 | `capture::gdi_capture(None)`(虚拟屏合并区域,多显示器支持)→ BGRA → RGB → JPEG q0.85 编码 → `{ base64, width, height }` |
49+
| `save_photo_capture(session_id, image_b64)` | 截图落库 + OCR | 解码校验 → `SessionImageStore.save_frame`(full+thumb,双指纹去重,命中返回"重复")→ 全图 OCR(缩 960px 有界等待)→ OCR 块落库(region=full,screen_id=None 走视图层聚类)→ 返回 `{ duplicated, block_count, preview: [String; ≤3] }` |
50+
| `finish_photo_session(session_id)` | 完成采集 | 零截图 → 删除会话(含图片库);否则 `finish_session` |
51+
| `discard_photo_session(session_id)` | 放弃采集 | 删除会话(级联子表 + 清理图片库目录) |
52+
53+
### 3.1 关键实现点
54+
55+
- **OCR 输入**:保存原图尺寸到 `full/<ts>.webp`(屏卡显示清晰);OCR 用缩至 960px 的副本(复用 `import_frame::crop_and_scale` 同款缩放开销);bbox 按缩放比反算回原图坐标系(TD-046 同思路)
56+
- **OCR 失败降级**:不阻断截图保存——图已落盘,返回 `block_count=0` + 提示;预览取识别文本前 3 条(按置信度排序,去空)
57+
- **重复截图**:`save_frame` 双指纹命中 → 不落新 OCR 块,返回 `duplicated=true` 提示用户(防误触)
58+
- **残留清扫**:会话列表加载时 `UPDATE sessions SET status='failed' WHERE kind='photo' AND status='recording' AND started_at < now-24h`(24h 保证不误伤进行中的采集);清扫函数注入 `&Db` 可单测
59+
- **互斥注册**:`AppState` 增 `photo_session: Arc<Mutex<Option<i64>>>`;`start_photo_session` 检查并占用;`finish/discard` 释放;实时捕获 `start_live` 增加图文占用检查
60+
61+
## 四、前端交互
62+
63+
### 4.1 图文采集面板(新组件 `PhotoCapturePanel.tsx`,课堂助手左栏)
64+
65+
三态动线(仿实时捕获面板结构):
66+
67+
```
68+
空闲态: [▶ 开始图文采集]
69+
采集中: 会话 #12 采集中 · 已截 3 张
70+
[🖼 最近截图缩略图](convertFileSrc 预览 full/<ts>.webp)
71+
[📷 截屏] [✅ 完成采集] [放弃]
72+
完成态: ✅ 已保存会话 #12(3 张截图)
73+
[去会话页查看](复用 onOpenSessions prop)
74+
```
75+
76+
- 状态机:`idle → collecting → done`;互斥时开始按钮禁用 + 原因提示
77+
- 截屏后显示识别反馈:「已识别 8 块:预览文本…」;`duplicated=true` 显示「与上一张内容相同,未重复保存」
78+
- 完成采集后 `onStatus` 上抛 + 「去会话页」直达
79+
80+
### 4.2 全屏框选遮罩(新组件 `ScreenSelectOverlay.tsx`)
81+
82+
复用 `BoxSelectOverlay` 交互范式(拖框 + 确认浮层 + CSS 放大镜预览):
83+
84+
- 快照图 **letterbox 缩放适配**显示(窗口内 fixed 覆盖,第一版不做真实全屏窗口——YAGNI)
85+
- **DPI 换算**:归一化坐标按**图像物理像素**换算(Windows 125%/150% 缩放下 GDI 捕获为物理像素、CSS 显示为逻辑像素——`selectedPixels = normalized × imagePhysicalSize`,不经 window.devicePixelRatio)
86+
- 确认 → canvas 裁剪(物理像素矩形)→ **PNG 编码**(无损,OCR 友好;快照显示用 JPEG 仅省流量)→ base64 → `save_photo_capture`
87+
- Esc / ✕ 取消关闭;保存中禁用交互防重复提交
88+
89+
## 五、列表/详情/转笔记适配
90+
91+
| 位置 | 改动 |
92+
|------|------|
93+
| `SessionListPanel` | 列表项类型徽标:`kind === "photo"` → 「📷 图文」(`Session.kind` 数据源) |
94+
| `SessionDetailPanel` | 空 segments 空态文案:「本会话无讲述内容(图文采集)」;屏卡流/画面要点/结构图**零改动**(`build_screens` 聚类 + `attach_images` 配图对导入链路已生效,图文会话同路径) |
95+
| 转笔记 | `session_to_note` / `preview_session_note` **零改动**(status=finished 即可,纯 OCR 拼接);实现期补 `filter_note` 空 segments 测试验证(不 panic、产出画面要点笔记) |
96+
97+
## 六、跨链路采集体验同步(视频导入)
98+
99+
| # | 同步项 | 改动 |
100+
|---|--------|------|
101+
| 1 | 导入完成后「去会话页」直达 | `VideoImportPanel` 增加 `onOpenSessions` prop;完成态文案旁加直达按钮(与图文面板共用交互) |
102+
| 2 | OCR 进度携带识别块数 | `import.rs` ocr 阶段进度消息「画面识别 x/y」→「画面识别 x/y · 已识别 N 块文字」(累计块数经 `&mut` 计数传入 `ocr_keyframes` 回调) |
103+
104+
明确**不同步**(核实结论):
105+
106+
- PNG 无损:实时链路原始帧直喂 OCR、视频导入 ffmpeg 抽帧 PNG——天然无损,仅图文链路(canvas 裁剪环节)需要
107+
- 960px 缩放:三链路已具备(`live_frame_process.rs MAX_OCR_WIDTH=960` / `import_frame.rs crop_and_scale 960`)——图文链路遵循既有口径即可
108+
- 重复提示:自动链路静默去重(双指纹/帧间文本去重)与手动链路(提示用户)交互性质不同
109+
- DPI 换算:实时链路窗口矩形已用物理像素、屏卡框选为图像坐标——各链路已正确
110+
111+
## 七、错误处理与降级
112+
113+
| 场景 | 行为 |
114+
|------|------|
115+
| 截屏失败(GDI 不可用/远程桌面) | 遮罩不打开,状态条显示原因,会话不受影响 |
116+
| 保存失败(解码失败/超预算) | 提示重试;不破坏会话状态 |
117+
| OCR 失败/超时 | 图已落盘,`block_count=0` + 提示(下张截图重试语义) |
118+
| base64 解码失败 | 前置拒绝(可诊断错误,不落盘垃圾——`capture_fragment` 同模式) |
119+
| 应用崩溃残留 recording | 24h 清扫 → failed(列表加载时) |
120+
| 采集完成零截图 | 删除会话(不留空壳) |
121+
122+
## 八、测试计划
123+
124+
**Rust(内存库 + 临时目录端到端,仿 `commands_refine_inner_tests` 范式)**
125+
126+
| # | 用例 |
127+
|---|------|
128+
| 1 | `save_photo_capture` 端到端:base64 → 存图(full+thumb)→ OCR 块落库 → `build_screens` 聚类出屏卡 |
129+
| 2 | 零截图 `finish_photo_session` → 会话删除(含图片目录清理) |
130+
| 3 | 互斥:live active 时 `start_photo_session` 拒绝;图文占用时 `start_live` 拒绝;`finish/discard` 释放占用 |
131+
| 4 | 残留清扫:kind=photo+recording+超 24h → failed;未超时/非 photo 不误伤 |
132+
| 5 | `filter_note` 空 segments:纯 OCR 会话产出画面要点笔记(不 panic、非空) |
133+
| 6 | `create_session` kind 往返 + `ensure_column` 幂等(旧库迁移零回归) |
134+
| 7 | `save_photo_capture` 重复截图(同内容双指纹)→ duplicated=true,不重复落块 |
135+
136+
**前端(Vitest)**
137+
138+
- `PhotoCapturePanel` 状态机(idle/collecting/done + 互斥禁用)
139+
- 坐标换算纯函数(归一化 → 物理像素,含非整数 DPI 边界)
140+
141+
## 九、文件清单
142+
143+
**新增**
144+
145+
- `app/src-tauri/src/photo_capture.rs` — 截图保存 + OCR + 落库编排(业务模块,纯逻辑可测)
146+
- `app/src-tauri/src/commands_photo.rs` — 5 命令 + 互斥 + 残留清扫
147+
- `app/src/components/PhotoCapturePanel.tsx` — 图文采集面板
148+
- `app/src/components/ScreenSelectOverlay.tsx` — 全屏框选遮罩
149+
150+
**修改**
151+
152+
- `app/src-tauri/src/db_migrations.rs` — sessions 加 kind 列(建表 + ensure_column)
153+
- `app/src-tauri/src/types.rs` / `db_sessions.rs` — Session.kind / NewSession.kind / create_session 扩展
154+
- `app/src-tauri/src/lib.rs` — 注册 commands_photo
155+
- `app/src-tauri/src/commands_live.rs` — `start_live_session` 增加图文占用互斥检查
156+
- `app/src-tauri/src/import.rs` — OCR 进度消息携带块数
157+
- `app/src/types/session.ts` — Session.kind
158+
- `app/src/pages/ClassroomPage.tsx` — 挂载 PhotoCapturePanel
159+
- `app/src/components/VideoImportPanel.tsx` — onOpenSessions prop + 直达按钮
160+
- `app/src/components/SessionListPanel.tsx` — 图文徽标
161+
- `app/src/components/SessionDetailPanel.tsx` — 空转写空态(视检查结果)
162+
163+
**文档**
164+
165+
- `docs/versions/v0.11.7.md`(版本文档)+ `docs/versions/README.md` 索引 + `docs/versions/v0.11.md` 系列总览追加
166+
- ADR:会话类型字段(扩展 ADR-004)——随实现提交
167+
168+
## 十、YAGNI 清单(明确不做)
169+
170+
- 单张截图删除/回看网格
171+
- 截图内容类型标注(表格/代码/公式——结构图体系后续可接线)
172+
- OCR 结果在会话内编辑
173+
- 真实全屏窗口模式(letterbox 缩放第一版足够)
174+
- 图文会话音频/转写(图文场景无音频,segments 恒空)
175+
176+
## 关联
177+
178+
- [v0.11.7 版本文档](../../versions/v0.11.7.md) · [v0.11 系列总览](../../versions/v0.11.md) ·
179+
- 复用资产:`capture/gdi_capture.rs`(全屏捕获)· `capture/frame_diff.rs`(裁剪/downscale)· `import_frame.rs`(960px 口径)· `image_store.rs`(full/thumb + 双指纹)· `engine.rs`(OCR 有界等待)· `screens.rs`(屏卡聚类)· `BoxSelectOverlay.tsx`(框选范式)· `capture_fragment`(base64 落盘范式)

‎docs/versions/README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
| v0.10.2 | [v0.10.2.md](./v0.10.2.md) — 结构图准确性重构:参考图集单一分析 + 三层过滤(L0 字幕重叠 / L1 版面类型 / L2 OCR 置信度反向 / L3 位置约束) | 发版文档(规划定稿 2026-08-21) |
2828
| v0.11 | [v0.11.md](./v0.11.md) — 大目标版本系列:笔记组与学习循环(v0.11.0 组基建容器侧 / v0.11.1 feed 进料 / v0.11.2 FSRS 学习循环 / v0.11.3 组结算 / v0.11.4 内容分型+周契约+feed 消费闭环,v4 Phase 1-3 落地) | 系列文档(代码已交付 2026-08-22,真机验收待执行) |
2929
| v0.11.5 | [v0.11.md](./v0.11.md#v0115-交付采集体验与笔记打磨11-项2026-08-22-用户裁决立项) — 采集体验与笔记打磨(档案采集中可改+记忆机制修正 / 识别率增强 / 窗口刷新 / 编号序号化 / 新颖度变化区域 / 水印 A+B+C / 产物视图下线+精修懒自动 / 精修工作台 / 锚点剥离 / 画面要点移出笔记 / 笔记页树形合并+缩略图,2026-08-22 交付) | 版本文档(代码已交付 2026-08-22,真机验收待执行) |
30+
| v0.11.6 | [v0.11.6.md](./v0.11.6.md) — AI 平台化:Provider 抽象 + 能力声明化 + 产出策略定制(三层;M1→M3 排期) | 版本文档(规划中 2026-08-22) |
31+
| v0.11.7 | [v0.11.7.md](./v0.11.7.md) — 图文会话:截屏导入 + 跨链路采集体验同步(kind 字段 + 5 命令 + 框选遮罩;排期于 v0.11.6 之后) | 版本文档(规划中 2026-08-22) |
3032

3133
## 沉淀规则
3234

‎docs/versions/v0.11.7.md‎

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# v0.11.7:图文会话——截屏导入 + 跨链路采集体验同步
2+
3+
> 状态:规划中(2026-08-22 设计已批准,排期于 v0.11.6 AI 平台化之后)
4+
> 依据:[设计 spec](superpowers/specs/2026-08-22-v0.11.7-photo-session-design.md)(已批准)·
5+
> 前置:v0.11.6(AI 平台化)交付后开工
6+
7+
## 一句话
8+
9+
课堂助手新增第三采集动线「图文采集」——连续框选截屏组成图文会话(无音频),补齐网页/PDF/图文教程等图文场景的知识采集空白;同步两项采集体验改进到视频导入。
10+
11+
## 范围
12+
13+
| # | 需求 | 落点 |
14+
|---|------|------|
15+
| 1 | 图文内容可采集为会话 | 截屏导入(全屏快照 → 框选 → 截图落库 + OCR)→ `kind=photo` 图文会话;会话列表/详情/转笔记全链路可用 |
16+
| 2 | 会话类型可区分 | `sessions.kind` 字段(NULL=视频类零回归,photo=图文);列表徽标 + 详情空态适配 |
17+
| 3 | 跨链路采集体验同步 | 视频导入完成后「去会话页」直达按钮;OCR 进度消息携带累计识别块数 |
18+
| 4 | 健壮性 | 图文/实时采集互斥;崩溃残留 24h 清扫;零截图不留空壳;OCR 失败不阻断截图 |
19+
20+
## 交付内容
21+
22+
### M1 · 数据模型 + 后端链路
23+
24+
- `sessions.kind` 列(建表 + `ensure_column` 幂等迁移);Rust/前端类型穿透
25+
- 5 命令:`start_photo_session` / `capture_screen_snapshot` / `save_photo_capture` / `finish_photo_session` / `discard_photo_session`
26+
- `photo_capture.rs` 业务编排:base64 解码校验 → `SessionImageStore.save_frame`(双指纹去重)→ 全图 OCR(960px 口径,bbox 反算)→ OCR 块落库 → 识别预览返回
27+
- 互斥注册(AppState.photo_session)+ 残留清扫(列表加载时 24h 超时 photo recording → failed)
28+
29+
### M2 · 前端交互
30+
31+
- `PhotoCapturePanel`(课堂助手左栏三态面板:开始/截屏/完成 + 最近截图缩略图 + 识别反馈 + 去会话页直达)
32+
- `ScreenSelectOverlay`(全屏框选遮罩:letterbox 缩放 + 物理像素坐标换算 + canvas PNG 裁剪)
33+
- 会话列表「📷 图文」徽标;会话详情空转写空态
34+
- `VideoImportPanel` 直达按钮 + 导入 OCR 进度增强
35+
36+
### M3 · 验证与收尾
37+
38+
- Rust 测试 7 组(端到端/零截图/互斥/清扫/filter_note 空 segments/迁移/重复截图)+ 前端状态机与坐标换算测试
39+
- 文档:ADR(会话类型字段)+ 本版本文档 + 系列总览 + README 索引 + CHANGELOG
40+
- 全量回归:cargo test / clippy / tsc / vitest
41+
42+
## 关键决策与纪律
43+
44+
- **kind 字段而非派生**:会话类型是领域语义,列表徽标/详情空态/清扫都需要——派生判断(如无 segments)脆弱且不可靠
45+
- **前端 canvas 裁剪而非后端**:命令无状态,两次调用间无法保持帧缓存;canvas 裁剪与 base64 通道均有项目先例
46+
- **截图不过 UI 垃圾黑名单**:用户显式框选的内容即意图(与自动采帧链路不同)
47+
- **三链路 OCR 口径统一**:960px 缩放 + bbox 反算 + score≥0.5(实时/导入已具备,图文遵循)
48+
- **互斥**:图文采集与实时捕获共享屏幕内容动线,避免状态混乱
49+
- **PNG 无损裁剪**:快照显示用 JPEG 省流量、裁剪图 PNG 无损(OCR 友好)
50+
- **YAGNI**:单张删除/内容类型标注/OCR 编辑/真实全屏窗口/图文音频——不纳入本期
51+
52+
## 验收标准
53+
54+
1. 课堂助手可开始图文采集 → 连续框选截屏 ≥2 张 → 每张立即显示识别块数与文本预览 → 完成采集后会话出现在会话列表(📷 图文徽标)
55+
2. 图文会话详情页:无讲述内容空态 + 屏卡流完整(标题/正文/配图/标签);可一键转笔记(纯 OCR 画面要点)
56+
3. 视频导入完成后有「去会话页」直达按钮;导入 OCR 进度显示累计识别块数
57+
4. 实时捕获进行中无法开始图文采集(原因可见);图文采集中无法开始实时捕获
58+
5. 零截图完成采集 → 不留空壳会话;人为制造超 24h recording 图文会话 → 列表加载被清扫为 failed
59+
6. 全量回归通过(cargo test / clippy / vitest / tsc)
60+
61+
## 关联
62+
63+
- [设计 spec](superpowers/specs/2026-08-22-v0.11.7-photo-session-design.md) · [v0.11 系列总览](v0.11.md) ·
64+
- 复用资产:`capture/gdi_capture.rs` · `image_store.rs` · `import_frame.rs`(960px 口径)· `screens.rs`(屏卡聚类)· `BoxSelectOverlay.tsx`(框选范式)

‎docs/versions/v0.11.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
| **v0.11.4**(已交付 2026-08-22) | 学习循环补齐 + feed 消费闭环 | 内容分型(N13 续:action 卡最小版,REQ-199)+ 周契约视图 UI(弹性承诺呈现层,REQ-200)+ feed 最小消费闭环(碎片生命周期 UI + 碎片卡徽标;开关默认关,REQ-201) |
2222
| **v0.11.5**(规划 2026-08-22) | 采集体验与笔记打磨(11 项) | 档案采集中可改+自动重评+记忆机制修正 / 视频信息识别率增强(通用信号+B站试点)/ 窗口展开自动刷新 / 会话编号显示序号化 / 新颖度变化区域 / 水印过滤 A+B+C / 产物视图下线+精修懒自动化 / 精修工作台(前后对比)/ AI 输入剥离锚点 / 画面要点词汇表移出笔记 / 笔记页树形合并+大纲收起+缩略图+归组排查 |
2323
| **v0.11.6**(规划 2026-08-22) | AI 平台化(三层) | Provider 抽象(BYOK 多 Provider + 降级链 + 旧配置无感迁移)/ 能力声明化(chains 范式 JSON 化:prompt+协议+策略维度)/ 产出策略定制(档位预设 + 指令注入)/ 设置页 AI 配置重构(4 面板拆分·移除第三方余额卡片);充值系统远期留桩(ai_quota_* 隔离) |
24+
| **v0.11.7**(规划 2026-08-22) | 图文会话(截屏导入 + 跨链路体验同步) | 第三采集动线「图文采集」:全屏框选截屏 → kind=photo 图文会话(OCR 全图识别 + 屏卡流 + 转笔记);sessions.kind 字段 + 列表徽标/详情空态;图文/实时采集互斥 + 24h 残留清扫;视频导入「去会话页」直达 + OCR 进度携带识别块数 |
2425

2526
## 关键决策与纪律
2627

0 commit comments

Comments
 (0)