Skip to content

[R2-07][文字] 验收复杂 Unicode、IME 与键盘/选择语义 #30

Open
2 of 3 issues completed
Open

Description

@JavaZeroo

当前状态(2026-11-01)

用户明确要求 #30保留。正式产品已有Unicode标量/宽格基础、Qt文字字节传输、IME组合/提交/取消(#205/#230),但不是完整文字交互。#155是本项IME验收子项,不重复计作独立产品工作包。原生前置#26/#39已闭,可独立推进;#39的Alacritty字素成功不能转作自主核心能力。

本轮实现(2026-11-01)

已推送联合源码 f20fe0b:正式 Qt 新增无修饰 Home/End/Insert/Delete/PageUp/PageDown,并接入可见物理页基础正反拖选高亮。Home/End 复用既有 DECCKM 编码;本轮端到端验的是普通模式。独立 ReadConsoleW 接收端精确验证31B及代理项在导航键两侧的替换顺序;Shift+Insert 不会变成普通 Insert,Ctrl 字母除C的边界保持。

Qt 三项在同次联合执行通过,含真实子进程、固定 ConPTY、输入隔离和自然回收。新增鼠标检查使用公开消息及 offscreen 图像;不等于物理键鼠、原生 IME 或 CF 验收。固定源码、原始失败和验证边界。已随 f72c5c4 推送 main。

同一提交的 正式 Qt/固定 ConPTY CI 与 Windows/Linux 总回归 均成功;既有参考构建回归另列,不转作正式核心能力或性能验收。

系统复制/粘贴、选词/选行、历史浏览/自动滚动、Find 与八会话组合仍未完成;#155 按既定合同继续。本项不关闭。

剩余四层合同

  • 核心文字:明确Unicode版本、字素边界/宽度、有界标记与spill策略;核对ZWJ/keycap等固定样本、分块/逐字节、宽格擦除及resize/历史交叉。当前Unicode15逐scalar和最多16marks不等于完整字素。
  • Qt呈现与交互:核对实际glyph/fallback/网格;接入选择、复制、粘贴、Find与#29映射,使用实际CF与独立child字节预期。
  • 键盘与IME:与#28核对应用模式、编辑/修饰键、代理项和bracketed paste;当前Ctrl字母除C不支持,改变前按原版证据处理。#155继续候选身份、字体变化、Ctrl-C交付/消费以及输出/resize/焦点/隐藏/关闭交叉。
  • 组合会话:在同一正式构建完成Copy→Paste→IME与八会话目标隔离,记录取消、关闭、错误和回收;两会话或旧Win32工作流不能替代。

Qt入口 · 固定拼音 · N11有限对照。

N11来自历史冻结Qt源5fab30b:输出后面板停旧位两侧一致、resize延后点均更新;原版字体放大未触发、面板身份未证明。不能覆盖当前main全输入验收。R2单屏96/144由#35最终配置核对;跨屏归#297/R4。

Activity

  1. added
    enhancementNew feature or request
    blocked有明确未完成前置;见原生 blocked-by
    on Oct 24, 2026
  2. added
    ready-for-agent范围明确且真实阻塞已解除,可开始实施
    and removed
    blocked有明确未完成前置;见原生 blocked-by
    on Oct 25, 2026
  3. JavaZeroo commented on Oct 25, 2026

    @JavaZeroo
    OwnerAuthor

    调查记录(保持 OPEN,未形成验收):

    • 在隔离 1008×681 工作区的原始 F 场景(五路各 32000 条)中,源记录为 35 个点;真实 ConPTY 运输包含 30 个点、CSI 定位、再写 6 个点。TermForge 的物理行是 31/5,ReadConsoleOutputW 的子控制台单元格则是 30/5。不能用“直接源字节 == 最终屏幕”或“接受任意续行”处理这组差异。
    • 独立 pyte 重放也产生 31/5,但它把 e+U+0301 规范化为预组合 é,不能直接当作原文判据。候选 trace 的空白投影同样不直接替代原文验证。
    • 固定 WindTerm 2.7.0(SHA-256 7b10531d…804d0)只读 Ghidra 调查中,0x1405fab20 对 mark 等条件走整个 QString 的 horizontalAdvance;0x1405fa450 有按 scalar 的字体度量缓存。10 个定向函数均 decompilation_completed=true。这些是静态事实,不证明实际 ConPTY 网格、宽度或 WindTerm 等价。

    后续分别验收源字节、子控制台、独立模型与 TermForge 的文字/单元格,保留分解形式、选择复制和已知策略差异;不修改负载、超时或断言来掩盖问题。当前没有 R2 完成或真实呈现结论。

  4. JavaZeroo commented on Oct 25, 2026

    @JavaZeroo
    OwnerAuthor

    已推送限定校验 730b5cde632bb33e2411686442a3b9c7255a64c5,本 issue 的完整 Unicode/IME 验收仍保持 OPEN。

    已验证:22 个纯校验与篡改负例、无窗口 ConPTY fixture、默认/候选各两轮常规工作流,以及隔离 1008×681 的完整 F 场景。五路各 32000 条记录和原 90 秒上限未改变;26 检查点和 18 个会话身份/回收通过,complete_workflow_verified=false。

    校验不再假设直接源字节等于最终屏幕:完整源文件、真实子控制台 CHAR_INFO、实际运输尾部与独立 pyte==0.8.2 布局分别核对,TermForge 必须保留 e+U+0301 的分解形式。pyte 的 NFC 结果只用于布局,不能替代原文。多点、少点、错行、错误尺寸/代次、缺少完成边界和分解形式变化均拒绝。

    调查入口与只读 FindUnicodeMetrics.java 已同步到 research/README.md。静态 Ghidra 事实不冒充运行时或 WindTerm 等价;完整字素/emoji、IME、真实 DPI、fallback、#36/#38 和实际呈现仍未完成。

  5. JavaZeroo commented on Oct 25, 2026

    @JavaZeroo
    OwnerAuthor

    已推送限定输入切片 fe0240051d8cc7e508362df9578c2a3c4e4a2d08 与证据文档 e3d10baa5cbd6c44d2ff90913fe9ad06a30e3f8b;本 issue 继续保持 OPEN。

    • 固定 e + U+0301、单个 emoji、👩‍💻(ZWJ)和 1️⃣(keycap)经 WM_CHAR → ConPTY → 真实 ReadConsoleW 接收端逐字节一致。默认/候选各两轮,四份接收文件均为 84 字节,SHA-256 为 30a48ee8a238d1e6757a69ec5d7ec3848c694b9d8c9edc342e45ae1a0c0cc305;AltGr、编辑键和代理项边界仍在同一断言内。
    • 默认完整构建通过;候选定向五项 CTest 全部通过。默认完整 CTest 为 39/40,唯一失败仍是 shell 内 [R1回归] 持续 cmd 输出时 ETX 未中断导致 normal/resize 超时 #36 的 normal/resize 中断场景(2 errors、3 既有 hosted skip),没有减少负载或延长超时。
    • 证据:experiments/shell/results/2026-10-26-unicode-keyboard/validation.json;FEATURES、EVIDENCE 和场景说明已同步。源码/二进制/接收文件哈希已复核。

    这只完成精确输入字节验收,不证明新增样本的字素整形、单元格、glyph/fallback、选择复制、完整 IME 或应用键盘模式,也不构成 WindTerm 等价。未截图、未操作前台输入,presented_done=null。后续继续分别验收真实 ConPTY 屏幕与绘制路径,R2 路标不因此关闭。

  6. JavaZeroo commented on Oct 25, 2026

    @JavaZeroo
    OwnerAuthor

    限定原文/glyph 实现与测试已推送:18c36ce8a38fd4bbd3f09acd224b35e0852b3160。本 issue 与完整字素合同继续保持 OPEN。

    • DWrite 现在按实际一格/两格分别缓存布局,只在文字超出现有物理宽度时缩小字号,并在调整后重新审计 glyph;固定网格不扩大。原始超宽 emoji 为 24.7148 DIP,本机两格预算为 24 DIP,修复后实际布局为 24 DIP,无缺字。
    • 32 组 DPI/zoom/backend 绘制回归在默认和候选通过;呈现/工作流定向回归通过。test_candidate_tui 全组通过,其中固定 Unicode 真实场景两轮及十组篡改负例通过。
    • 固定 91 字节源、真实 ConPTY transport、子控制台 CHAR_INFO、候选文字/单元格与实际 glyph 分开核对。无 TermForge 窗口的原生探针也确认本机控制台将非 BMP 标量映射为 leading/trailing 两个 U+FFFD;这不冒充原文或 WindTerm 行为。
    • 当前候选仍将 ZWJ 分为 👩‍ 与 💻,keycap 为一格。这些仅是观察,未接受为完整聚类/宽度合同。完整流式分段由新建子项 #39 承担,[R2-07][文字] 验收复杂 Unicode、IME 与键盘/选择语义 #30 已有原生 blocked-by 关系;没有静默新增依赖。

    只读 WindTerm 调查只能确认整串/逐 scalar 测量路径,不能确定实际 ZWJ/keycap 网格宽度。完整默认/候选回归正在执行,最终证据和计数随后提交;不宣称完整 Unicode/IME、真实呈现或 R2 完成。

  7. JavaZeroo commented on Oct 25, 2026

    @JavaZeroo
    OwnerAuthor

    限定证据已推送:c73871e(实现为 18c36ce)。完整记录见 experiments/shell/results/2026-10-26-unicode-renderer/validation.json。

    本切片只通过原文保留、单元格观测和实际 glyph-run/布局宽度验证;完整 ZWJ/keycap 聚类与宽度仍由 #39 阻塞,完整 Unicode/IME、选择、真实 DPI、呈现和 R2 均未完成。

  8. added
    blocked有明确未完成前置;见原生 blocked-by
    ready-for-agent范围明确且真实阻塞已解除,可开始实施
    and removed
    ready-for-agent范围明确且真实阻塞已解除,可开始实施
    blocked有明确未完成前置;见原生 blocked-by
    on Oct 26, 2026
  9. JavaZeroo commented on Oct 30, 2026

    @JavaZeroo
    OwnerAuthor

    结论

    核对对象:HEAD d3f24d6(落后 origin/main 两个提交,差异不涉下表文件)。5 项清单:0 项完成,3 项部分,2 项未实现。产品核心按 Unicode 15.0.0 逐 scalar 宽度(experiments/core/unicode_width.hpp:1-2),组合标记以 marks<=16 附到基格(experiments/core/screen.hpp:407-411),没有字素聚类(ZWJ 会拆成多个宽字);Qt 侧无 clipboard/selection。

    清单逐项状态

    验收项 状态 证据
    1. 最终配置交叉合同:标量/分解、单元格/光标、glyph/fallback、命中选择复制、真实字节 部分 真实接收字节已验(src/qt/unicode_input_test.cpp,src/qt/README.md:43:中文/NFD/nonBMP/ZWJ/keycap 精确 UTF-8,含代理项未配对正反例 :92、:197);宽度/分解保留见 screen.hpp:404-411。缺:无字素聚类(👩‍💻 拆格)、Qt 无缺字/fallback 审计(对比 Win32 experiments/presentation/render.hpp:109-142 的 GlyphAudit)、无命中/选择复制
    2. IME 组合/提交/取消 × 输出/刷新/resize/zoom/会话焦点/隐藏/关闭 部分 事件级实现+已测:src/qt/terminal_widget.hpp:172-188,src/qt/shell_smoke.cpp:361-386(preedit 不发送、commit 到达、cancel 写 0 字节);固定微软拼音真实事件序列与光标锚点 research/ui/results/2026-10-30-qt-ime-fixed-pinyin/README.md:12-34。缺:与 resize/zoom/标签切换/隐藏/关闭的交叉(Qt 无 zoom);候选窗不可观测;resize 后 caret rect 未复核(:142-152 只在查询时现算)
    3. 应用键盘模式、编辑/修饰键、代理项边界、bracketed paste 真实字节 部分 代理项边界已覆盖(unicode_input_test.cpp:92,197);缺:Qt 键映射只有 Return/Backspace/Tab/方向键(terminal_widget.hpp:104-121),无 Home/End/Delete/Insert/PgUp/PgDn、无 Ctrl/Alt 编辑组合;无 DECCKM(?1,terminal.hpp:33-50 只认 ?1049/?7);产品源码无 2004
    4. 最终配置实际 CF 选择复制、Copy→Paste→IME、同次八会话 R1 未实现 src/qt 无 clipboard/selection(检索 0);八会话 R1 只在 Win32 实验 shell(experiments/shell/test_workflow_sessions.py:85,191-208)
    5. Unicode 版本/bounded spill/字体宽度策略/已知差异进 FEATURES 部分 docs/FEATURES.md:44 只写候选 .4 的 Unicode 17 合同;产品核心的 Unicode 15.0 + marks<=16 + 无有界 spill 未写入;Qt 字体策略仅 src/qt/terminal_widget.hpp:242-249(固定 Roboto Mono,无 fallback 描述)

    下三个切片

    切片 1 · Qt 编辑/导航/修饰键精确字节

    • 文件:src/qt/terminal_widget.hpp(:104-121 键映射扩展 Home/End/Delete/Insert/PgUp/PgDn 与 Ctrl/Alt 编辑组合;以 experiments/shell/keyboard.hpp 既有预期字节为准,不另立策略)、src/qt/unicode_input_test.cpp(新增用例)。
    • 证明测试:扩展 qt_unicode_input(35 s、offscreen、独立 shell_input_client.exe 接收端)断言每个键的精确字节与修饰键差异,并保留未配对代理项/不可用目标的负例。
    • 决策:无(沿用 R1 已验的键位策略与 #28 的字节预期)。

    切片 2 · IME 与 resize/标签切换/隐藏/关闭交叉

    • 文件:src/qt/terminal_widget.hpp(preedit 在失焦/隐藏/关闭时按"未提交即丢弃"处理,绝不写入会话;resize/字体变化后重新查询 caret rect)、src/qt/shell_smoke.cpp(新增阶段)、结果记录 experiments/shell/results/2026-10-30-qt-ime-cross/validation.json。
    • 证明测试:qt_cpp_shell 新阶段:组合中 resize / 切标签 / 隐藏并关闭,断言另一会话收不到任何组合字节、已提交文字不重复、caret rect 在新网格内;失败即红,不放宽断言。
    • 决策:无("未提交文本只做预览、不跨目标"是既有实现与本项验收条的交集;真实输入法的失焦行为仍留作未观测项,不在此片裁决)。

    切片 3 · 产品核心按 #39 合同实现 ZWJ/keycap 聚类与宽度

    • 文件:experiments/core/screen.hpp(put 增加聚类前置状态:ZWJ/VS16/区域指示符/keycap,只生成单个 lead 与其 2 格尾格)、experiments/core/unicode_width.hpp + generate_width.py(仅在确需新表时)、CMakeLists.txt(grapheme_cluster_core)。
    • 证明测试:新增 experiments/core/grapheme_cluster_test.cpp 并扩展 experiments/core/test_unicode.py:👩‍💻 单 lead/两格、1️⃣ 单格、NFD 原文保留、分块与逐字节一致、1 列屏幕拒绝/Recovery 替换;与 Qt 绘制只用 cell.width(terminal_widget.hpp:201-213)对齐。
    • 决策:无(采用 docs/research/streaming-graphemes.md 已发布的 [R2-07][文字] 裁决并实现流式复杂字素聚类与宽度 #39 合同,不新增行为选择)。

    边界

    • 仅只读核对:未构建、未运行 ctest 或 GUI 场景,未修改代码、测试或证据。
    • 未做真实 IME 的失焦/resize 交叉观察;候选窗、真实不同 DPI 与实际呈现仍未知。
    • 未验证候选 .4 的 Unicode 17 字素实现能否直接搬入产品核心(字形/宽度表差异未核对,仅引用合同)。
    • 未取得原版 WindTerm 的字素运行行为;ZWJ/keycap 目标宽度仍以 [R2-07][文字] 裁决并实现流式复杂字素聚类与宽度 #39 合同为准,不宣称原版等价。
  10. added 2 commits that reference this issue on Oct 30, 2026
  11. JavaZeroo commented on Oct 30, 2026

    @JavaZeroo
    OwnerAuthor

    现状记录:Qt 入口的 Ctrl+字母 目前是契约上不支持的

    我试着给 Qt 入口补齐 Ctrl+A…Z(发 0x01–0x1A,与 Win32 侧 WM_CHAR 的字节一致),CI 的 qt_unicode_input 立刻变红,原因是一条早已写死的断言:

    QKeyEvent unsupported(QEvent::KeyPress, Qt::Key_D, Qt::ControlModifier, QString(QChar(4)));
    unsupported.setAccepted(false); QApplication::sendEvent(&terminal, &unsupported);
    require(!unsupported.isAccepted(), "Qt Ctrl-D became a supported product action");

    即:除 Ctrl+C 之外,Qt 入口的 Ctrl+字母必须不被处理,这是既有的验收契约(src/qt/unicode_input_test.cpp:159-161)。同一用例还锁定了既有字节串 Ab9 + CR/TAB/DEL + 0x03 + 四个方向键序列(:164)。

    我的处置

    • 已撤回该改动(不修改既有契约);
    • 若要支持 Ctrl+字母,这属于行为变更,需要先确认:目标 WindTerm 在 Qt 入口这些组合的实际字节,以及是否允许改变现有契约。

    顺带确认

    Win32 入口的 Ctrl+字母是经 WM_CHAR 得到 C0 字节的,与 Qt 入口的"只支持 Ctrl+C"并不一致——这个差异本身值得记录,但按现行契约,Qt 侧保持原样。

    边界

    • 本条只记录观察与撤回动作;
    • 未改变任何测试或产品行为。
  12. JavaZeroo commented on Oct 31, 2026

    @JavaZeroo
    OwnerAuthor

    本机可验证的部分已固化在 core 测试里

    本项验收要求在「自主 C++ 和 Qt 声明配置」下核对原始标量、字素边界、宽度/光标、实际 glyph、复制与子进程接收,并完成固定 IME 与同次八会话交叉。我把其中不依赖 GUI/真实输入的那一层先摸清,便于把范围写实。

    已固化(experiments/core/test_unicode.py,15 项,本机通过)

    python -m unittest experiments.core.test_unicode
    Ran 15 tests in 3.023s — OK
    

    覆盖项:CJK 宽度与 ASCII、四字节标量、歧义宽度按窄处理、组合字符贴右边距、剩一列时的宽字换行、宽字主/尾单元的覆盖与擦除、SGR 与 reset(含亮色与选择性 reset)、SGR 不取消 pending-wrap、带样式的宽字进入历史、擦除使用当前样式、非法 UTF-8 与不支持序列、单列放不下宽字。

    这些用例对每个固定 chunk 大小都跑一遍,因此覆盖的是解析层的分块无关性,不是渲染层。

    仍未覆盖(属于本项剩余验收)

    要求 状态
    实际 glyph(字体栅格化后的字形/宽度) 未覆盖,需要真实绘制证据
    真实 OS IME(非合成事件) 由 #155 承接,已有固定拼音限定证据
    复制与子进程接收 部分在 experiments/shell 的选择/剪贴板场景里,未与本项交叉
    同次八会话交叉 未覆盖
    Qt 声明配置下的同一批标量 未覆盖:上述 15 项只跑自主 C++ 核心

    建议

    把本项按上表拆成「核心解析层(已固化)/Qt 呈现层/真实输入层/交叉会话层」四块验收,各自指向已有证据或明确标为缺口;否则「复杂 Unicode 验收」会一直是一个无法判定的整体。

    本条只做覆盖核对,不关闭本项,也不修改验收标准。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agent范围明确且真实阻塞已解除,可开始实施

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions