更新换行算法和APP-PUSH
This commit is contained in:
115
spec_kit/Text Wrap/modules/search-engine-widget/plan.md
Normal file
115
spec_kit/Text Wrap/modules/search-engine-widget/plan.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# search-engine-widget(技术计划)
|
||||
|
||||
## 1. 计划目标
|
||||
|
||||
在 **WIDGET** 场景实现确定性的 Beam Search(文档 9.2):
|
||||
|
||||
- 每一行扩展时只保留 TopK partial layouts(beam)
|
||||
- 每步扩展断点数 M 受控(更强裁剪)
|
||||
- 支持 `widthMode='MEASURE' | 'APPROX'`
|
||||
- 结束时从 `pos==N` 的 beams 中选最优;若无解返回可解释的失败原因(最终由 `overflow-fallback` 统一语义)
|
||||
|
||||
## 2. 确定性与性能约束
|
||||
|
||||
### 2.1 确定性(必须)
|
||||
|
||||
- beams 的遍历顺序固定(按当前 beams 排序后的顺序)
|
||||
- nextPos 候选顺序固定(`pos` 升序)
|
||||
- pruning 固定(先按候选优先级/位置排序,再取前 M)
|
||||
- beam 的 TopK 维护固定:`score` 降序;再 `tieKey`;再 `breaks` 字典序
|
||||
|
||||
### 2.2 性能参数(写死默认值)
|
||||
|
||||
来自文档 9.3:
|
||||
|
||||
- `beamK=5`
|
||||
- `expandM=12`
|
||||
|
||||
## 3. Beam Search 设计(对应文档 9.2)
|
||||
|
||||
令 `N=tokens.length`,token 边界为 `pos in [0..N]`。
|
||||
|
||||
### 3.1 beam 状态
|
||||
|
||||
一个 beam(partial layout)包含:
|
||||
|
||||
- `pos`:当前已覆盖到的 token 边界
|
||||
- `breaks: number[]`
|
||||
- `lines: LineInfo[]`
|
||||
- `score` + `tieKey`
|
||||
- `meta`(可选):debug Top-3 terms、approx reason 等
|
||||
|
||||
### 3.2 扩展 nextPos(每步最多 M)
|
||||
|
||||
对每个 beam,从 `pos` 扩展到若干 `nextPos`:
|
||||
|
||||
- nextPos 来源:`breakpoints[].pos > pos`(按 pos 升序)
|
||||
- 额外包含 `nextPos=N`(结束边界)
|
||||
- 为控制复杂度:对候选 nextPos 进行裁剪,仅保留前 `expandM`
|
||||
|
||||
裁剪规则(确定性,首版简单可控):
|
||||
|
||||
- 先取 `nextPos` 最小的 `expandM` 个(最短前缀优先,利于早结束)
|
||||
|
||||
> 注:后续可升级为“优先级 + 距理想切分点近”裁剪,但首版以确定性与稳定为主。
|
||||
|
||||
### 3.3 宽度模式
|
||||
|
||||
- `widthMode='MEASURE'`:
|
||||
- 若提供测量能力:使用 `measureSliceWidthCached` 获取 lineWidth
|
||||
- 若测量不可用/失败:降级为 approx(记录 `reason=WIDTH_UNKNOWN` 或 `MEASURE_FAILED`),lineWidth 采用近似值
|
||||
- `widthMode='APPROX'`:
|
||||
- 不调用测量;lineWidth 使用近似值:
|
||||
- EN:`tokenCount`
|
||||
- TC:`charCount`
|
||||
- meta 标记 `reason=WIDTH_UNKNOWN`
|
||||
|
||||
### 3.4 硬约束(最小集)
|
||||
|
||||
与 App 一致的最小硬约束:
|
||||
|
||||
- 禁止空行:`nextPos > pos`
|
||||
- 最大行数:lineIndex 不超过 `maxLines`
|
||||
- TC 禁止行首标点(H6):若 `tokens[nextPos]` 属于 `tcPunctuations` 则该转移无效
|
||||
- 超宽约束:
|
||||
- MEASURE 模式下若 width 可用且 `lineWidth > availableWidth`,转移无效
|
||||
- APPROX 模式下不执行超宽硬过滤(因为单位不一致),交给评分处理
|
||||
|
||||
### 3.5 每轮保留 beamK
|
||||
|
||||
对所有扩展出来的新 beams:
|
||||
|
||||
- 使用 TopK 维护(去重同 breaks,排序按 score/tieKey/breaks)
|
||||
- 全局只保留 `beamK` 个作为下一轮 beams
|
||||
|
||||
### 3.6 结束选择
|
||||
|
||||
重复扩展最多 `maxLines` 轮后:
|
||||
|
||||
- 优先从 beams 中选 `pos==N` 的最优
|
||||
- 若无 `pos==N`:返回 `NO_CANDIDATE`(由 overflow-fallback 统一兜底语义)
|
||||
|
||||
## 4. 代码落位(客户端)
|
||||
|
||||
- `client/src/features/textWrap/searchWidget/`
|
||||
- `types.ts`
|
||||
- `beam.ts`
|
||||
- `topK.ts`(可复用 App 逻辑,首版复制一份避免耦合)
|
||||
- `constraints.ts`
|
||||
- `index.ts`
|
||||
- `__tests__/searchWidget.test.ts`
|
||||
|
||||
## 5. 测试计划(Vitest)
|
||||
|
||||
- 确定性:同输入重复执行输出一致
|
||||
- 性能约束:beamK/expandM 生效(可通过构造大量断点断言扩展次数上限)
|
||||
- widthMode=APPROX:仍能产出结果,且 meta.reason=WIDTH_UNKNOWN
|
||||
- TC H6:行首标点转移被禁止
|
||||
|
||||
## 6. 完成定义(DoD)
|
||||
|
||||
- Beam Search 可运行,且确定性单测通过
|
||||
- 宽度模式与降级 meta 符合 spec
|
||||
- 性能参数默认值写死且可覆盖
|
||||
- 更新 overview 标记阶段性完成并记录变更文件
|
||||
|
||||
46
spec_kit/Text Wrap/modules/search-engine-widget/spec.md
Normal file
46
spec_kit/Text Wrap/modules/search-engine-widget/spec.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# search-engine-widget(子模块规范)
|
||||
|
||||
## 子模块名称
|
||||
|
||||
search-engine-widget(WIDGET 搜索器:Beam Search)
|
||||
|
||||
## 目标描述
|
||||
|
||||
在 WIDGET 场景实现确定性 Beam Search:
|
||||
|
||||
- 每一行扩展时只保留 TopK partial layouts(beam)
|
||||
- 每步扩展断点数 M 受控(更强裁剪)
|
||||
- 支持 width unknown / approx mode
|
||||
- 最终选择 pos==N 的最优解;无解走 `overflow-fallback`
|
||||
|
||||
## 输入/输出定义
|
||||
|
||||
### 输入
|
||||
|
||||
- `tokens: Token[]`
|
||||
- `breakpoints: Breakpoint[]`
|
||||
- `availableWidth: number`(可为常量 profile)
|
||||
- `maxLines: number`
|
||||
- `context: 'WIDGET'`
|
||||
- `measure: { widthMode: 'MEASURE' | 'APPROX' }`
|
||||
- `config: { beamK: number; expandM: number }`
|
||||
|
||||
### 输出
|
||||
|
||||
- `bestLayout: { breaks: number[]; lines: string[]; wrappedText: string; meta?: { breaks: number[]; scoreTopTerms?: ... } }`
|
||||
|
||||
## 验收标准(可验证)
|
||||
|
||||
- **确定性**:beam 扩展、剪枝、排序规则固定;同输入同输出
|
||||
- **性能约束生效**:
|
||||
- beamK 默认 5
|
||||
- 每步扩展 M 默认不超过 12
|
||||
- **宽度不可用降级一致**:
|
||||
- 进入 approx mode 时 meta 标记 `reason=WIDTH_UNKNOWN`
|
||||
- 搜索仍可运行并输出可解释结果
|
||||
|
||||
## 依赖与关联
|
||||
|
||||
- **依赖**:`core-contract`、`width-measurement`、`breakpoint-candidates`、`scoring-tiebreak`、`overflow-fallback`
|
||||
- **被依赖**:`integration`、`golden-tests`
|
||||
|
||||
78
spec_kit/Text Wrap/modules/search-engine-widget/tasks.md
Normal file
78
spec_kit/Text Wrap/modules/search-engine-widget/tasks.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# 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] 写入变更文件清单
|
||||
|
||||
Reference in New Issue
Block a user