Files
mindfulness/spec_kit/Text Wrap/modules/search-engine-app/tasks.md
2026-02-10 11:39:33 +08:00

93 lines
3.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# search-engine-app任务清单
> 目标:在 APP 场景实现 `DP + TopK` 的确定性组合搜索(对应文档 9.1),并接入 `scoring-tiebreak` 输出最优 layout。
## 0. 对齐与准备
- [x] 复核输入/输出与依赖
- [x] 阅读 `spec_kit/Text Wrap/modules/search-engine-app/spec.md`
- [x] 阅读 `spec_kit/Text Wrap/modules/search-engine-app/plan.md`
- [x] 阅读 `设计说明文档/文档换行算法.md` 的 9.1/9.3/11 章节
- [x] 创建客户端模块目录
- [x] 新建 `client/src/features/textWrap/searchApp/`
## 1. 类型定义与导出
- [x] 新建 `client/src/features/textWrap/searchApp/types.ts`
- [x] 定义 `LineMode = 'AUTO' | 'FIXED'`
- [x] 定义 `SearchAppConfig`(包含 `topK``tooLongThresholds` 等,首版给默认值)
- [x] 定义 `SearchAppInput`
- [x] `tokens: Token[]`
- [x] `lang: 'TC' | 'EN'`
- [x] `breakpoints: Breakpoint[]`
- [x] `availableWidth: number`
- [x] `maxLines: number`
- [x] `lineMode: LineMode`
- [x] `measure: { contextProfile; fontSpec; measureWidthImpl }`(复用 width-measurement 类型)
- [x] `scoring: { config; lexicons; debug? }`(复用 scoring-tiebreak 类型)
- [x] 定义 `SearchAppResult`
- [x] 成功:`bestLayout { breaks; lines; wrappedText; meta }`
- [x] 失败:`{ ok:false; reason:'TOO_LONG'|'WIDTH_UNKNOWN'|'NO_CANDIDATE'; meta }`
- [x] 新建 `client/src/features/textWrap/searchApp/index.ts`
- [x] 统一导出 types 与核心入口 `searchBestLayoutApp()`
## 2. TopK 维护(确定性)
- [x] 新建 `client/src/features/textWrap/searchApp/topK.ts`
- [x] 实现 `compareLayouts(a,b)`
- [x] `score` 降序
- [x] `tieKey` 逐项比较(数值越小越优)
- [x] `breaks` 字典序兜底11.0A
- [x] 实现去重:同 breaks 仅保留最优
- [x] 实现 `insertTopK(list, cand, K)`:插入、去重、排序、截断(全程确定性)
## 3. 约束与可解释失败原因
- [x] 新建 `client/src/features/textWrap/searchApp/constraints.ts`
- [x] 实现 `isTooLong(tokens, lang)`TC>60 或 EN>30首版写死
- [x] 实现 `isLineStartPunctTC(tokens, pos, tcPunctuations)`H6
- [x] 实现 `isOverWidth(width, availableWidth)`H1
## 4. DP + TopK 主流程
- [x] 新建 `client/src/features/textWrap/searchApp/dpTopK.ts`
- [x] 实现入口 `searchBestLayoutApp(input)`
- [x] 构造 dp`dp[pos][linesUsed] = TopK[]`
- [x] 枚举顺序固定:
- [x] pos0..N
- [x] linesUsed0..maxLines-1
- [x] nextPos从 breakpoints 里取 `>pos` 的 pos 升序 + 追加 `N`
- [x] 每次转移:
- [x] 构造 lineText使用 `joinTokens`EN 默认空格TC 使用 rawSeparators 拼接)
- [x] 测量 lineWidth使用 `measureSliceWidthCached`
- [x] 过滤硬约束H1/空行/H6/maxLines
- [x] 构造 layoutCandidatebreaks + lines[]
- [x] 调用 `scoreLayout` + `buildTieKey` 得到 score/tieKey
- [x] 插入目标 dp 并维护 TopK
- [x] 结束选择:
- [x] AUTO`dp[N][<=maxLines]` 选最优
- [x] FIXED优先 `dp[N][==maxLines]`,否则返回 `NO_CANDIDATE`(交给 overflow-fallback 再降级)
- [x] meta 输出:
- [x] `breaks`
- [x] `scoreTopTerms`(若 debug=true从 scoring 输出 Top-3
## 5. 单测Vitest
- [x] 新建 `client/src/features/textWrap/searchApp/__tests__/searchApp.test.ts`
- [x] 确定性:同输入运行多次结果一致
- [x] TopK 去重:两条路径同 breaks 只保留一个
- [x] FIXED必须刚好 maxLines否则失败 reason=NO_CANDIDATE
- [x] TC H6行首标点导致的转移必须被禁止
- [x] TOO_LONG触发阈值直接返回 reason=TOO_LONG
## 6. 收尾
- [x] 跑测试与类型检查
- [x] `npm test`
- [x] `npx tsc --noEmit`
- [x] 将本 `tasks.md` 全部勾选完成
- [x] 更新 `spec_kit/overview.md`
- [x] 标记 `search-engine-app` 已完成编码(阶段性)
- [x] 写入变更文件清单