更新换行算法和APP-PUSH
This commit is contained in:
136
spec_kit/Text Wrap/modules/grapheme-segmentation/plan.md
Normal file
136
spec_kit/Text Wrap/modules/grapheme-segmentation/plan.md
Normal 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 触发条件、返回结构)
|
||||
|
||||
52
spec_kit/Text Wrap/modules/grapheme-segmentation/spec.md
Normal file
52
spec_kit/Text Wrap/modules/grapheme-segmentation/spec.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# grapheme-segmentation(子模块规范)
|
||||
|
||||
## 子模块名称
|
||||
|
||||
grapheme-segmentation(TC 字符簇分割)
|
||||
|
||||
## 目标描述
|
||||
|
||||
在 TC(中文/繁中)场景下,统一“按 grapheme cluster(字符簇)切分”的实现口径,确保:
|
||||
|
||||
- 不拆分 surrogate pair(代理对)
|
||||
- 不拆分 ZWJ 序列(家庭 emoji 等)
|
||||
- 不拆分 variation selector(VS16 等)
|
||||
- 不拆分 skin tone modifier(肤色修饰符)
|
||||
- 不拆分 regional indicator flags(国旗)
|
||||
- 覆盖组合字符(如 `é`)
|
||||
|
||||
优先使用平台级 segmentation(如 ICU / 系统 API / `Intl.Segmenter`),无库时提供最低可用兜底。
|
||||
|
||||
## 输入/输出定义
|
||||
|
||||
### 输入
|
||||
|
||||
- `text: string`(已完成空白归一化的文本,或原始文本)
|
||||
- `mode: 'PREFERRED' | 'FALLBACK'`
|
||||
|
||||
### 输出
|
||||
|
||||
- `clusters: string[]`
|
||||
- 每个元素为一个 grapheme cluster(用于 TC tokens)
|
||||
- `meta?: { strategy: 'PLATFORM' | 'INTL_SEGMENTER' | 'FALLBACK'; hadFallback: boolean }`
|
||||
|
||||
## 验收标准(可验证)
|
||||
|
||||
至少通过以下回归样例(每个样例都必须“单 token 不拆”):
|
||||
|
||||
- `👨👩👧👦`
|
||||
- `🇸🇬`
|
||||
- `👍🏽`
|
||||
- `😮💨`
|
||||
- `é`
|
||||
|
||||
并满足:
|
||||
|
||||
- **确定性**:同输入同输出(clusters 顺序与内容完全一致)
|
||||
- **边界一致**:任意换行断点只允许发生在 `clusters` 边界
|
||||
|
||||
## 依赖与关联
|
||||
|
||||
- **被依赖**:`core-contract`(TC token 生成)、`breakpoint-candidates`
|
||||
- **不依赖其他模块**
|
||||
|
||||
126
spec_kit/Text Wrap/modules/grapheme-segmentation/tasks.md
Normal file
126
spec_kit/Text Wrap/modules/grapheme-segmentation/tasks.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# grapheme-segmentation(任务清单)
|
||||
|
||||
> 对应计划:`spec_kit/Text Wrap/modules/grapheme-segmentation/plan.md`
|
||||
>
|
||||
> 状态含义:`[ ]` 未完成,`[x]` 已完成。
|
||||
> 执行完本清单后,需要在 `spec_kit/overview.md` 的 `Text Wrap` 条目下补充“已完成编码/任务执行完毕”的标记(见最后一节)。
|
||||
|
||||
---
|
||||
|
||||
## 0. 任务标记规则
|
||||
|
||||
- 用勾选框标记执行状态:
|
||||
- `[ ]` 未完成
|
||||
- `[x]` 已完成
|
||||
- 每个任务必须可独立验收(有明确产出与检查方式)。
|
||||
- 所有代码注释必须为简体中文,且把“分割口径”写清楚,避免跨端实现漂移。
|
||||
|
||||
---
|
||||
|
||||
## 1. 前置检查(环境能力与策略选择)
|
||||
|
||||
- [x] 1.1 确认运行时是否支持 `Intl.Segmenter`(Expo/RN 当前引擎)
|
||||
- **方式**:在本地运行/测试环境中打印或断言 `globalThis.Intl?.Segmenter` 是否存在
|
||||
- **验收**:记录结论:存在/不存在;若不存在,fallback 必须覆盖所有必测样例。
|
||||
|
||||
- [x] 1.2 确认 fallback 策略选择为“方案 A:`grapheme-splitter`”
|
||||
- **要求**:若项目明确禁止新增依赖,需要在本任务中写明原因并切换到“方案 B(手写最低可用)”,同时补齐更高测试覆盖
|
||||
- **验收**:plan 与实际实现策略一致(不出现“文档写 A、代码做 B”的漂移)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 依赖与目录骨架(客户端侧实现)
|
||||
|
||||
- [x] 2.1 新建目录 `client/src/features/textWrap/grapheme/`
|
||||
- **产出**(建议文件):
|
||||
- `segmentGraphemes.ts`(对外纯函数)
|
||||
- `strategies/intlSegmenter.ts`
|
||||
- `strategies/fallback.ts`
|
||||
- `types.ts`(返回结构与 meta 类型)
|
||||
- `__tests__/segmentGraphemes.test.ts`
|
||||
- **验收**:目录存在,TS 可正常 import(不报路径错误)。
|
||||
|
||||
- [x] 2.2(方案 A)新增依赖 `grapheme-splitter` 并锁定到 `client/package.json`
|
||||
- **验收**:
|
||||
- `npm install grapheme-splitter` 成功
|
||||
- `npm test` 仍能通过(不破坏现有测试)
|
||||
|
||||
---
|
||||
|
||||
## 3. 纯函数实现(分割 + meta)
|
||||
|
||||
- [x] 3.1 实现 `segmentGraphemes(text, mode)` 的返回契约
|
||||
- **要求**:
|
||||
- 返回 `{ clusters, meta }`
|
||||
- `clusters.join('') === text`(不丢字符/不改顺序)
|
||||
- `text==''` 时 `clusters==[]`
|
||||
- **验收**:为上述约束写入单测并通过。
|
||||
|
||||
- [x] 3.2 实现优先策略 `Intl.Segmenter`(`mode='PREFERRED'` 时优先)
|
||||
- **要求**:
|
||||
- `meta.strategy='INTL_SEGMENTER'`
|
||||
- `meta.hadFallback=false`
|
||||
- **验收**:在支持该能力的环境中,至少 1 个常规输入能走到该策略(可通过 meta 断言)。
|
||||
|
||||
- [x] 3.3 实现 fallback 策略(满足所有必测“不拆”样例)
|
||||
- **触发条件**(任一满足即 fallback):
|
||||
- `mode='FALLBACK'`
|
||||
- `Intl.Segmenter` 不存在
|
||||
- `Intl.Segmenter` 抛错/返回异常结果(空/丢字符)
|
||||
- **要求**:
|
||||
- `meta.strategy='FALLBACK'`
|
||||
- `meta.hadFallback=true`
|
||||
- **验收**:必测样例全部通过(见 4.2)。
|
||||
|
||||
---
|
||||
|
||||
## 4. 单元测试(Vitest)
|
||||
|
||||
- [x] 4.1 基础性质测试
|
||||
- **覆盖**:
|
||||
- 可逆性:`clusters.join('') === input`
|
||||
- 确定性:同输入多次调用输出一致
|
||||
- 空字符串:`'' -> []`
|
||||
- **验收**:测试通过且不会出现偶现失败。
|
||||
|
||||
- [x] 4.2 必测回归样例(文档要求:每个都必须“不拆”为 1 个 cluster)
|
||||
- **样例**:
|
||||
- `👨👩👧👦`
|
||||
- `🇸🇬`
|
||||
- `👍🏽`
|
||||
- `😮💨`
|
||||
- `e\u0301`(组合字符形式)
|
||||
- **断言**:
|
||||
- `clusters.length === 1`
|
||||
- `clusters[0] === input`
|
||||
- **验收**:在本地 `npm test` 中稳定通过。
|
||||
|
||||
- [x] 4.3 跨策略一致性测试(在支持 `Intl.Segmenter` 的环境中执行)
|
||||
- **内容**:同一输入在 `PREFERRED` 与强制 `FALLBACK` 下输出 clusters 一致
|
||||
- **验收**:一致;若不一致,必须新增回归样例并在文档中写明差异与治理方式(`configVersion`)。
|
||||
|
||||
---
|
||||
|
||||
## 5. 最终自检清单(合入前)
|
||||
|
||||
- [x] 5.1 `npm test` 通过(包含本模块新增用例)
|
||||
- **验收**:不影响现有测试文件。
|
||||
|
||||
- [x] 5.2 `npx tsc --noEmit` 通过(或项目既有 TS 检查命令通过)
|
||||
- **验收**:无类型错误。
|
||||
|
||||
- [x] 5.3 注释与口径自检(简体中文)
|
||||
- **检查点**:
|
||||
- 明确“字符簇不拆”的边界意义(后续断点仅能在 clusters 边界)
|
||||
- 明确 fallback 触发条件与 meta 含义
|
||||
- **验收**:后续模块开发者只看代码也不会产生歧义。
|
||||
|
||||
---
|
||||
|
||||
## 6. 文档回写(任务清单执行完毕后必须做)
|
||||
|
||||
- [x] 6.1 在 `spec_kit/overview.md` 的 `Text Wrap` 条目下补充执行状态
|
||||
- **建议写法**:
|
||||
- 增加一行:`- **已完成编码(阶段性)**:grapheme-segmentation(TC 字符簇分割)`
|
||||
- **验收**:overview 能反映该子模块已完成,便于全局追踪。
|
||||
|
||||
Reference in New Issue
Block a user