diff --git a/apps/ely-tree/src/view.rs b/apps/ely-tree/src/view.rs index cee2f5e..a901e90 100644 --- a/apps/ely-tree/src/view.rs +++ b/apps/ely-tree/src/view.rs @@ -209,13 +209,7 @@ impl Tree { .iter() .filter(|entry| entry.state != State::Ignored) .count(); - let (icon, title) = match changed { - 0 => (IconName::CircleCheck, text(cx, "tree.clean", &[])), - n => ( - IconName::GitBranch, - ely_ui::i18n::counted(cx, "tree.changed", n as u64, &[("n", &n.to_string())]), - ), - }; + let (icon, title) = summary(changed, cx); EmptyState::new(("tree-summary", opened.id), icon, title).into_any_element() } } @@ -263,3 +257,25 @@ impl Render for Tree { .children(self.dialog(cx)) } } + +/// The work tree's line: clean, or how many files changed. +pub(crate) fn summary(changed: usize, cx: &App) -> (IconName, SharedString) { + match changed { + 0 => (IconName::CircleCheck, text(cx, "tree.clean", &[])), + n => (IconName::GitBranch, ely_ui::i18n::counted(cx, "tree.changed", n as u64, &[("n", &n.to_string())])), + } +} + +#[cfg(test)] +mod tests { + #[gpui::test] + fn the_summary_counts_files_in_both_languages(cx: &mut gpui::TestAppContext) { + cx.update(|cx| { + ely_ui::i18n::install_as("en-US", cx); + let said = |n: usize, cx: &gpui::App| super::summary(n, cx).1.to_string(); + assert_eq!([said(0, cx), said(1, cx), said(2, cx)], ["Nothing to commit", "1 file changed", "2 files changed"]); + ely_ui::i18n::install_as("zh-CN", cx); + assert_eq!([said(1, cx), said(2, cx)], ["1 个文件有改动", "2 个文件有改动"]); + }); + } +} diff --git a/crates/ely-ui/src/i18n.rs b/crates/ely-ui/src/i18n.rs index b4e2a59..ec673cf 100644 --- a/crates/ely-ui/src/i18n.rs +++ b/crates/ely-ui/src/i18n.rs @@ -134,6 +134,11 @@ pub fn install(cx: &mut App) { let tag = locale_tag(cx); let locale = resolve(&tag); log::info!("i18n: locale {tag} -> {locale}"); + install_as(locale, cx); +} + +/// Installs the catalogs speaking `locale` (en-US or zh-CN). +pub fn install_as(locale: &'static str, cx: &mut App) { let (en, zh) = catalogs(); cx.set_global( I18n::new(locale) @@ -234,6 +239,28 @@ mod tests { } } + #[gpui::test] + fn counted_reads_one_and_many_in_both_languages(cx: &mut gpui::TestAppContext) { + cx.update(|cx| { + super::install_as("en-US", cx); + let said = |n: u64, cx: &gpui::App| super::counted(cx, "tree.changed", n, &[("n", &n.to_string())]); + assert_eq!((said(1, cx), said(2, cx)), ("1 file changed".into(), "2 files changed".into())); + super::install_as("zh-CN", cx); + assert_eq!((said(1, cx), said(2, cx)), ("1 个文件有改动".into(), "2 个文件有改动".into())); + }); + } + + #[test] + fn every_english_singular_reads_unlike_its_plural_at_one() { + let (en, _) = catalogs(); + let text = |key: &str| en.iter().find(|(k, _)| *k == key).map(|(_, v)| *v).expect("the key"); + for (key, one) in en.iter().filter(|(key, _)| key.ends_with(".one")) { + let many = text(key.trim_end_matches(".one")).replace("{n}", "1").replace("{count}", "1"); + assert_ne!(*one, many, "{key} reads like its plural"); + assert!(!one.contains("{n}") && !one.contains("{count}"), "{key} names its one"); + } + } + #[test] fn both_catalogs_hold_the_same_keys_and_singulars() { let (en, zh) = catalogs(); diff --git a/tasks/reviews/R2.11-r1.md b/tasks/reviews/R2.11-r1.md new file mode 100644 index 0000000..b390987 --- /dev/null +++ b/tasks/reviews/R2.11-r1.md @@ -0,0 +1,3 @@ +FAIL + +crates/ely-ui/src/i18n.rs:230 — 单复数回归测试只检查 count_key 的键名;:238 的目录测试只检查键集合及已有 .one 键的复数对应项,缺少 counted 的实际输出和 ElyTree 汇总文案断言。具体复现:在隔离副本中将 tree.changed.one 的英文值改成 "1 files changed",原有三项 i18n 测试全部通过,实际 counted 输出和使用同一 EmptyState 组件的原生窗口截图 OCR 均为 "1 files changed";同时删除中英目录中的 tree.changed.one,三项测试仍全部通过,原生窗口在计数为 1 时因 no message "tree.changed.one" in en-US 崩溃。基线独立运行 ely-ui 单测 30/30、ElyTree 单测 110/110,五个受影响应用的 cargo check 通过。修复方向:通过安装真实目录并调用 counted,断言中英文的单数、复数和插值结果;为 ElyTree 的 0、1、2 个改动场景增加实际渲染文案断言,使上述文案变异、缺键及调用点退回 text 都能触发测试失败。 diff --git a/tasks/reviews/R2.12-r1.md b/tasks/reviews/R2.12-r1.md new file mode 100644 index 0000000..1509950 --- /dev/null +++ b/tasks/reviews/R2.12-r1.md @@ -0,0 +1,7 @@ +FAIL + +../Ely-GPUI-Components/src/git/rows.rs:99 — 文件类型判断只产生 prose 布尔值,代码文件继续统一调用通用 code_colors,R2.12 的按文件类型着色仍有错误。复现:用 DiffViewer 分别查看 script.py、config.toml,旧内容为 `# return 42\n`,新内容为 `# return 42\nvalue = 1\n`。Python 和 TOML 的首行均为整行注释,原生窗口中却把 return 画成紫色关键字、42 画成橙色数字;行内和并排布局均复现。实际词法输出为 Mark、Word、Number,调用链没有传入语言。修复方向:将文件类型传入实际着色层,按对应语言处理注释、字符串和关键字,并保留 Markdown 的纯文本处理。 + +../Ely-GPUI-Components/src/git/rows.rs:131 — 新增测试只断言 prose(path) 的分类结果,缺少实际 DiffViewer 着色的回归断言。隔离副本中将 viewer.rs:197 的 `let prose = prose(&self.path);` 改为 `let prose = false;`,Markdown 在两种布局下重新出现代码高亮,组件库 `cargo test --lib --features test-support --offline --locked git::` 仍然 35 项全过。原生截图对同一 Markdown 文本区域比较,行内布局有 873 个像素变化,并排布局有 805 个像素变化;证据保留于 /tmp/R2.12-r1-render.log 和 /tmp/R2.12-r1-mutation-test.log。基线 ElyTree 构建、检查和 110 项测试也通过。修复方向:补充两种布局的实际文本颜色断言,覆盖 Markdown 纯文本和代码文件的语言着色,使该突变触发测试失败。 + +../Ely-GPUI-Components/src/git/rows.rs:96 — 本次新增注释 `A line's text, colored as code unless prose, changed words washed.` 按空白分词为 11 个词,超过本次审查要求及 agents.md 的十词上限。修复方向:将其缩短至十词以内,保持单行。 diff --git a/tasks/reviews/R2.13-r1.md b/tasks/reviews/R2.13-r1.md new file mode 100644 index 0000000..c84a2fe --- /dev/null +++ b/tasks/reviews/R2.13-r1.md @@ -0,0 +1,7 @@ +FAIL + +apps/ely-mark/web/src/preserve.js:57 — 表格豁免会放行无法无损写回的未知语法,违反 PRD §14.3 的原文保留要求。复现:以可视模式打开 `|a|[[Wiki Link]]|\n|-|-|\n|1|2|\n`,在首格 `a` 前键入 `x` 后保存,第二格变成 `\[\[Wiki Link]]`,Wiki Link 语法被转义。同一原文经过两次解析所得节点相等,仍会包含序列化时改写的未知语法。真实 CEF 与原生键入、保存均复现;原生字节断言失败,见 `/tmp/R2.13-r1-native-wiki.log`,CEF 输出见 `/tmp/R2.13-r1-native-probe.log`。修复方向:将放行范围限定为表格空白和分隔行差异,保留未知语法的原文块保护,并补充相邻单元格编辑后的保存回归断言。 + +apps/ely-mark/web/src/preserve.js:57 — 单独解析表格片段会丢失整篇文档的引用定义上下文,使合法的紧凑 GFM 表格继续显示为原文块,未满足 R2.13 和 MARK-03。复现:打开 `|a|[link][r]|\n|-|-|\n|1|2|\n\n[r]: https://example.com\n`;整篇解析得到链接节点,片段解析得到引用文字,节点比较失败。真实 CEF 显示零个表格和两个原文块,页面仍报告 `visual`、`fallback:false`;内联链接对照用例正常显示表格。证据:`/tmp/R2.13-r1-native-probe.log`、`/tmp/R2.13-r1-browser-more.log`。修复方向:使用完整文档的引用定义上下文核验表格,并加入引用链接表格的实际渲染断言。 + +apps/ely-mark/web/src/preserve.js:57 — 表格豁免只检查顶层节点类型,合法容器内的紧凑表格仍因序列化空白差异整体变成原文块,未满足 R2.13 和 MARK-03。复现:打开 `> |alpha|beta|\n> |---|---|\n> |1|2|\n`,真实 CEF 显示整个引用块的 Markdown 原文,表格数为零;列表中的 `- |alpha|beta|\n |---|---|\n |1|2|\n` 同样复现。顶层节点分别为 `blockquote` 和 `bullet_list`,表格判断无法覆盖其中的表格。证据:`/tmp/R2.13-r1-native-probe.log`、`/tmp/R2.13-r1-browser-more.log`。修复方向:对受支持容器内的表格核验空白差异,保留容器内其他结构的原文保护,并补充引用与列表内紧凑表格的渲染回归断言。 diff --git a/tasks/reviews/R2.3-r1.md b/tasks/reviews/R2.3-r1.md new file mode 100644 index 0000000..9ac6f26 --- /dev/null +++ b/tasks/reviews/R2.3-r1.md @@ -0,0 +1,7 @@ +FAIL + +site/video/scenes/turn.ts:22:离场场景把 ElyTree 屏幕的 y 强制设为 7.9,群像渲染器 `site/video/montage/scene.ts:25` 使用当前屏幕的 y=8.22,导致带着录屏的屏幕在切场强拍上瞬移。独立重渲染群像和 files 场景后,第 2185→2186 帧的 ElyTree 录屏左上角从 (989.087, 450.748) 跳到 (972.900, 441.395),单帧位移 18.695 像素;其余五块屏幕位移均小于 0.009 像素。逐帧提取交付的 `site/public/film/ely-office.mp4` 同样复现该跳移。修复方向:让群像和离场使用一致的屏幕几何,重新导出成片并核对交界帧。 + +site/video/type/scenes.js:132、site/video/scenes/page.ts:181:两个 R2.3 回归点缺少自动化验收断言。独立执行类型检查、官网构建、13 项单测及 `e2e/film.spec.ts` 的 2 项测试均通过;随后在隔离副本中把退场时序恢复为 `(13 + out[i])`,并把录屏条件改成 `land > 2`,相同的 15 项测试仍全部通过。变异后的 ElyDoc 字体素一直可见到第 577 帧,展平从第 563 帧开始,重叠 15 帧;files 第 2186 帧的录屏 image 数量为 0,正常版本为 6。修复方向:加入直接驱动视频渲染器的回归测试,断言六个应用名在展平首帧前消失,以及六块离场屏幕持续加载对应时间的录屏,并确认这两种变异触发失败。 + +site/video/scenes/page.ts:170:新增注释 `Draws the moment; slabs carry the montage recordings at frame f.` 按空白分词共 11 词,违反 agents.md 的单行十词限制。读取本提交该行即可复现。修复方向:将该注释缩短到十词以内。