From 4ef7c9691632f235780eaf8bcb76748773469fd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Mon, 19 Oct 2026 00:26:23 -0400 Subject: [PATCH] R8.4: ElyMark typesets inline formulas; \( and \[ read as math Co-Authored-By: Claude --- Cargo.lock | 2 +- Cargo.toml | 2 +- agents.md | 5 +++-- apps/ely-mark/src/output/mod.rs | 5 ++--- apps/ely-mark/src/output/plain.rs | 15 +++++++++++---- apps/ely-mark/src/output/tests/visual.rs | 18 ++++++++++++++++-- tasks/TASKS.md | 5 +++-- tasks/reviews/R8.4-r1.md | 17 +++++++++++++++++ tasks/reviews/R8.4-r2.md | 7 +++++++ tasks/reviews/R8.4-r3.md | 3 +++ 10 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 tasks/reviews/R8.4-r1.md create mode 100644 tasks/reviews/R8.4-r2.md create mode 100644 tasks/reviews/R8.4-r3.md diff --git a/Cargo.lock b/Cargo.lock index 8c3a3b0..5d5d1ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1986,7 +1986,7 @@ dependencies = [ [[package]] name = "ely-gpui-component" version = "0.1.1" -source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=7d704d97f40a9d47c08891c35d7dbe26a2220e55#7d704d97f40a9d47c08891c35d7dbe26a2220e55" +source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=de961eb164c1c88797ea35f0860aba9d18c95e4f#de961eb164c1c88797ea35f0860aba9d18c95e4f" dependencies = [ "alacritty_terminal", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 0ff1eca..f289bf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,7 @@ jiff = "0.2" anyhow = "1" directories = "6" dunce = "1" -ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "7d704d97f40a9d47c08891c35d7dbe26a2220e55" } +ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "de961eb164c1c88797ea35f0860aba9d18c95e4f" } env_logger = "0.11" gpui = { package = "gpui-pre", version = "=0.3.8", default-features = false, features = ["stacker"] } gpui_platform = { package = "gpui-pre-platform", version = "=0.3.8", features = ["font-kit", "wayland", "x11"] } diff --git a/agents.md b/agents.md index 086c58b..5d582ed 100644 --- a/agents.md +++ b/agents.md @@ -40,7 +40,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木 ## 组件映射 -依赖 `ely-gpui-component`(pin 7d704d9,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。 +依赖 `ely-gpui-component`(pin de961eb,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。 | 需要 | 用 | | --- | --- | @@ -80,7 +80,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木 | 幻灯片缩略图条(`documents::PageThumbnailList` 只收图片路径,无隐藏标记、右键菜单与重排) | `apps/ely-show` 的 `slides.rs`,内核按页绘制到内存图 | | PDF 页面缩略图(同上,且一次铺全部页,不合 PERF-05) | `apps/ely-pdf` 的 `side.rs`,`uniform_list` 只画可见行,worker 按需渲染 | | 演示的参考线:LOK 不把参考线画进图块,也无参考线接口 | 补丁 0027 读写参考线;`apps/ely-show` 的 `guides.rs` 画在画布上,拖动移动,拖出幻灯片即删除 | -| gpui 文本里不能嵌图片:行内公式无从排版 | 组件库的 `MarkdownRenderer` 把行内公式显示为 TeX 原文(着色),块公式才排版 | +| gpui 文本里不能嵌图片:行内公式无从排版 | 组件库 `render/math_text.rs`:以不断行的空格占出公式的宽(整个公式作一个词折行),排好的公式画在其上,按下处读回公式的原文 | | `A11yChecker` 的 `audit` 与对照表是私有的;Omarchy 色板要照同一套规则 | `ely-ui` 的 `omarchy/palette.rs` 抄了对照表。上游待办:公开 `audit` | | 组件无 role/aria 语义:菜单、列表等控件在 AX 树仍不可见 | 上游已修核心控件(7380afb:Button、TextInput、Tabs、面板行、对话框等);其余上游待办。项目自绘控件已自带 | | gpui 无 aria-disabled API(`AriaProperties` 无 disabled 字段) | 组件 7380afb 起以本地化 aria_description 表达;上游待办:gpui 补 API | @@ -320,3 +320,4 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely - 2026-10-16(用户定):ElyMark 打开的块保持原样(R7.5)。组件库 bff7ebc 起,可视模式里按下的块以原文显示在它合上时的框里(同样的边距与透明边框),字号、行高与颜色同合上时:段落、列表、引用等为正文,标题按其级数的字号、1.3 倍行高、加粗(六级为次要色),照原文写出的块(链接定义等)为等宽次要色;不再加深色底板与另一套边距,按下前后不挪位置。代码块、块公式与 front matter 仍有自己的底板(代码与 front matter 为等宽小号,front matter 为次要色,同合上时)。输入框的着色(源码模式同用)给标题与粗体同一字重(原为半粗,压过所设的加粗),六级标题里的字照为次要色(行内代码照为正文色,2e65b05),front matter 照 front matter 读(原读成分隔线加标题,键名变亮变粗)(1ff993d、962964e)。表格单元格照旧。打字机模式的居中另按两端余量判定(原只按块高与视图高,打开前后同高时首块不再居中)。同批带入组件库 cdc5ce8(标题栏拖动区里的控件按下归控件,Windows)。Windows 真窗口核对(`.work/r75`):段落、三级标题、代码块打开前后位置相同。 - 2026-10-18:Word 公式(R8.2,Gitea #9)。附件 math.docx 的六类公式逐类核对(本机无 Word,以文件所写为准):键盘输入、OMML(内核转为公式对象)、MathType(OLE,显示随带的 WMF 预览)、上下标、图片原本就对;差别在两处根因。其一,运行的 `w:position`(升降)原先只给文字,图片与 OLE 公式所在的运行失去它(25 张图片与一个 ½ 浮在行上):补丁 `0049-writer-inline-objects-move-with-their-run.patch` 把它作为固定升降写到对象的锚字符上(按锚字符自己的字号换算百分比,不扩展到其后的文字),版面让“作为字符”的对象随锚字符的固定升降移动(vertAlign 的自动上下标不动对象,同原样);各导出本就写出锚字符的升降,DOCX 往返仍为 `w:position`。其二,EQ 域原先只认 `\o`、`\s\do` 与注音,其余(如分数 `\f`)整个消失:补丁 `0050-writer-eq-field-parser.patch`(读法)与 `0051-writer-eq-fields-as-formulas.patch`(导入)把 EQ 域转为公式对象:`\f` 分数、`\r` 根式、`\s` 上下标、`\b` 括号、`\l` 列表、`\a` 阵列、`\i` 积分与求和求积;`\x` 与 `\o` 只留内容(无框、无叠印),`\d` 只前移,`\ai`、`\di` 只是行距(不升降),开关不分大小写;每个命令字符保留其运行的上下标(Word 照此画),同一段上下标里字样变了仍是一个上下标;斜体字母写成标识符(StarMath 与 OMML 都画成斜体,DOCX 往返不丢),斜体的数字与符号写成 `ital` 文字,存为 DOCX 后成正体(OMML 无此写法,已知限制);没有底的上下标以空文字为底(空的底在 OMML 里是占位符,重开成 ``);公式取域的字号;读不通的命令以其域代码显示为文字,不消失。只有整个域是 `\s\do N(文字)` 时照原样成为下移的文字,其余都经读法(原先小写的 `\s\do` 读到第一个括号即止,其后丢失)。`client/tests/word.rs` 以 `tests/fixtures/office/word.docx`(由 `.work/r8/gen/eq.py` 生成,不入库)核对公式文本(含开头的空格、大写开关、读不通的域、字样变化的下标、`\ai`、`\di`、小写 `\s\do` 的尾部与嵌套)、DOCX 往返后的斜体、下标与无占位符、锚字符的升降与图块里图片低于文字(去掉版面的升降即失败)。审查三轮:第 3 轮的四处已修并以上述测试核对,未再审查(每条至多三轮)。仍与 Word 不同:编辑区里公式与图片四周有内核的辅助框线(`ShowBoundaries`,视图辅助,不印出);DOCX 另存后重开行距变宽,因内核导出改写文档默认字体与段距,与公式无关。 - 2026-10-18:工作表过多时标签条(R8.3,Gitea #8)。组件库 `navigation::Tabs` 横排时可横向滚动(滚轮同样),标签可窄于自然宽度;所选标签在所选或标签条宽度变了时滚入视口,在绘制时以刚排好的宽度判定(原先只在首帧滚一次,其后右栏打开、标签条变窄,所选标签被挤出视口);手动滚动保留到二者之一再变;横排的标签至多与标签条同宽,名字过长以省略号结尾(组件库 e56a343、3a68d1b、7d704d9,同批带入上游列外壳拖动的四个提交)。ElyTable 的标签条随之收缩,添加按钮留在其旁;标签条的状态按工作簿各记一份(元素 id 带标签号),切到另一工作簿时其所选标签照样滚入。`navigation/tests/tabs.rs` 以 30 个标签核对:首帧滚到所选、滚轮回到第一个、新选的滚入、变窄后所选仍在末端(去掉按宽度判定即失败)、长名的所选标签整个在标签条内(去掉宽度上限即失败);ElyTable 的 `tests/strip.rs` 以 `sheets::strip` 画两个各有 30 张表、都选第 30 张的工作簿,滚回开头后来回切换,所选标签每次都在标签条内(两个工作簿共用一个 id 即失败)。Windows 真窗口(`.work/r8/strip3.png`):30 张表、第 30 张为当前,1100 宽时标签条末端为它。 +- 2026-10-18:ElyMark 公式(R8.4,用户定两项都做)。行内公式照块公式由 RaTeX 排版(原只显示着色的 TeX 原文):gpui 的文本里放不进图片,组件库 `render/math_text.rs` 把每个行内公式换成等宽的不断行空格(U+00A0,按字体量出个数),排好的公式居中画在其上,行高随最高的公式撑开;空格串与词一样整个折到下一行,画在其所在行(折行处的序号 gpui 读作上一行末);按下与右键所在的字节经换算读回公式的原文;排不出的公式照旧显示 TeX 原文,其后以危险色写出 RaTeX 给的原因与位置,记警告(不含公式)。`\(…\)` 读作行内公式,`\[…\]` 读作块公式(ChatGPT 等常写的分隔符;GitHub 不认,Typora 默认不认):解析前逐字节把成对的分隔符换成 `$`(长度不变,位置照旧对得上)。成对只在同一段落、标题或单元格里(跨列表项、CR 空行不成对);不跨代码、公式、HTML 标签、链接地址与引用标签、图片、脚注引用,``、`` 之间(含嵌套;交叉闭合照可视的读法关闭与重开)的文字照原文;`\\(` 不换。公式里与段落里另有的 `$`(含 `\$`)解析时暂写成文本里没有的控制字节,解析后还原,不与换上的 `$` 配对;相邻的两个公式之间暂插一个空格,解析后去掉,仍是两个公式。公式节点的范围是整对分隔符,值是写下的 TeX 原文。独占一段的 `\[…\]` 为块公式(段首可有一至三个空格)。组件库公开 `documents::markdown_tree`,ElyMark 的 HTML、PDF、纯文本导出都经它解析(`markdown_options` 不再公开),纯文本里独占一段的公式照块公式写其 TeX。同时查出(组件库 e095f06):RaTeX 的 SVG 以 pt 定尺寸,usvg 按 px 量墨迹,两者差 4/3,每个公式的画面右侧与下方多出约四分之一的空白(块公式因此偏左)、导出的公式比正文大 4/3;现生成时即把根改为 px。`markdown/tex/tests.rs` 与 `tree/tests.rs` 核对分隔符的各种情形(相邻、`\$`、链接地址、``、跨块),`math_text.rs` 的窗口测试核对公式排进并读回原文、折到第二行的公式画在第二行首(改回按起点定位即失败)、排不出的公式写出原因(组件库 a20e712、5fba7b3、de961eb),`math/tests.rs` 核对画面与公式同宽(改回 pt 即失败),ElyMark 的 `visual.rs` 核对导出与纯文本。Windows 真窗口(`.work/r8/mark/visual2.png`):`$…$`、`\(…\)` 排在行内,`\[…\]` 居中成块,行内代码与转义的 `\\(` 照原文。 diff --git a/apps/ely-mark/src/output/mod.rs b/apps/ely-mark/src/output/mod.rs index f5aa857..64acaed 100644 --- a/apps/ely-mark/src/output/mod.rs +++ b/apps/ely-mark/src/output/mod.rs @@ -10,7 +10,7 @@ mod tests; use std::{io, path::Path}; -use ely_gpui_component::documents::markdown_options; +use ely_gpui_component::documents::markdown_tree; pub use files::{Format, Target, write}; pub use pictures::{Pictures, picture_file}; @@ -43,8 +43,7 @@ pub fn body(text: &str, beside: Option<&Path>, pictures: &Pictures) -> io::Resul /// `text`'s body; `placed` keeps what it copied. fn page_of(text: &str, placed: &mut pictures::Placed) -> io::Result { - let tree = markdown::to_mdast(text, &markdown_options()) - .map_err(|error| io::Error::other(error.to_string()))?; + let tree = markdown_tree(text); let mut page = Page::default(); html::write(&tree, text, placed, &mut page)?; log::info!( diff --git a/apps/ely-mark/src/output/plain.rs b/apps/ely-mark/src/output/plain.rs index 6093681..52da632 100644 --- a/apps/ely-mark/src/output/plain.rs +++ b/apps/ely-mark/src/output/plain.rs @@ -3,14 +3,13 @@ use std::io; use ely_gpui_component::documents::{ - MarkdownBlock, MarkdownReader, alert, is_toc, markdown_options, + MarkdownBlock, MarkdownReader, alert, is_toc, markdown_tree, }; use markdown::mdast::Node; /// `text` as plain text, a blank line between blocks. pub fn plain(text: &str) -> io::Result { - let tree = markdown::to_mdast(text, &markdown_options()) - .map_err(|error| io::Error::other(error.to_string()))?; + let tree = markdown_tree(text); let reader = MarkdownReader::joined(text, &tree); let mut blocks = Vec::new(); // Alerts and `[toc]` live only at the root. @@ -75,7 +74,15 @@ fn block(reader: &MarkdownReader, node: &Node, blocks: &mut Vec) { block(reader, child, blocks); } } - Node::Paragraph(_) | Node::Heading(_) => blocks.push(words(reader, node)), + Node::Paragraph(_) => { + let mut read = Vec::new(); + reader.block(node, &mut read); + match read.as_slice() { + [MarkdownBlock::Math(tex)] => blocks.push(tex.clone()), + _ => blocks.push(words(reader, node)), + } + } + Node::Heading(_) => blocks.push(words(reader, node)), Node::Html(_) => { let mut read = Vec::new(); reader.block(node, &mut read); diff --git a/apps/ely-mark/src/output/tests/visual.rs b/apps/ely-mark/src/output/tests/visual.rs index de5bfa4..1b6c302 100644 --- a/apps/ely-mark/src/output/tests/visual.rs +++ b/apps/ely-mark/src/output/tests/visual.rs @@ -1,7 +1,7 @@ //! Exports read the file as the visual mode does (R4.4). use super::{Pictures, page, plain}; -use crate::output::{Page, html::write, markdown_options, pictures::Placed}; +use crate::output::{Page, html::write, markdown_tree, pictures::Placed}; fn html(text: &str) -> String { page(text, None, &Pictures::Inline).html @@ -160,7 +160,7 @@ fn notes_under_deep_marks_write_in_linear_time() { "".repeat(count), "x[^n] ".repeat(count) ); - let tree = markdown::to_mdast(&source, &markdown_options()).expect("parsed"); + let tree = markdown_tree(&source); let mut placed = Placed::new(None, &Pictures::Inline); let start = std::time::Instant::now(); write(&tree, &source, &mut placed, &mut Page::default()).expect("written"); @@ -237,3 +237,17 @@ fn a_soft_break_joins_in_exports_as_typora_exports_it() { assert!(page.contains("

你好 测试
end

"), "{page}"); assert_eq!(text("你好\n测试\n"), "你好 测试\n"); } + +#[test] +fn tex_delimiters_export_as_formulas() { + let source = r"a \(x^2\) b + +\[ +y+1 +\] +"; + let page = page(source, None, &Pictures::Inline); + assert_eq!(page.html.matches("` 被换、`\$` 被拒、跨块成对、行内错误不显示、注释超长;第 2 轮:段首空格的块公式、嵌套 ``、注释超长;第 3 轮:交叉闭合的标签,修后以测试核对,不再审查) +- [ ] R8.5 发布 0.1.8(用户 2026-10-18 定,要快):版本号,打标签由发布流程三端建包、签名公证、上传 +- [ ] R8.6 答复 #8、#9,写已修于 0.1.8,修好的关闭;`agents.md` 随之 diff --git a/tasks/reviews/R8.4-r1.md b/tasks/reviews/R8.4-r1.md new file mode 100644 index 0000000..67d0bcf --- /dev/null +++ b/tasks/reviews/R8.4-r1.md @@ -0,0 +1,17 @@ +FAIL + +1. `../Ely-GPUI-Components/src/documents/render/math_text.rs:182`:U+00A0 占位不能保证公式整体换行;当前 GPUI 会在这些字符之间断行,图片却仍从占位起点整张绘制。160px 宽的段落显示 `aaaaaaaaaa $x^2+y^2=r^2$ tail` 时,探针测得同一公式占位从 `(105.6, 0)` 跨到 `(76.8, 26)`,公式越过右边界,下一行留下空位,点击位置也不再对应画面。不符合 R8.4 的行内排版,`agents.md` 的“不断行”说明亦不成立。修正方向:让公式宽度以不可拆分的单元参与换行,并测试公式接近行尾时的排版与命中。证据:`.work/r84-r1-probe-tests.log` 的 `formula_space_is_indivisible`;原有窗口测试未覆盖换行。 + +2. `../Ely-GPUI-Components/src/documents/markdown.rs:64`:把两端替换为 `$ ` 和 ` $` 会把相邻公式的边界拼成 `$$`。`\(x\)\(y\)` 实际变成 `$ x $$ y $`,只解析出一个内容为 `x $$ y` 的公式;`$x$\(y\)` 同样合并。可视模式与导出都失去原来的两条公式。修正方向:保留每对分隔符的独立公式节点,避免转换产生新的美元符号连续串,并覆盖两种分隔符相邻的组合。证据:`.work/r84-r1-probe-tests.log` 的 `adjacent_formulas_remain_distinct`、`.work/r84-r1-parser.log`。 + +3. `../Ely-GPUI-Components/src/documents/markdown.rs:40`:保护范围没有排除链接和图片的目标地址,整文件扫描会把地址里的转义括号当公式。合法的 `[go](https://e.test/\(x\))` 原本指向 `https://e.test/(x)`,现在变成正文碎片与指向 `https://e.test/$` 的错误链接;`![p](pics/\(x\).png)` 则不再是图片。违反 MARK-03,也影响使用同一解析入口的输出。修正方向:只在允许公式的正文范围转换,保留目标地址、标题和定义的语法;增加地址含转义括号的回归测试。证据:`.work/r84-r1-probe-tests.log` 的 `links_and_images_keep_their_addresses`、`.work/r84-r1-parser.log`。 + +4. `../Ely-GPUI-Components/src/documents/markdown.rs:62`:只要内容出现 `$` 就拒绝整对分隔符,连合法的 TeX 转义 `\$` 也拒绝。`\(\$5\)` 因此显示为普通文字 `($5)`,没有公式节点,而相同的 `\$5` 可由本项目的 RaTeX 正常排版。不符合 R8.4 的 `\(…\)` 支持。修正方向:按 TeX 的转义语义处理内容,不能用任意美元符号否决整对分隔符;覆盖含 `\$` 的行内与块公式。证据:`.work/r84-r1-probe-tests.log` 的 `escaped_dollar_is_valid_tex` 先确认排版成功,再在公式节点数量上失败。 + +5. `../Ely-GPUI-Components/src/documents/markdown.rs:56`:配对状态只在 LF 空行处清除,可以跨两个不同的 Markdown 块配对,且漏掉 CR 换行的空行。两项列表 `- \(a\n- b\)` 原本分别显示 `(a`、`b)`,现在分别显示 `$ a`、`b $`;`\(a\r\rb\)` 也会凭空出现美元符号。转换既未形成公式,又改变了普通文本。修正方向:按实际块边界限定配对范围,并使用 CommonMark 支持的 LF、CRLF、CR 换行规则;覆盖跨列表项和 CR 空行。证据:`.work/r84-r1-probe-tests.log` 的 `unrelated_blocks_do_not_acquire_dollars`、`.work/r84-r1-parser.log`。 + +6. `../Ely-GPUI-Components/src/documents/markdown.rs:44`:仅保护 `Html` 节点的标签字节,没有保护 HTML 代码标签的正文。`a \(x\) b` 中的代码内容被转换成公式节点,渲染时会排成公式,失去应展示的代码文字,与 `agents.md` 所称代码、HTML 中不转换不符。修正方向:识别并保留 HTML 代码内容的范围,使解析、可视显示与导出都保持代码语义;增加 `` 内分隔符的回归测试。证据:`.work/r84-r1-probe-tests.log` 的 `html_code_stays_literal`。 + +7. `../Ely-GPUI-Components/src/documents/render/math_text.rs:187`:行内排版失败时直接丢弃 `MathError`,只记固定警告,界面保留着色 TeX 而没有错误原因或位置。例如 `$x^2^3$` 的错误本应是 `Double superscript at 3`,此分支不把它交给任何可见元素。违反 §14.2“语法错误显示原文与错误位置”,也违反不吞错的规则;块公式已有可见错误处理。修正方向:保留并展示行内公式的错误及位置,同时保留原文,并增加失败公式的可见错误断言;日志继续不含公式内容。 + +8. `../Ely-GPUI-Components/src/documents/tree/mod.rs:321`、`../Ely-GPUI-Components/src/typography/math.rs:196`:两条新增或改写的注释分别为 “Text split at its images; lone `$$` or `\[` math is block.” 和 “`svg`'s drawing under a root viewing `[left, top, w, h]` in px.”,按空白分词均为 12 个词,超过十词上限。修正方向:各缩至十词以内。 diff --git a/tasks/reviews/R8.4-r2.md b/tasks/reviews/R8.4-r2.md new file mode 100644 index 0000000..74515b0 --- /dev/null +++ b/tasks/reviews/R8.4-r2.md @@ -0,0 +1,7 @@ +FAIL + +1. `../Ely-GPUI-Components/src/documents/tree/mod.rs:326`:块公式的判定检查整个段落是否以分隔符开头,漏掉 CommonMark 允许的段首空格。独占一段的 ` \[x\]` 因而读成 `Paragraph` 内的行内公式,而同样内容去掉一个空格即读成 `Math`,违反 R8.4 的块公式要求。修正方向:从公式节点的原文范围识别分隔符,正确处理段首一至三个空格,并补回归测试。实测:`.work/r84-r2-review/component-probes.log` 的 `review_r84_display_math_accepts_prose_indentation` 失败。 + +2. `../Ely-GPUI-Components/src/documents/markdown/tex.rs:100`:HTML 代码保护只记一个标签,遇到同名内层标签的关闭即清掉保护,外层仍未关闭。`a \(x\) \(y\) b` 中的 `\(y\)` 被转成公式;组件读出的文字由 `a (x) (y) b` 变成 `a (x) y b`,并给 `y` 加上 `Math` 样式。这仍会把代码内容排成公式,违反 MARK-04 与 `agents.md` 的代码保护说明;第 1 轮第 6 项只修好了非嵌套情形。修正方向:按现有 HTML 读者的嵌套与闭合规则维护保护范围,覆盖嵌套的 `code`、`kbd`。实测:`.work/r84-r2-review/component-probes.log` 的 `review_r84_nested_code_stays_literal`、`review_r84_nested_code_keeps_its_text_and_styles` 均失败。 + +3. `../Ely-GPUI-Components/src/typography/math.rs:196`、`../Ely-GPUI-Components/src/documents/markdown/tex.rs:244`:注释 “`svg` drawn in a px root viewing `[left, top, w, h]`.” 与 “Restores a text's dollars; drops a parting space at its ends.” 按空白分词均为 11 词,仍超过十词上限;前者是第 1 轮第 8 项未修完的问题,后者为本轮新增。修正方向:各缩至十词以内。证据:`.work/r84-r2-review/rules.log`。 diff --git a/tasks/reviews/R8.4-r3.md b/tasks/reviews/R8.4-r3.md new file mode 100644 index 0000000..885b827 --- /dev/null +++ b/tasks/reviews/R8.4-r3.md @@ -0,0 +1,3 @@ +FAIL + +1. `../Ely-GPUI-Components/src/documents/markdown/tex.rs:109`:代码保护的新栈只删除同名标签,没有遵循 `Reader::end` 的闭合与重开规则。`a x \(y\) b` 中,读者在 `` 处同时结束不会重开的 `kbd`,其后的文字已是普通正文;保护栈却仍保留 `kbd`,把正文保护到段末,导致 `\(y\)` 不被识别为公式。实测可视读法为 `a x (y) b`、没有 `Math` 样式,纯文本导出也多出括号;`a x \(y\) b` 同样失败。违反 R8.4 与 MARK-04/11。修正方向:让保护范围与现有 HTML 读者采用相同的标签闭合、内层清理和重开规则,并覆盖交叉闭合后恢复公式识别的情形。证据:`.work/r84-r3-review/probe-tests.log` 中三个失败探针;第 2 轮的段首缩进与正常嵌套探针均已通过。