feat(ax): item 19 screen-reader 纯文本渲染 (无 ANSI 装饰) + 装饰 char 替换 (CC 2.1.208, #109) - #110
Merged
Merged
Conversation
…1.208, #109) P4.1 item 19 剩余 gap: Ink 纯文本渲染 (无 ANSI 装饰) + Unicode 装饰 char 降 ASCII, 在 screen-reader (prefersReducedMotion) 激活时生效, 默认 off 保持 byte-identical。 两 choke point (in-function gate, 非 global chalk.level=0): - src/ink/colorize.ts: colorize()/applyTextStyles() 加 screen-reader ANSI gate, 激活时返 raw string (零 ANSI escape)。applyColor 路由 colorize 自动继承。in-function gate 只影响 Ink 渲染路径, 不动非 Ink chalk (telemetry/gracefulShutdown/markdown.ts 独立 import)。 - src/constants/figures.ts: getFigures() lazy factory, 首调用读 prefersReducedMotion, 返 frozen UNICODE_SET (默认, byte-identical 现常量) 或 ASCII_SET (降级)。createRequire 懒加载 settings 打破 figures↔settings↔PermissionMode 循环 (PAUSE_ICON TDZ)。 3 消费方迁移 + 2 inline box-drawing 站: - EffortIndicator.ts: EFFORT_* → fig.EFFORT_* - ContextVisualization.tsx: 12 └ tree-branch literal → TREE_BRANCH module-scope const (reducedMotion → '- ') - MarkdownTable.tsx: │/─/box border → VBAR/HBAR/BORDERS module-scope const (reducedMotion → +/-/| ASCII) 测试 (2 新文件, 14 case): - colorizeScreenReader.test.ts: gate OFF (chalk.level=3 force, ANSI emitted) vs gate ON (raw, zero ANSI), early-return 不破, env chain 锁定 - figuresScreenReader.test.ts: default byte-identical 现常量, env-on ASCII 降级, 缓存失效, ASCII/UNICODE 同键集 默认 off = byte-identical; screen-reader 激活 (FUSION_AX_SCREEN_READER=1 或 settings.axScreenReader) 剥 ANSI + 降 ASCII。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6 tasks
dahai80
added a commit
that referenced
this pull request
Aug 21, 2026
…112) 二次规范化缓存: messages 严格 append-only, 已存对象引用稳定 → 按前缀引用 增量复用 normalized 输出, 仅尾部续算, 避免 O(n) 每 render/append 重 flatMap 27k 消息。compact/rewind 整体替换 → 前缀引用不匹配 → 全量重算 (自动失效, 无 stale)。 实现: - messages.ts: 抽 normalizeMessagesCore(messages, seedIsNewChain) 返回 {normalized, isNewChain}, 行为与原内联 loop 完全一致 (isNewChain 单调 false→true + deriveUUID 确定性)。normalizeMessages 变薄 wrapper, 9 其它 调用方零变化。 - normalizeMessagesCache.ts (新): 前缀引用增量缓存 NormalizedCacheState {sourceRef, segmentCounts, normalized, isNewChain}。逐 source message 记 段长 (1 msg flatMap → N normalized), 按 segmentCounts 对齐复用前缀, seed isNewChain 续算尾部, UUID 与全量重算 byte-identical。 - Messages.tsx:464: useRef 持缓存, useMemo [messages] 内增量调用后写回, 输出再 filter(isNotEmptyMessage)。 - 7 case 单测: baseline 等价 / 增量前缀复用 / isNewChain 跨边界续传 / compact 替换失效 / 部分前缀 / 空数组 / 段长正确。26 expect。 验证: typecheck 0 err; 235 test 全过无回归; build:dev OK; biome 无 err (仅 test noNonNullAssertion warning, 与现有 test 风格一致)。 :598 层增量 + item 17 全屏完整历史推迟 (issue #111): reorderMessagesInUI 签名含 synthetic (尾部 append) + applyGrouping 全数组 resultsByToolUseId + buildMessageLookups orphan-marker messages.at(-1) 三处跨边界使前缀不可 定稿, 非 append-only 安全缓存。本 PR 仅 normalize 层。 Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
item 19 — screen-reader 纯文本渲染 (无 ANSI 装饰) + 装饰 char 替换
Closes #109。P4.1 item 19 剩余 gap: Ink 纯文本渲染 (无 ANSI 装饰) + Unicode 装饰 char 降 ASCII, screen-reader (
prefersReducedMotion) 激活时生效。默认 off = byte-identical。设计: 2 choke point (in-function gate, 非 global chalk.level=0)
ANSI gate (
src/ink/colorize.ts):colorize()/applyTextStyles()加screenReaderAnsiGateActivegate, 激活时返 raw string (零 ANSI escape)applyColor路由colorize自动继承main.tsx/ gracefulShutdown / markdown.ts 独立import chalk) — 比global chalk.level=0(波及 11+ 文件, 违 surgical) 精准applyScreenReaderAnsiGate(active)API 供运行时切换装饰 char gate (
src/constants/figures.ts):getFigures()lazy factory: 首调用读getInitialSettings().prefersReducedMotion, 返 frozenUNICODE_SET(默认, byte-identical 现常量) 或ASCII_SET(降级: ●→*, ↑→^, ○→o, ×→x, ...)createRequire懒加载 settings — 打破figures → settings → settings/types → PermissionMode → figures (PAUSE_ICON)循环 (top-level import 触发 TDZCannot access 'PAUSE_ICON' before initialization)export const(BLACK_CIRCLE 等) 保持不变, 未迁移消费方仍 Unicode3 消费方迁移 + 2 inline box-drawing 站
EffortIndicator.tsEFFORT_*→fig.EFFORT_*ContextVisualization.tsx└tree-branch literal →TREE_BRANCHmodule-scope const (reducedMotion →-)MarkdownTable.tsx│/─/box border →VBAR/HBAR/BORDERSmodule-scope const (reducedMotion →+/-/|ASCII)取舍 (Rule 2/3 surgical)
●literal 未迁移: React Compiler memo block ($[0] === react.memo_cache_sentinel= compute-once 常量), settings-dependent 值风险 stale cache on mid-session toggle。deferfigures.pointer/tick) 未 gate — 保 Unicode, defer测试 (2 新文件, 14 case, 全 pass)
colorizeScreenReader.test.ts(9 case): gate OFF (chalk.level=3force → ANSI emitted,length > 1) vs gate ON (raw, zero ANSI=== "x"), early-return 不破, env entry-point chain 锁定figuresScreenReader.test.ts(4 case): default byte-identical 现常量, env-on ASCII 降级, 缓存失效, ASCII/UNICODE 同键集验证
bun run typecheck— 0 errorsbun test src/__tests__/— 228 pass, 0 fail (无回归)bun run build+bun run build:dev:full— 全 passBLACK_CIRCLE=⏺+ colorize 输出 ANSI;FUSION_AX_SCREEN_READER=1BLACK_CIRCLE=*+ colorize 返 raw (零 ANSI) ✓Co-Authored-By: Claude Fable 5 noreply@anthropic.com