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