更新换行算法和APP-PUSH

This commit is contained in:
吕新雨
2026-02-10 11:39:33 +08:00
parent f03d36b5e9
commit ee2d9f44ea
105 changed files with 9967 additions and 233 deletions

View File

@@ -0,0 +1,136 @@
# grapheme-segmentation技术计划
## 1. 计划目标
基于 `spec.md``设计说明文档/文档换行算法.md v1.2.1`在客户端侧JS/TS落地**可复用且确定性**的 TC grapheme cluster字符簇分割能力用于 Text Wrap 的 TC tokens 生成,确保:
- 不拆 surrogate pair、ZWJ、VS16、肤色修饰符、国旗regional indicator flags、组合字符`é`
- 同输入同输出clusters 内容与顺序完全一致)
- 输出边界可作为 TC 断点边界(后续模块仅在 cluster 边界断行)
- 提供可解释 meta采用何种策略、是否降级
## 2. 默认技术决策(本计划采用)
> 说明:本模块要解决的是“分割口径”,不引入排版/搜索逻辑。
- **优先策略**:若运行环境支持 `Intl.Segmenter`,优先使用:
- `new Intl.Segmenter('zh-Hant', { granularity: 'grapheme' })`
- 取其 `segment(text)``segment` 字段作为 clusters
- **兜底策略(两种实现路径,默认选 A**
- **方案 A推荐**:引入轻量依赖 `grapheme-splitter` 作为 fallback避免手写不完整的 Unicode 规则导致漏拆/误拆
- **方案 B无依赖兜底**:实现“最低可用”分割(满足文档列出的组合不拆),但不承诺覆盖所有 Unicode 边界规则(风险较高)
> 本计划默认采用 **方案 A**。若后续明确“禁止新增依赖”,再切换到方案 B 并补齐更多回归。
## 3. 目录与产物
本子模块目录:
- `spec_kit/Text Wrap/modules/grapheme-segmentation/spec.md`
- `spec_kit/Text Wrap/modules/grapheme-segmentation/plan.md`(本文)
建议未来代码落位(实现阶段落地):
- `client/src/features/textWrap/grapheme/`
- `segmentGraphemes.ts`
- `strategies/intlSegmenter.ts`
- `strategies/fallback.ts`
- `__tests__/segmentGraphemes.test.ts`
## 4. API 设计(实现阶段的稳定契约)
实现一个最小可用纯函数:
- `segmentGraphemes(text: string, mode: 'PREFERRED' | 'FALLBACK') => { clusters: string[]; meta: { strategy: 'INTL_SEGMENTER' | 'FALLBACK'; hadFallback: boolean } }`
设计约束:
- `clusters.join('') === text`(不允许丢字符/改字符顺序)
- `clusters` 为空数组时必须是 `text==''`(不允许把空白当成 cluster 误输出)
## 5. 实现步骤(按落地顺序)
### 5.1 优先策略Intl.Segmenter
- 检测 `globalThis.Intl?.Segmenter` 是否可用
- 若可用且 `mode='PREFERRED'`
- 使用 `granularity='grapheme'` 分割
- 输出 `meta.strategy='INTL_SEGMENTER'``hadFallback=false`
注意:
- 需要确认 Expo/RN 的运行时是否始终具备 `Intl.Segmenter`(不同 JS 引擎/版本可能差异)
- 即使可用,也必须通过回归样例验证“不拆”要求
### 5.2 兜底策略Fallback
当出现以下任一情况时进入 fallback
- `mode='FALLBACK'`
- `Intl.Segmenter` 不存在
- `Intl.Segmenter` 运行抛错/返回异常结果(如空、丢字符)
#### 方案 A`grapheme-splitter`
- 新增依赖:`grapheme-splitter`
- 使用其分割能力输出 clusters
- 输出 `meta.strategy='FALLBACK'``hadFallback=true`
#### 方案 B最低可用手写规则仅在禁止依赖时启用
实现最低要求:
- 合并 surrogate pair
- 合并 ZWJ sequence`U+200D` 连接)
- 合并 variation selector`U+FE0F`
- 合并 skin tone modifier`U+1F3FB..U+1F3FF`
- 合并 regional indicator flags两两成对
- 合并组合字符combining marks与预组合等价形式至少覆盖 `e\u0301`
风险提示:
- 该实现容易漏掉其他扩展 grapheme cluster 规则;需要更高的测试覆盖与持续维护
## 6. 回归用例与测试计划Vitest
### 6.1 必须覆盖的样例(文档要求)
以下输入必须“不拆”为单个 cluster
- `👨‍👩‍👧‍👦`
- `🇸🇬`
- `👍🏽`
- `😮‍💨`
- `é`(至少覆盖 `e\u0301` 组合形式)
断言:
- `clusters.length === 1`
- `clusters[0] === input`
### 6.2 基础性质测试(建议)
- **可逆性**`clusters.join('') === input`
- **确定性**:同输入多次调用输出完全一致
- **空字符串**`'' -> []`
### 6.3 跨策略一致性(建议)
在支持 `Intl.Segmenter` 的环境中:
- 同一输入在 `PREFERRED``FALLBACK` 两种模式下输出 clusters 应一致
- 若出现差异,必须新增回归样例并明确差异原因(并在上层通过 configVersion 治理)
## 7. 性能与安全
- 单次分割复杂度应接近 \(O(n)\)
- 对超长文本(例如 > 2000 code units建议在上层模块触发裁剪或打点本模块仅保证不崩溃
- 任何异常必须被捕获并降级到 fallback保证“可用性优先”
## 8. 完成定义DoD
- `segmentGraphemes()` 在本地可运行,并通过必测回归样例
- 输出 meta 能区分 `INTL_SEGMENTER``FALLBACK`
- 单测覆盖:必测样例 + 可逆性 + 确定性
- 文档口径与实现一致不拆要求、fallback 触发条件、返回结构)