# search-engine-widget(任务清单) > 目标:在 WIDGET 场景实现确定性 Beam Search(文档 9.2),支持 `widthMode=MEASURE/APPROX`,并输出最优 layout 或可解释失败原因。 ## 0. 对齐与准备 - [x] 阅读并对齐口径 - [x] 阅读 `spec_kit/Text Wrap/modules/search-engine-widget/spec.md` - [x] 阅读 `spec_kit/Text Wrap/modules/search-engine-widget/plan.md` - [x] 阅读 `设计说明文档/文档换行算法.md` 的 9.2/9.3/11 章节 - [x] 创建客户端模块目录 - [x] 新建 `client/src/features/textWrap/searchWidget/` ## 1. 类型定义与导出 - [x] 新建 `client/src/features/textWrap/searchWidget/types.ts` - [x] 定义 `WidthMode = 'MEASURE' | 'APPROX'` - [x] 定义 `SearchWidgetConfig`:`beamK`、`expandM`、`tooLongThresholds` - [x] 定义 `SearchWidgetInput`: - [x] `tokens/lang/breakpoints/availableWidth/maxLines/context='WIDGET'` - [x] `measure: { widthMode; contextProfile?; fontSpec?; measureWidthImpl? }` - [x] `scoring: { config; lexicons; debug? }` - [x] 定义 `SearchWidgetResult`: - [x] 成功:`bestLayout { breaks; lines; wrappedText; meta }` - [x] 失败:`{ ok:false; reason:'TOO_LONG'|'NO_CANDIDATE'; meta? }` - [x] 新建 `client/src/features/textWrap/searchWidget/index.ts` - [x] 统一导出 types 与入口 `searchBestLayoutWidget()` ## 2. TopK/BeamK 维护(确定性) - [x] 新建 `client/src/features/textWrap/searchWidget/topK.ts` - [x] 实现 compare:score desc;tieKey asc;breaks 字典序 - [x] 实现去重:同 breaks 只保留最优 - [x] 实现 `insertTopK`(用于全局 beamK 保留) ## 3. 约束与降级 meta - [x] 新建 `client/src/features/textWrap/searchWidget/constraints.ts` - [x] `isTooLong`(TC>60 / EN>30) - [x] `isLineStartPunctTC(tokens, nextPos, tcPunctuations)`(H6) - [x] `approxWidth(line, lang)`(EN=tokenCount / TC=charCount) ## 4. Beam Search 主流程 - [x] 新建 `client/src/features/textWrap/searchWidget/beam.ts` - [x] 初始化 beams:`[{pos:0, breaks:[], lines:[], score:0}]` - [x] 循环 lineIndex=1..maxLines - [x] 对每个 beam 枚举 nextPos(breakpoints>pos + N) - [x] 裁剪:仅取前 `expandM` 个 nextPos(pos 升序) - [x] 构造行文本:`joinTokens` - [x] 计算行宽: - [x] widthMode=MEASURE:尝试 `measureSliceWidthCached`;失败则降级 approx 并 meta.reason 标记 - [x] widthMode=APPROX:直接 approx,并 meta.reason=WIDTH_UNKNOWN - [x] 硬约束过滤: - [x] 禁止空行、maxLines、TC H6 - [x] 若 width 可用且 > availableWidth:过滤(仅 MEASURE 可信) - [x] 评分:`scoreLayout` + `buildTieKey` - [x] 全局 newBeams 维护 TopK(beamK) - [x] 结束:从 beams 中选 pos==N 最优,否则 NO_CANDIDATE ## 5. 单测(Vitest) - [x] 新建 `client/src/features/textWrap/searchWidget/__tests__/searchWidget.test.ts` - [x] 确定性:同输入重复执行结果一致 - [x] widthMode=APPROX:仍可输出,且 meta.reason=WIDTH_UNKNOWN - [x] 性能约束:expandM/beamK 生效(可统计测量/扩展次数) - [x] TC H6:行首标点被禁止 ## 6. 收尾 - [x] 跑测试与类型检查 - [x] `npm test` - [x] `npx tsc --noEmit` - [x] 将本 `tasks.md` 全部勾选完成 - [x] 更新 `spec_kit/overview.md` - [x] 标记 `search-engine-widget` 已完成编码(阶段性) - [x] 写入变更文件清单