|
| 1 | +# v0.12.4:视频会话画面要点纯图落地 + 笔记预览时间戳锚点 chip 化 |
| 2 | + |
| 3 | +> 状态:代码已交付(2026-08-23,两项旧改动完整性修复) |
| 4 | +> 依据:用户对话 2026-08-23(①「视频会话中,画面要点应该是纯图片」——0.12.1 仍见 OCR 文字屏; |
| 5 | +> ②「笔记预览中(没有进行 AI 精修之前)仍有 `[⏱ 00:00]([[ts:233]])`」——预览阶段原始锚点 Markdown 外露) |
| 6 | +> 前置:v0.12.3(浮窗死锁修复)代码已交付;v0.12.2(笔记页信息架构重构)规划未启动,顺延为后续版本候选 |
| 7 | +
|
| 8 | +## 一句话 |
| 9 | + |
| 10 | +两个"此前已裁决但未真正落地"的旧改动,本次补齐到**全链路生效**: |
| 11 | + |
| 12 | +- **视频会话画面要点 = 关键帧纯图**(ADR-023):v0.12.0 M5 的帧侧改动只删了"全帧 OCR 兜底 + 明文路径", |
| 13 | + 但**布局分析后的区域 OCR 分支**(`!is_subtitle` 时对版面区域做 `region_ocr_blocks`)仍存活,导入路径 |
| 14 | + 的中区域 OCR 也还在跑——0.12.1 的视频会话仍产出 OCR 文字画面。本次删除该分支 + 导入路径中区域 OCR, |
| 15 | + 关键帧一律**纯图归档**(存图不识别),展示层按会话类型分派(photo 保留 OCR 文本屏,video 走纯图屏)。 |
| 16 | +- **笔记预览时间戳锚点 chip 化**(v0.11.5):正式笔记(`NoteMarkdown`)的 `[⏱ MM:SS]([[ts:ms]])` 早已渲染为 |
| 17 | + 可跳转 chip,但**预览阶段的两个轻量字符串渲染器**(`NotePreviewView.renderMarkdown`、 |
| 18 | + `RefineWorkbench.renderMd`)直接输出转义后文本——`[⏱ 00:00]([[ts:233]])` 原文外露。本次为两个渲染器 |
| 19 | + 统一接入 `renderTimestampAnchors`,与正式笔记同款 chip(颜色/虚线底/title 跳转)。 |
| 20 | + |
| 21 | +## 范围 |
| 22 | + |
| 23 | +| # | 项目 | 类型 | 落点 | |
| 24 | +|---|------|------|------| |
| 25 | +| 1 | 视频画面要点残留 region OCR(直播路径) | **P0** 修复 | `live_frame_process.rs`——删除 `!is_subtitle` 布局分析 + 区域 OCR 分支,`else` 纯关键帧归档 | |
| 26 | +| 2 | 视频画面要点残留区域 OCR(导入路径) | **P0** 修复 | `import.rs`/`import_frame.rs`——关键帧纯图归档(`rgb_to_bgra`),仅字幕 OCR 门控保留,删中区域 OCR | |
| 27 | +| 3 | 展示层纯图分派 | P1 契约 | `screens.rs`(`build_keyframe_screens`/`build_view_screens`)+ `get_session_detail`/`preview_session_note`/`manual_capture_inner` 三处接入 | |
| 28 | +| 4 | 预览时间戳锚点 chip 化 | **P0** 修复 | `utils/html.ts`(`renderTimestampAnchors`)+ `NotePreviewView.tsx`/`RefineWorkbench.tsx` 接入 | |
| 29 | +| 5 | 会话详情面板 kind 感知 | P2 信息 | `SessionDetailPanel.tsx`——photo「画面要点(OCR)」/ video「画面要点(关键帧纯图)」 | |
| 30 | +| 6 | 退役机制标注 | 伴随 | 退役 OCR 路径按 `#[allow(dead_code)]` + `@ai-context` 保留(region_ocr/layout_cache/grid_from_bgra/save_crop),不删除 | |
| 31 | +| 7 | 文档与治理 | 伴随 | 本版本文档 + CHANGELOG + 索引 + ADR-023 状态行 + 版本号 0.12.4 | |
| 32 | + |
| 33 | +## 根因(Bug ①:画面要点残留) |
| 34 | + |
| 35 | +- **帧侧**:`live_frame_process.rs` 的旧五段分支 `is_subtitle → 布局分析 → `layout_regions.is_empty()` 判断 |
| 36 | + 后**仍调用 `region_ocr_blocks` → `handle_full_frame`**——布局分析识别出的"正文区域"被当作 OCR 区域 |
| 37 | + 全文识别入库(`region=full`),视频会话画面要点在 0.12.1 仍是 OCR 文字屏。 |
| 38 | +- **导入侧**:`import.rs` 的 `ocr_keyframes` 只按"有字幕才识别"做判断,无 OCR 的关键帧**不归档图片**; |
| 39 | + `import_frame.rs` 的中区域(MIDDLE_TOP/BOTTOM)OCR 对视频画面做区域识别——同样产出 OCR 文字。 |
| 40 | +- **展示侧**:`get_session_detail`/`preview_session_note` 对视频会话仍无条件走 `build_screens` |
| 41 | + (OCR 文字屏构建),未按会话类型分派。 |
| 42 | + |
| 43 | +## 根因(Bug ②:预览锚点外露) |
| 44 | + |
| 45 | +- 笔记真实渲染(注意 `NoteMarkdown` 组件)用 react-markdown 的 `a` 组件把 `( [[ts:ms]] )` 渲染为 chip; |
| 46 | +- 但**预览阶段**(未精修)走 `NotePreviewView.renderMarkdown` / `RefineWorkbench.renderMd` 两个轻量 |
| 47 | + 字符串渲染器——`escapeHtml` 后直接原文输出,`[⏱ 00:00]([[ts:233]])` 作为字面文本保留。 |
| 48 | +- v0.11.5 只改了正式渲染组件,遗漏了两个字符串渲染器 → 预览仍见原始锚点。 |
| 49 | + |
| 50 | +## 修复方案 |
| 51 | + |
| 52 | +### ① 关键帧纯图(ADR-023 全链路) |
| 53 | + |
| 54 | +- `live_frame_process.rs`:`else`(非字幕帧)→ **纯关键帧归档**(`handle_full_frame(&frame, &[], ...)`, |
| 55 | + `layout_changed=None`),零 OCR 调用;删除 `layout_cache` 参数(仅退役的布局分析使用)。 |
| 56 | +- `import.rs`:`ocr_keyframes` 对每个关键帧**无条件归档纯图**(`SessionImageStore` + `rgb_to_bgra`), |
| 57 | + 字幕 OCR 独立门控(`ocr_enabled`);进度消息「关键帧归档 i/N · 已识别字幕 N 块」。 |
| 58 | +- `import_frame.rs`:删 MIDDLE_TOP/BOTTOM 常量与中区域 OCR,只保留底部字幕带。 |
| 59 | +- `screens.rs`:新增 `build_keyframe_screens`(每个归档图一屏:`first_seen=last_seen=ts`、`image_ref`, |
| 60 | + 无 title/body/labels/structure)+ `build_view_screens(kind, ...)` 分派器 |
| 61 | + (photo→`build_screens` 保持 ADR-021 OCR 文本屏;video/None→纯图屏)。 |
| 62 | +- 命令层接入:`get_session_detail`、`preview_session_note`(photo→`attach_images`)、 |
| 63 | + `manual_capture_inner`(经 `kind` 分派)。 |
| 64 | +- **保留边界**:`filter_note` 的转写路径统计/`ocr_screens` 未动;photo 会话 OCR 屏行为与 v0.12.1 完全一致; |
| 65 | + AI 精修 vision 提取(`vision_refine_enabled`)仍按 ADR-023 走屏卡图——本改动只消灭"视频画面要点出 |
| 66 | + **本地 OCR 文字**"这一条路径。 |
| 67 | + |
| 68 | +### ② 预览锚点 chip |
| 69 | + |
| 70 | +- `utils/html.ts` 新增纯函数 `renderTimestampAnchors(escaped)`:在**已转义**文本上先替换章头包裹形态 |
| 71 | + `## 标题 [[⏱ MM:SS]([[ts:ms]])]`,再替换段落形态 `[⏱ MM:SS]([[ts:ms]])`(顺序契约:章头含段落子串, |
| 72 | + 先章头防误吃);输出 `<span>` chip(teal 色、虚线底边、圆角、title+点击跳转语义),与 `NoteMarkdown` 一致。 |
| 73 | +- `NotePreviewView.renderMarkdown` / `RefineWorkbench.renderMd` 全部渲染分支(`# `/`## `/`> `/`- `/段落) |
| 74 | + 接入;img 分支不动。 |
| 75 | + |
| 76 | +## 测试 |
| 77 | + |
| 78 | +- Rust(`cargo test --test app_lib_tests`,新增 3 项):`keyframe_screens_one_per_archived_image`、 |
| 79 | + `keyframe_screens_empty_without_images`、`view_screens_dispatches_by_kind`(photo 保留 OCR 屏 / |
| 80 | + video 纯图屏 / 无图空屏不落回)。 |
| 81 | +- 前端 vitest(`html.test.ts` 新增 5 项):段落 chip、章头 chip、无锚点穿透、转义-替换次序注入安全、 |
| 82 | + 非数字分钟不误匹配。 |
| 83 | +- 全量:Rust 1547 通过(3 失败为**预存基线问题**——`ai_client` 默认 Provider 断言 SiliconFlow 而 |
| 84 | + v0.12.0 M4 已默认 DeepSeek;`note_filter` 两条黄金用例,均在未含本次改动的 HEAD 上复现); |
| 85 | + `tsc --noEmit` 零错误;vitest 全绿;vite 构建通过;clippy 零新增警告(既有警告均位于未改动文件)。 |
| 86 | + |
| 87 | +## 验证(真机待执行) |
| 88 | + |
| 89 | +- 视频会话采集会话详情:画面要点清单为**图(缩略图卡)**,无 OCR 文字条目;点开为关键帧原图。 |
| 90 | +- 导入视频(无字幕):关键帧已归档(session-images/<id>/full/*.webp),画面要点纯图。 |
| 91 | +- 笔记预览(未精修):`[⏱ 00:00]([[ts:233]])` 不再原文显示,渲染为可点击 chip;精修后不变。 |
| 92 | +- 图文(photo)会话:画面要点仍为 OCR 文本屏(回归不变)。 |
0 commit comments