From b5bfb77ba057be10cfe4e4c06db878731c864f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=B7=E7=94=B5=E8=8A=BD=E8=A1=A3?= Date: Tue, 13 Oct 2026 23:37:47 -0400 Subject: [PATCH] R5.1: in a table, ElyDoc's panel shows the Table group first (component 9ff403d keys folds by title); three rounds, R3 PASS Co-Authored-By: Claude Opus 5.5 (1M context) --- Cargo.lock | 24 +++++----- Cargo.toml | 2 +- agents.md | 2 +- apps/ely-doc/src/props.rs | 14 +++--- apps/ely-doc/src/props/groups.rs | 9 +++- apps/ely-doc/src/props/table.rs | 14 +++--- apps/ely-doc/src/props/tests.rs | 80 ++++++++++++++++++++++++++++++++ scripts/fetch-references.sh | 2 +- tasks/TASKS.md | 7 +++ tasks/reviews/R5.1-r1.md | 3 ++ tasks/reviews/R5.1-r2.md | 3 ++ tasks/reviews/R5.1-r3.md | 1 + 12 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 tasks/reviews/R5.1-r1.md create mode 100644 tasks/reviews/R5.1-r2.md create mode 100644 tasks/reviews/R5.1-r3.md diff --git a/Cargo.lock b/Cargo.lock index 6c1a9c9..5c7c11c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,7 +275,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -286,7 +286,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1751,7 +1751,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.3", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -1923,7 +1923,7 @@ dependencies = [ [[package]] name = "ely-gpui-component" version = "0.1.1" -source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=8fd3e431db3c57c67328869cffdcd43d5f7fd056#8fd3e431db3c57c67328869cffdcd43d5f7fd056" +source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=9ff403df31894b07904cfd197a074d1ff95e6de1#9ff403df31894b07904cfd197a074d1ff95e6de1" dependencies = [ "alacritty_terminal", "anyhow", @@ -2334,7 +2334,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -4501,7 +4501,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "536bfad37a309d62069485248eeaba1e8d9853aaf951caaeaed0585a95346f08" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -4666,7 +4666,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -6418,7 +6418,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7006,7 +7006,7 @@ dependencies = [ "cfg-if", "libc", "psm", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -7346,7 +7346,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] @@ -7696,7 +7696,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", - "windows-sys 0.61.2", + "windows-sys 0.60.2", ] [[package]] @@ -8495,7 +8495,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 36aab30..c189685 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 = "8fd3e431db3c57c67328869cffdcd43d5f7fd056" } +ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "9ff403df31894b07904cfd197a074d1ff95e6de1" } 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 c04c986..07f15b6 100644 --- a/agents.md +++ b/agents.md @@ -40,7 +40,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木 ## 组件映射 -依赖 `ely-gpui-component`(pin 8fd3e43,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 9ff403d,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。 | 需要 | 用 | | --- | --- | diff --git a/apps/ely-doc/src/props.rs b/apps/ely-doc/src/props.rs index 48ccfc6..fc73b7a 100644 --- a/apps/ely-doc/src/props.rs +++ b/apps/ely-doc/src/props.rs @@ -250,15 +250,15 @@ impl Render for Props { let grid = if graphic { grid.group(self.object_group(cx)) } else { - let grid = grid - .group(self.text_group(window, cx)) - .group(self.paragraph_group(cx)) - .group(self.style_group(cx)) - .group(self.page_group(cx)); - match self.state(".uno:DeleteTable", cx).as_deref() { + // In a table, its group leads, as in Word. + let grid = match self.state(".uno:DeleteTable", cx).as_deref() { Some("enabled") => grid.group(self.table_group(cx)), _ => grid, - } + }; + grid.group(self.text_group(window, cx)) + .group(self.paragraph_group(cx)) + .group(self.style_group(cx)) + .group(self.page_group(cx)) }; div() .size_full() diff --git a/apps/ely-doc/src/props/groups.rs b/apps/ely-doc/src/props/groups.rs index 046886d..c970414 100644 --- a/apps/ely-doc/src/props/groups.rs +++ b/apps/ely-doc/src/props/groups.rs @@ -2,7 +2,7 @@ use ely_gpui_component::{data_display::PropertyGroup, primitives::IconName}; use ely_office::panel::{Panel, color_state, font_box, palette, row, size_menu, toggle}; -use gpui::{App, Context, SharedString, Window}; +use gpui::{App, Context, InteractiveElement, ParentElement, SharedString, Window, div}; use super::Props; use crate::docs::Docs; @@ -77,7 +77,12 @@ impl Props { PropertyGroup::new(tip("doc.props.text")) .row(tip("doc.props.font"), font) .row(tip("doc.props.size"), size_menu(self, "doc-props-size", cx)) - .row(tip("doc.props.effects"), effects) + .row( + tip("doc.props.effects"), + div() + .debug_selector(|| "doc-props-text".into()) + .child(effects), + ) .row( tip("doc.props.color"), palette(&this, "format.color", (color, AUTOMATIC), None, cx), diff --git a/apps/ely-doc/src/props/table.rs b/apps/ely-doc/src/props/table.rs index b6c49f6..45b0e8f 100644 --- a/apps/ely-doc/src/props/table.rs +++ b/apps/ely-doc/src/props/table.rs @@ -2,7 +2,7 @@ use ely_gpui_component::{buttons::Button, data_display::PropertyGroup, theme::ControlSize}; use ely_office::panel::{button, color_state, palette, row, send, toggle}; -use gpui::{AnyElement, Context, IntoElement, SharedString}; +use gpui::{AnyElement, Context, InteractiveElement, IntoElement, ParentElement, SharedString, div}; use super::Props; use crate::docs::Docs; @@ -14,11 +14,13 @@ impl Props { PropertyGroup::new(tip("doc.props.table")) .row( tip("doc.props.rows"), - row([ - button(self, "table.row_above", "doc.props.above_row", why, cx), - button(self, "table.row_below", "doc.props.below_row", why, cx), - button(self, "table.delete_rows", "doc.props.delete", why, cx), - ]), + div() + .debug_selector(|| "doc-props-table".into()) + .child(row([ + button(self, "table.row_above", "doc.props.above_row", why, cx), + button(self, "table.row_below", "doc.props.below_row", why, cx), + button(self, "table.delete_rows", "doc.props.delete", why, cx), + ])), ) .row( tip("doc.props.columns"), diff --git a/apps/ely-doc/src/props/tests.rs b/apps/ely-doc/src/props/tests.rs index 35a62c2..df014d6 100644 --- a/apps/ely-doc/src/props/tests.rs +++ b/apps/ely-doc/src/props/tests.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, rc::Rc}; +use ely_gpui_component::theme::Theme; use ely_lok::LokError; use ely_office::panel::Panel as _; use gpui::{Entity, TestAppContext}; @@ -296,3 +297,82 @@ fn quick_spacing_edits_keep_each_other() { "both quick edits land" ); } + +/// A window root that shows the panel. +struct Shown(Entity); + +impl gpui::Render for Shown { + fn render( + &mut self, + _: &mut gpui::Window, + _: &mut gpui::Context, + ) -> impl gpui::IntoElement { + use gpui::{ParentElement as _, Styled as _}; + gpui::div().size_full().child(self.0.clone()) + } +} + +/// The top of the element marked `selector`. +fn top(cx: &mut gpui::VisualTestContext, selector: &'static str) -> Option { + // Past reduced motion's 1ms folds. + for _ in 0..3 { + std::thread::sleep(std::time::Duration::from_millis(2)); + cx.update(|window, _| window.refresh()); + cx.run_until_parked(); + } + cx.debug_bounds(selector).map(|bounds| bounds.origin.y) +} + +#[test] +fn in_a_table_the_table_group_comes_first_and_folds_stay_put() { + const DOWN: i32 = 1024; + const UP: i32 = 1025; + let dir = tempfile::tempdir().expect("a temp dir"); + let mut cx = TestAppContext::single(); + let Some(docs) = docs(&mut cx, dir.path()) else { + eprintln!("SKIP: no LOK build"); + return; + }; + cx.update(|cx| { + ely_gpui_component::init_for_tests(cx); + Theme::update(cx, |theme| theme.reduced_motion = true); + }); + blank(&mut cx, &docs); + type_into(&mut cx, &docs, "Table"); + let props = panel(&mut cx, &docs); + let window = cx.add_window(|_, _| Shown(props.clone())); + let mut shown = gpui::VisualTestContext::from_window(window.into(), &cx); + assert_eq!( + top(&mut shown, "doc-props-table"), + None, + "no table, no group" + ); + let header = gpui::point(gpui::px(10.0), gpui::px(12.0)); + shown.simulate_click(header, gpui::Modifiers::none()); + assert_eq!(top(&mut shown, "doc-props-text"), None, "Text folds"); + shown.simulate_click(header, gpui::Modifiers::none()); + assert!(top(&mut shown, "doc-props-text").is_some(), "Text opens"); + with_window(&mut cx, &docs, |docs, _, cx| { + docs.insert_table(3.0, 2.0, cx) + }); + let in_table = |app: &gpui::App| props.read(app).state(".uno:DeleteTable", app); + settle(&cx, |app| in_table(app).as_deref() == Some("enabled")); + let table = top(&mut shown, "doc-props-table").expect("the table group"); + let text = top(&mut shown, "doc-props-text").expect("the text group"); + assert!(table < text, "the table group leads: {table:?} vs {text:?}"); + keys(&mut cx, &docs, &[UP]); + settle(&cx, |app| in_table(app).as_deref() != Some("enabled")); + shown.simulate_click(header, gpui::Modifiers::none()); + assert_eq!(top(&mut shown, "doc-props-text"), None, "Text folds again"); + keys(&mut cx, &docs, &[DOWN]); + settle(&cx, |app| in_table(app).as_deref() == Some("enabled")); + assert!(top(&mut shown, "doc-props-table").is_some(), "Table opens"); + assert_eq!(top(&mut shown, "doc-props-text"), None, "Text stays folded"); + keys(&mut cx, &docs, &[UP]); + settle(&cx, |app| in_table(app).as_deref() != Some("enabled")); + assert_eq!( + top(&mut shown, "doc-props-text"), + None, + "and after the table" + ); +} diff --git a/scripts/fetch-references.sh b/scripts/fetch-references.sh index 4d68036..be091b9 100755 --- a/scripts/fetch-references.sh +++ b/scripts/fetch-references.sh @@ -36,7 +36,7 @@ fetch() { # name url rev [sparse paths...] echo "fetch $name $rev" } -fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git 8fd3e431db3c57c67328869cffdcd43d5f7fd056 +fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git 9ff403df31894b07904cfd197a074d1ff95e6de1 fetch zed https://github.com/zed-industries/zed.git 1a28cff4b409169bac058bca40dfbfeb7621d19b fetch libreoffice-core https://github.com/LibreOffice/core.git 8221e31b3ac356a1623c672912a3d2b492f7e3d1 \ include/LibreOfficeKit libreofficekit desktop/source/lib desktop/inc \ diff --git a/tasks/TASKS.md b/tasks/TASKS.md index 391e1bf..625ca26 100644 --- a/tasks/TASKS.md +++ b/tasks/TASKS.md @@ -243,3 +243,10 @@ - [ ] R4.9 ElyMark:“段落”“格式”菜单与 Typora 同项同键(Ctrl+1–6、Ctrl+0、Ctrl+=/-、Ctrl+B/I/U、Ctrl+Shift+`、Alt+Shift+5、Ctrl+K、Ctrl+Shift+I、Ctrl+\、Ctrl+T、Ctrl+Shift+K/M/Q/[/]);源码模式 Ctrl+/;字号 Ctrl+Shift+=/-/0;专注 F8、打字机 F9;表格菜单加移动行列;复制为 Markdown Ctrl+Shift+C、粘贴为纯文本 Ctrl+Shift+V - [ ] R4.10 ElyMark:右键菜单照 Typora 按目标变化:剪贴板行(剪切、复制、粘贴、删除)、复制/粘贴为 ▸(Markdown、HTML 代码、纯文本、粘贴为纯文本)、格式行、段落 ▸ 或表格 ▸ 或代码项、插入 ▸、链接(打开、复制地址)、图片(打开所在位置、删除图片)、删除块;预览只读时只留复制类 - [ ] R4.11 验收:同一份样本在 ElyMark 与 Typora 的真窗口截图逐项对照;帮助、PRD、`agents.md` 随之;逐条核对表 + +## R5 ElyDoc 照 Word 的右键与表格(用户 2026-10-13 报:右键没反应;表格不能加行列、不能调大小;三项方案经用户确认) + +- [x] R5.1 属性栏:光标在表格里时表格组排在最前(同 Word 的表格布局页签),否则次序不变(DOC-07) — R3 PASS(组件 9ff403d:属性组按标题记折叠) +- [ ] R5.2 右键菜单(DOC-02、07、08、11、12):原先只在内核菜单带拼写建议时才显示,其余右键无反应;改为照 Word 按目标出 Ely 的菜单,开在所点之处:剪切、复制、粘贴为图标行;正文为链接…、新建批注;表格单元格另有插入 ▸(左侧列、右侧列、上方行、下方行)、删除 ▸(行、列、表格)、合并单元格、拆分单元格 ▸(两列、两行)、平均分布各行与各列;图片为文字环绕 ▸、置于顶层 ▸、置于底层 ▸、插入题注…、新建批注;拼写错词时建议与“全部忽略”在最上。目标与各项可否取自内核这次给的菜单,命令只经 `COMMANDS`,文案进中英目录 +- [ ] R5.3 拖动表格边线(DOC-07):读内核的 `LOK_CALLBACK_TABLE_SELECTED`(光标所在表格的列与行边线),指针在边线上变为调整指针,拖动时画参考线,松开以 `.uno:TableChangeCurrentBorderPosition` 改列宽或行高(左、内、右列边线;内、下行边线),一步撤销,按读到边线时的修订执行;内核在 LOK 下有意不处理边线的指针与拖动(`SwEditWin::changeMousePointer`、`RulerColumnDrag` 依赖 LOK 没有的标尺);未进入的表格先单击一次 +- [ ] R5.4 验收:Windows 真窗口逐项核对(右键四种目标、加行加列、拖动列宽与行高、撤销、保存后重开);`agents.md` 随之;逐条核对表 diff --git a/tasks/reviews/R5.1-r1.md b/tasks/reviews/R5.1-r1.md new file mode 100644 index 0000000..ac1372d --- /dev/null +++ b/tasks/reviews/R5.1-r1.md @@ -0,0 +1,3 @@ +FAIL + +- apps/ely-doc/src/props.rs:254 — Prepending Table transfers collapsed states between unrelated groups. The pinned `PropertyGrid` stores each fold under its numeric position (`fold-{ix}`). Collapse Text outside a table, then insert a table: Table inherits the collapsed state, hiding its controls, while Text unexpectedly reopens. A Windows GPUI/LOK probe reproduced `table=None, text=Some(115px)` with this diff; restoring only the previous ordering made the same probe pass with `table=Some(690px), text=None`. Give groups stable state identities through the shared component and cover entering/leaving a table with previously collapsed groups. The existing 10 property-panel tests pass but miss this transition. Evidence: `.work/R5.1-r1-review/fold-regression.log` and `fold-baseline.log`. diff --git a/tasks/reviews/R5.1-r2.md b/tasks/reviews/R5.1-r2.md new file mode 100644 index 0000000..8f8d3c0 --- /dev/null +++ b/tasks/reviews/R5.1-r2.md @@ -0,0 +1,3 @@ +FAIL + +- reference/Ely-GPUI-Components/src/data_display/records.rs:106 — The added `PropertyGroup` documentation violates the explicit one-line, ten-word comment rule. At b3d6cc0, it extends the preceding comment to two lines and adds eleven words: “Its title keys its fold, so titles differ within a grid.” Condense the documentation into one line of at most ten words. Evidence: `.work/R5.1-r2-review/new-comment-check.log`. diff --git a/tasks/reviews/R5.1-r3.md b/tasks/reviews/R5.1-r3.md new file mode 100644 index 0000000..0cdd613 --- /dev/null +++ b/tasks/reviews/R5.1-r3.md @@ -0,0 +1 @@ +PASS