Files
mindfulness/spec_kit/Text Wrap/modules/grapheme-segmentation/plan.md
2026-02-10 11:39:33 +08:00

5.0 KiB
Raw Blame History

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 运行抛错/返回异常结果(如空、丢字符)

方案 Agrapheme-splitter

  • 新增依赖:grapheme-splitter
  • 使用其分割能力输出 clusters
  • 输出 meta.strategy='FALLBACK'hadFallback=true

方案 B最低可用手写规则仅在禁止依赖时启用

实现最低要求:

  • 合并 surrogate pair
  • 合并 ZWJ sequenceU+200D 连接)
  • 合并 variation selectorU+FE0F
  • 合并 skin tone modifierU+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 的环境中:

  • 同一输入在 PREFERREDFALLBACK 两种模式下输出 clusters 应一致
    • 若出现差异,必须新增回归样例并明确差异原因(并在上层通过 configVersion 治理)

7. 性能与安全

  • 单次分割复杂度应接近 (O(n))
  • 对超长文本(例如 > 2000 code units建议在上层模块触发裁剪或打点本模块仅保证不崩溃
  • 任何异常必须被捕获并降级到 fallback保证“可用性优先”

8. 完成定义DoD

  • segmentGraphemes() 在本地可运行,并通过必测回归样例
  • 输出 meta 能区分 INTL_SEGMENTERFALLBACK
  • 单测覆盖:必测样例 + 可逆性 + 确定性
  • 文档口径与实现一致不拆要求、fallback 触发条件、返回结构)