5.0 KiB
5.0 KiB
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(推荐):引入轻量依赖
本计划默认采用 方案 A。若后续明确“禁止新增依赖”,再切换到方案 B 并补齐更多回归。
3. 目录与产物
本子模块目录:
spec_kit/Text Wrap/modules/grapheme-segmentation/spec.mdspec_kit/Text Wrap/modules/grapheme-segmentation/plan.md(本文)
建议未来代码落位(实现阶段落地):
client/src/features/textWrap/grapheme/segmentGraphemes.tsstrategies/intlSegmenter.tsstrategies/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 === 1clusters[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 触发条件、返回结构)