R4.13: round 1 fixes; app language leads Han layout

- ely-ui passes its resolved locale to Windows text layout, so
  Chinese UI lays out as Chinese whatever the Windows region.
- Component a4bf696: Find box, sort and filter builders and
  password tooltips read the catalog; stricter shipped-copy scan.
- ElyMark deletes the right-clicked picture (delete_image(at)).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-14 20:52:17 -04:00
1 parent b714024d2b
commit 45ac9c6ec4
12 files changed
+81 -20

No files matched your search

Generated
+3 -1
View File
@@ -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=6df9ab0aa6cba99e6a79da80f79fc166f09890a8#6df9ab0aa6cba99e6a79da80f79fc166f09890a8"
source = "git+https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components?rev=a4bf6965f9dd4802ca37cb3da5d338b360a169d9#a4bf6965f9dd4802ca37cb3da5d338b360a169d9"
dependencies = [
"alacritty_terminal",
"anyhow",
@@ -2187,6 +2187,7 @@ dependencies = [
"futures",
"gpui-pre",
"gpui-pre-platform",
"gpui-pre-windows",
"log",
"notify",
"semver",
@@ -2981,6 +2982,7 @@ dependencies = [
"waker-fn",
"web-time",
"windows 0.62.2",
"zed-font-kit",
"zed-scap",
]
+1 -1
View File
@@ -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 = "6df9ab0aa6cba99e6a79da80f79fc166f09890a8" }
ely-gpui-component = { git = "https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components", rev = "a4bf6965f9dd4802ca37cb3da5d338b360a169d9" }
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"] }
+4 -4
View File
@@ -40,7 +40,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木
## 组件映射
依赖 `ely-gpui-component`(pin 6df9ab0,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 a4bf696,gpui 为 crates.io 的 `gpui-pre` =0.3.8)。名字取自 `reference/Ely-GPUI-Components/src`。gpui 用法规则见该仓库 `AGENTS.md`。`tests/scripts/agents_md.py` 核对下表每个名字都是组件库公开项。
| 需要 | 用 |
| --- | --- |
@@ -84,7 +84,7 @@ site/ 官网(Vite 8、pnpm、TypeScript,无框架;等轴 SVG 积木
| `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 |
| 组件内部文案未接 i18n(Ely 用到的组件已接:提示、默认占位、补全空行、字号示例,6df9ab0,`i18n/tests/shipped.rs` 防回退;其余组件之外:欢迎页标题、菜单项、`SortBuilder` 与 `FilterBuilder` 等) | 面板族文案已接 i18n(7380afb,内建 en/zh-CN),`CommentSidebar` 与 `RelativeTime` 同(408f2b0);其余上游待办 |
| 组件内部文案未接 i18n(Ely 用到的组件已接:提示、默认占位、补全空行、字号示例,6df9ab0;查找框、排序与筛选、密码开关,a4bf696;`i18n/tests/shipped.rs` 防回退;其余组件之外:欢迎页标题、菜单项等) | 面板族文案已接 i18n(7380afb,内建 en/zh-CN),`CommentSidebar` 与 `RelativeTime` 同(408f2b0);其余上游待办 |
| LOK 的 `.uno:StyleApply` 状态给界面名(`Default Paragraph Style`),样式列表给程序名(`Standard`),两者对不上 | ElyDoc 属性栏:列表外的当前样式显示为占位文字;更新样式用状态里的界面名。上游待办:状态带程序名 |
## gpui 规则
@@ -197,7 +197,7 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-04:ElyDoc 的标签、会话、打开、保存、另存为(按文件身份比对)、关闭询问、冲突、密码与标签条移入 `crates/ely-office`:应用实现 `Host`(格式表 `Kind`、每标签附加数据、内核回报钩子),共用逻辑以扩展 trait 提供(`use ely_office::prelude::*`)。ElyTable 用同一外壳。共用文案在 `office.*`。
- 2026-10-04:ElyTable(C09.1)。新建为空白 XLSX(首个工作表名随界面语言),保存保留 xlsx/ods/xls/fods,另存为 XLSX(默认)或 ODS,xlsm/xltm 进风险模式。上方名称框与公式栏(回车分别发 `.uno:GoToCell` 与 `.uno:EnterString`,Esc 交回网格),中间行列表头按内核几何自绘(点选整行整列,拖边改尺寸),下方工作表标签(右键与“工作表”菜单:插入、重命名、复制、左右移、隐藏、显示、删除;删除前列出引用该表的公式数与命名区域数)与状态栏(保存状态、内核给的选区统计)。“单元格”菜单:插删行列与单元格、合并拆分(多个数据格时先问移入或保留隐藏)、行高列宽(厘米)与最适、隐藏显示行列、冻结。填充柄拖到目标格发 `.uno:AutoFill`。选区统计文字由内核给出,当前为英文(内核界面语言未随 Ely 切换),待处理。
- 2026-10-04:ElyTable 公式、计算、名称(C09.2,TABLE-03 至 06)。公式语法为 Excel A1(用户定):`Sheet2!A1`、逗号分隔参数,由安装层 `packaging/libreoffice/ely.xcd` 设定;载入选项 `Language=en-US,DeviceFormFactor=desktop`,内核界面与数字区域都是 en-US(小数点、月/日/年输入)。单元格内编辑由内核完成:键入时内核给出匹配的函数(`editor::CompletionMenu` 显示在单元格下,上下键选,Tab 补全,经 `completeFunction` 入有序队列),参数提示(`editor::SignatureHelp`,取内核的用法文字),点选单元格插入引用并按内核颜色画框;内核只为点选的引用与再次编辑(F2)的已有公式画框,新键入的引用不画(同桌面版)。第一下 Esc 关提示,第二下取消(同内核)。语法错误的公式照原样输入,状态栏按界面语言解释当前格的错误(内核英文错误串换成界面文案,同统计);循环引用同此提示。“公式”菜单:自动计算开关(关时状态栏显示手动计算)、重新计算、全部重新计算、定义名称(名称、引用位置默认为所选区域的绝对引用、范围为工作簿或某表;名称先经内核判定)、名称列表(改引用、转到所命名的绝对区域、删除,失效引用标出;内核给出每个名称的区域,`.uno:SelectedRange` 给所选区域)、追踪错误、追踪引用与从属单元格、移去箭头。名称框可直接输入名称跳转,跳转后显示该名称。手动计算在 ods、xlsx、xls、fods 存后重开都保持。
- 2026-10-04:ElyTable 格式、数据整理、规则(C09.3,TABLE-07 至 09)。补丁 `0016-lok-calc-formats-and-conditions.patch`(数字格式、条件格式、两处 sdi 参数)、`0017-lok-calc-filter-validation-columns.patch`(筛选、验证、分列)、`0018-lok-calc-reads-requests-search.patch`(读值、请求、查找全部):数字格式按代码设(`.uno:NumberFormat` 的 `FormatCode`),`.uno:NumberFormatCode` 读当前格代码与类别(常规按区域块首判定)或校验代码;标准筛选收 JSON 条件(`.uno:DataFilterStandardFilter` 的 `Query`,含按列取值),`.uno:AutoFilterValues` 照内核自己的筛选菜单给出条目、他列隐藏与本列勾选;数据验证收 JSON 规则(`.uno:Validation` 的 `Rule`),`.uno:Validation` 读当前格规则,`.uno:DataSelectValues` 给序列;条件格式收 JSON(单元格规则配内核单元格样式、色阶、数据条、按键删除,`cellsh.sdi` 的同名槽也加参数),`.uno:ConditionalFormats` 列出本表;分列收 `Options`(ScAsciiOptions 串);`.uno:SelectedRange` 另给所选区域内数据区的四角、首行文字与内核的标题判定。LOK 下内核不再弹自己的筛选菜单与序列下拉,改发 `LOK_CALLBACK_TOOLTIP`(`autofilter`、`datalist`,带列行),无效输入发 `validityerror`(标题、内容、样式、是否接受;接受与否取内核对话框的默认:信息接受,警告与停止拒绝),Ely 以 `ely_lok::Request` 接收后自建菜单与提示。查找全部按格报矩形(内核原把相邻命中并成一块,计数偏少)。查找替换移入 `ely-office`(`Finding`,ElyDoc 与 ElyTable 共用,`Host::search_leaves` 供 ElyDoc 记返回位置)。“粘贴为值/公式”只在系统剪贴板仍是本视图上次的复制时进行(内核自己的单元格剪贴板;Ely 复制的 HTML 末尾带一个进程内唯一的标记注释,以标记而不是文字识别,同样文字的外来复制不算);其他内容(含别的工作簿复制的单元格)明确拒绝并提示改用粘贴:内核无法从外来内容单取值或公式。排序最多三个关键字(内核限制)。右侧格式面板(数字、对齐、边框、填充)、“数据”菜单(排序、筛选、删除重复项、分列、查找替换、粘贴为值或公式、数据验证)、“格式”菜单(格式面板开关、条件格式)。组件库 `SortBuilder`、`FilterBuilder` 的文案未接 i18n,中文界面下仍为英文(上游待办)。
- 2026-10-04:ElyTable 格式、数据整理、规则(C09.3,TABLE-07 至 09)。补丁 `0016-lok-calc-formats-and-conditions.patch`(数字格式、条件格式、两处 sdi 参数)、`0017-lok-calc-filter-validation-columns.patch`(筛选、验证、分列)、`0018-lok-calc-reads-requests-search.patch`(读值、请求、查找全部):数字格式按代码设(`.uno:NumberFormat` 的 `FormatCode`),`.uno:NumberFormatCode` 读当前格代码与类别(常规按区域块首判定)或校验代码;标准筛选收 JSON 条件(`.uno:DataFilterStandardFilter` 的 `Query`,含按列取值),`.uno:AutoFilterValues` 照内核自己的筛选菜单给出条目、他列隐藏与本列勾选;数据验证收 JSON 规则(`.uno:Validation` 的 `Rule`),`.uno:Validation` 读当前格规则,`.uno:DataSelectValues` 给序列;条件格式收 JSON(单元格规则配内核单元格样式、色阶、数据条、按键删除,`cellsh.sdi` 的同名槽也加参数),`.uno:ConditionalFormats` 列出本表;分列收 `Options`(ScAsciiOptions 串);`.uno:SelectedRange` 另给所选区域内数据区的四角、首行文字与内核的标题判定。LOK 下内核不再弹自己的筛选菜单与序列下拉,改发 `LOK_CALLBACK_TOOLTIP`(`autofilter`、`datalist`,带列行),无效输入发 `validityerror`(标题、内容、样式、是否接受;接受与否取内核对话框的默认:信息接受,警告与停止拒绝),Ely 以 `ely_lok::Request` 接收后自建菜单与提示。查找全部按格报矩形(内核原把相邻命中并成一块,计数偏少)。查找替换移入 `ely-office`(`Finding`,ElyDoc 与 ElyTable 共用,`Host::search_leaves` 供 ElyDoc 记返回位置)。“粘贴为值/公式”只在系统剪贴板仍是本视图上次的复制时进行(内核自己的单元格剪贴板;Ely 复制的 HTML 末尾带一个进程内唯一的标记注释,以标记而不是文字识别,同样文字的外来复制不算);其他内容(含别的工作簿复制的单元格)明确拒绝并提示改用粘贴:内核无法从外来内容单取值或公式。排序最多三个关键字(内核限制)。右侧格式面板(数字、对齐、边框、填充)、“数据”菜单(排序、筛选、删除重复项、分列、查找替换、粘贴为值或公式、数据验证)、“格式”菜单(格式面板开关、条件格式)。组件库 `SortBuilder`、`FilterBuilder` 的文案 a4bf696 起接 i18n(原为英文)。
- 2026-10-04:右侧属性栏宽取 `theme.sheet_size()`(380),左栏仍是 `sidebar_width`。`PropertyGrid` 名称列固定 120,240 宽时编辑器只剩约 76。颜色经色块弹出 `ColorPalette`,一次点击一条命令。
- 2026-10-01:MailCore 2 的子模块 libetpan 过旧,改钉 libetpan master `4d47862`。
- 2026-10-01:每个应用的全部数据在 `<本机数据目录>/ElyOffice/<slug>/`,路径由 `ely_platform::data_dir` 给出。卸载只动这里,且先问。用户文档永不碰。
@@ -294,5 +294,5 @@ cargo run -p ely-doc 启动一个应用(ElyDoc 先 cargo build -p ely
- 2026-10-14(Gitea #1,Linux Wayland 报):ElyShow 左侧缩略图条滚轮不滚。根因:列表包在 `ContextMenu` 宿主里,宿主无尺寸,列表随内容长高、从不溢出,滚轮无处可滚(右栏正常);宿主改为 `size_full`(同 ElyPDF 的三处),`tests/strip.rs` 以 24 张幻灯片核对滚轮滚到最后一张。各应用其余 `ContextMenu` 宿主已核:ElyPDF 三处本有尺寸,ElyTable 的工作表标签条与下拉选项不是滚动列表。
- 2026-10-14:ElyMark 的菜单与快捷键(R4.9,MARK-15)。项与键取自 Typora 1.14.10 主进程字节码的字符串表(`.work/typora-main-menus.txt`)与其 `zh-Hans.lproj/Menu.json`;实际大小在字节码里有 Ctrl+Shift+9 与 Ctrl+Shift+0 两串,照用户所定用 Ctrl+Shift+0,R4.11 在真窗口核对。表格是“段落”的子菜单(照 Typora),顶层不再有“表格”。原 Ctrl+1/2/3 切模式、Ctrl+=/-/0 字号、Ctrl+Shift+F 专注让给 Typora 的键;可视、源码、预览留在“视图”菜单,无键。Ctrl+/ 另绑在 `ElyEditor` 上(gpui 同深度后绑者胜),盖过代码编辑器的 `//` 注释切换(Markdown 没有这种注释);`MarkdownDocument` 的键上下文只在可视模式有。带 Shift 的标点:Windows 写键名(`ctrl-shift-[`),macOS 与 Linux 写字符(`cmd-{`),见 `blocks::shifted`。Ctrl+Enter 在打开的块里原是输入框的 Submit(文档不处理),改绑为下方插入行。拒绝一律成信息提示:当场的由 `Desk::edit` 说,后台读出的经 `DocumentEvent::EditRefused`、`view::Refused` 到宿主。菜单打开时,打开的块把焦点借给菜单,块与选区都留着,菜单命令照样作用于所选(组件 6d90351)。复制为 Markdown 与粘贴为纯文本只作用于文档:焦点在文档外的输入框(查找框、对话框)时交给该框自己的复制、粘贴。表格子菜单末项为删除表格(组件 `TableEdit::Delete`,一步撤销,插入点不在表格里即拒绝;在打开的块里删除后焦点回到文档,撤销键照用,组件 6c2d28c)。Typora 有而组件库暂缺的不进菜单:行内公式、上下标、注释、提示块子菜单、任务状态、列表缩进 Ctrl+[/]、代码工具。
- 2026-10-14:ElyMark 的右键菜单(R4.10)。项、次序与显隐规则取自 Typora 1.14.10 的 `resources/window.html`(`#context-menu` 与其子菜单)与 `frame.js` 的显隐函数,记在 `.work/R4.10/typora-context-menu.txt`。菜单在 `view/menu.rs`,`MarkView` 自持(`ely_office::menu::Offer` 与 `ContextMenu::manual`):可视模式由组件的 `DocumentEvent::Pointed` 开在指针处,源码与预览由视图自己的右键开。剪切、复制、粘贴、删除所选范围为图标行,按下即把其键交回菜单打开前的焦点(`dispatch_keystroke`),与键盘同一路径;无选区时剪切、复制、删除灰显。其余项经各自的动作,显示所绑的键。组件 013f142、1b5fb07:文本框与代码编辑器右键落在选区里时保留选区,否则把插入点移到所点处;可视模式打开的块右键不写回、不关闭,目标在后台按全文读出(块外的引用定义照样解析);`MarkdownDocument::delete_image`(删除图片,一步撤销,无图片即 `EditRefusal::NoImage`);可视无块时 Ctrl+V 粘贴文字(原只收图片,文字被丢弃)。复制为 HTML 代码的图片照原地址(`Pictures::Written`,同 Typora),复制为富文本照旧内嵌。“编辑”菜单的复制项照 Typora 次序:纯文本、Markdown、HTML 代码、富文本。Typora 有而不做的:缩进与减少缩进行(可视只在打开的块里有)、复制表格、代码自动缩进、图片上传、缩放、移动与语法切换、拼写、表情复制、检查元素。gpui 的测试只发按下,菜单在 Enter 松开时选中,测试另发松开(`blocks/context.rs` 的 `choose`)。
- 2026-10-14(用户报:整个软件中文显示问题很多):中文显示(R4.13)。Windows 上 gpui 以用户区域(本机为 en-US)排版全部文字,DirectWrite 按日文为汉字选回退字体,日文字体缺的简体字再落到另一字体,同一个词里字形与粗细不一(ElyTree 的“打开仓库”:“打开”粗、“仓库”细)。`vendor/gpui-pre-windows` 的 `text_locale`:用户区域不是中日韩时,改用 Windows 语言列表(WinRT `GlobalizationPreferences::Languages`,Win32 的界面语言只给 en-US)里第一个中日韩语言排版(日志 `text: laid out as …`);在 Windows 上打中文须把中文加进该列表,所以它就是用户读的汉字。只换排版的区域,字体族名与字体列表仍按用户区域。读不出语言列表即记警告、照用户区域。组件库 6df9ab0:Ely 用到的组件里写死的英文(下拉的“Choose…”、清除、关闭、移除、取消固定的提示,公式补全的“No suggestions”,设置里字号的重置提示与示例句)改走内建中英目录。仍为英文的是内核的文字(LibreOffice 界面语言固定英文:幻灯片占位“Click to add Title”、母版“Default”、配色名、数字格式代码“General”、函数名),以及启动失败对话框(出现在读入语言之前,中英并列)。
- 2026-10-14(用户报:整个软件中文显示问题很多):中文显示(R4.13)。Windows 上 gpui 以用户区域(本机为 en-US)排版全部文字,DirectWrite 按日文为汉字选回退字体,日文字体缺的简体字再落到另一字体,同一个词里字形与粗细不一(ElyTree 的“打开仓库”:“打开”粗、“仓库”细)。`vendor/gpui-pre-windows` 的 `text_locale`:用户区域不是中日韩时,改用 Windows 语言列表(WinRT `GlobalizationPreferences::Languages`,Win32 的界面语言只给 en-US)里第一个中日韩语言排版(日志 `text: laid out as …`);在 Windows 上打中文须把中文加进该列表,所以它就是用户读的汉字。只换排版的区域,字体族名与字体列表仍按用户区域。读不出语言列表即记警告、照用户区域。Ely 的界面语言是中文时(设置或 `ELY_LOCALE`),`ely_ui::i18n::install_as` 经 `gpui_windows::set_text_language` 让排版随它,不论 Windows 区域(日文区域下“打开仓库”原为 Yu Gothic UI 600 与 Microsoft JhengHei UI 700 两种字);语言下次启动生效,故启动时设一次。组件库 6df9ab0:Ely 用到的组件里写死的英文(下拉的“Choose…”、清除、关闭、移除、取消固定的提示,公式补全的“No suggestions”,设置里字号的重置提示与示例句)改走内建中英目录。a4bf696:查找框(计数、三个选项与上下一处的提示、替换与全部替换)、排序与筛选(添加规则、满足全部或任一、条件、升降序、添加排序)、密码框的显示与隐藏提示同此;筛选条件以目录键为值(`Test::key`);ElyMark 的删除图片按右键所点的字节(`delete_image(at)`)。仍为英文的是内核的文字(LibreOffice 界面语言固定英文:幻灯片占位“Click to add Title”、母版“Default”、配色名、数字格式代码“General”、函数名),以及启动失败对话框(出现在读入语言之前,中英并列)。
- 2026-10-14(用户定:dmg 不再显示未公证):macOS 发布以 Developer ID Application 证书签名、经 `notarytool` 公证并钉票。证书与公证凭据(`xcrun notarytool store-credentials`,App 专用密码只在钥匙串里)装在构建用的 Mac 上;发布流程的 macOS 任务从仓库变量 `ELY_SIGN_IDENTITY`(证书全名)与 `ELY_NOTARY_PROFILE`(凭据名)取得,`ci.sh` 在 macOS 上缺任一即失败,不出未公证的包。本机手动出包同样设这两个变量再跑 `packaging/macos/release.sh`。
+1 -3
View File
@@ -46,8 +46,7 @@ actions!(
Hyperlink,
InsertImage,
ClearFormat,
DeleteBlock,
DeleteImage
DeleteBlock
]
);
@@ -230,7 +229,6 @@ impl Desk {
InsertImage => |d, cx| d.format(F::Image, cx),
ClearFormat => |d, cx| d.format(F::Clear, cx),
DeleteBlock => |d, cx| d.block(B::Delete, cx),
DeleteImage => |d, cx| d.delete_image(cx),
)
}
}
+13 -7
View File
@@ -90,16 +90,14 @@ impl MarkView {
true => "mark.image.browser",
false => "mark.image.location",
};
let view = view.clone();
let (shown, dropped, at) = (view.clone(), view.clone(), target.at);
rows =
rows.row(MenuItem::new(said(key, cx)).on_click(move |_, cx| {
view.update(cx, |view, cx| view.show_picture(&url, cx))
shown.update(cx, |view, cx| view.show_picture(&url, cx))
}))
.row(MenuItem::new(said("mark.image.delete", cx)).on_click(move |_, cx| {
dropped.update(cx, |view, cx| view.delete_image(at, cx))
}))
.row(run(
said("mark.image.delete", cx),
Box::new(b::DeleteImage),
window,
))
.line();
}
if let Some(url) = &target.link {
@@ -162,6 +160,14 @@ impl MarkView {
rows.menu()
}
/// Deletes the picture at byte `at`, or says why not.
fn delete_image(&mut self, at: usize, cx: &mut Context<Self>) {
let deleted = (self.document).update(cx, |document, cx| document.delete_image(at, cx));
if let Err(why) = deleted {
cx.emit(super::Refused(why));
}
}
/// Shows a local picture's file, or opens a web one.
fn show_picture(&mut self, url: &str, cx: &mut Context<Self>) {
if web(url) {
+3
View File
@@ -19,6 +19,9 @@ log.workspace = true
notify.workspace = true
serde_json.workspace = true
[target.'cfg(target_os = "windows")'.dependencies]
gpui_windows = { package = "gpui-pre-windows", version = "=0.3.8" }
[features]
test-support = []
+2
View File
@@ -147,6 +147,8 @@ pub fn install(cx: &mut App) {
/// Installs the catalogs speaking `locale` (en-US or zh-CN).
pub fn install_as(locale: &'static str, cx: &mut App) {
#[cfg(target_os = "windows")]
gpui_windows::set_text_language(locale);
let (en, zh) = catalogs();
cx.set_global(
I18n::new(locale)
+1 -1
View File
@@ -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 6df9ab0aa6cba99e6a79da80f79fc166f09890a8
fetch Ely-GPUI-Components https://git.zacharyzhang.com/ZacharyZhang-NY/Ely-GPUI-Components.git a4bf6965f9dd4802ca37cb3da5d338b360a169d9
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 \
+1 -1
View File
@@ -241,7 +241,7 @@
- [~] R4.7 组件库:块命令(标题 1–6、段落、标题升降一级;引用、有序、无序、任务列表的切换;插入分隔线、代码块、公式块、目录、front matter、脚注、链接定义、前后空段落;删除块),一步撤销,拒绝说明原因。组件 58e212d 至 0558f0c(已推送):`MarkdownDocument::block` 照 Typora 的 `changeBlock`、`changeHeadingLevel`、`insertBlock`、`toggleIndent`、`insertParagraph`、`deleteBlock`,与格式共用一个后台执行器与 `EditRefusal`;同级标题保持不变(照 Typora 菜单),代码上的标题命令、任务首行的标题与引用拒绝(GFM 读作文字),前后空段落写成空行,越出打开的块先写回(多一步撤销)。审查三轮未过(`tasks/reviews/R4.7-r1..r3.md`):r1 十三项(标题标记里插入崩溃、任务框丢勾、列表里提示块拆开、拆分时行内标记断开、缩进代码改类型、列表项与脚注的前置段落、删除使邻段相接、尾部 `#` 与块起始符、任务首行、跨列表的选区、同级标题当开关);r2 十二项(拆分后重开标记成块语法、闭合 `#`、标记下一行的任务框、提示块的懒行与空行、标记与正文同行、Tab 缩进代码、任务列表包代码、引用里删除、拆分剩下的块起始符、长注释),r2 起标题与拆分写出前重新解析核对;r3 九项:核对忽略代码里的空白、按 mdast 而非可视读法核对(下划线、表情漏判)、提示块正文块失去缩进、按内容认围栏、非 ASCII 空白算缩进、去掉任务框露出块起始符、续行任务框解除时留下、空标题转段落误拒、转义的提示标记误拒。r3 已修:核对经 `MarkdownReader` 照绘制读(代码与公式里的空白计入),提示块的标记按提示读法判定、正文块保留缩进,围栏按源码结构认,缩进只数 ASCII 空格,去掉任务框时转义露出的起始符(续行同),空标题可转空段落;七项回归在 r3 的源码上失败(`.work/R4.7-fix3/red.md`);轮数用尽,未再审。遗留:列表与引用的切换不重新解析核对,只逐项修正
- [~] R4.8 组件库:表格行列上下左右移动、整理表格源码;代码块取代码内容;与已有的增删行列同一套拒绝。组件 bcee87c 至 6b033f3(已推送):`TableEdit` 加上下移行(表头与首行互换,超出表头列数的行不作表头,到边拒绝为 `TableRefusal::Edge`)、左右移列、整理;表格按显示宽度补齐(`unicode-width`,Tab 记 4、控制字符记 0,照 Typora 的 `wcswidth`),写回表格自身的换行;表格编辑并入后台执行器;右键代码块时 `DocumentTarget` 带出代码文字。审查三轮未过(`tasks/reviews/R4.8-r1..r3.md`):r1 两项(纯 CR 文本里整理表格把前文当作容器前缀、行内 Tab 宽度不合 Typora);r2 一项(End 后插入表格拆开 CRLF);r3 一项(r2 的修法让行辅助函数返回光标之前的位置,End 后的块命令崩溃)。r3 已修:编辑器只按 LF 分行,光标可落在 CR 与 LF 之间;执行器与插入表格在入口把选区移到 CR 前,行辅助函数改为断言不收这种位置;两项回归在 r3 的源码上失败(`.work/R4.8-fix3/red.md`);轮数用尽,未再审。遗留:编辑器自身的 CRLF 模型不变,End 仍落在 CR 之后;表情宽度按 `unicode-width` 记 2 列(Typora 记 1)
- [x] 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。ElyMark 5392062、f84f990、09fcb0b,组件 6d90351、24ffc45、6c2d28c(已推送),pin 6c2d28c:键与菜单项取自 Typora 字节码与其中文菜单;表格为“段落”子菜单,加删除表格;拒绝成提示;菜单借打开的块的焦点,块与选区留着;复制为 Markdown、粘贴为纯文本只作用于文档。R1 FAIL 六项、R2 FAIL 四项已修,R3 PASS
- [ ] R4.13 全部应用:中文显示(用户报,2026-10-14):Windows 上同一词里字形粗细不一(gpui 以 en-US 排版,汉字按日文回退);Ely 用到的组件里写死的英文改走中英目录;先于 R4.10 的修复做
- [ ] R4.13 全部应用:中文显示(用户报,2026-10-14):Windows 上同一词里字形粗细不一(gpui 以 en-US 排版,汉字按日文回退);Ely 用到的组件里写死的英文改走中英目录;先于 R4.10 的修复做。第 1 轮未过(三项:排版语言不随 Ely 的中文设置、查找框英文、排序筛选英文),已修,见 `.work/R4.13-fix1`
- [ ] R4.10 ElyMark:右键菜单照 Typora 按目标变化:剪贴板行(剪切、复制、粘贴、删除)、复制/粘贴为 ▸(Markdown、HTML 代码、纯文本、粘贴为纯文本)、格式行、段落 ▸ 或表格 ▸ 或代码项、插入 ▸、链接(打开、复制地址)、图片(打开所在位置、删除图片)、删除块;预览只读时只留复制类
- [x] R4.12 全部应用:已开着时再打开文件,交给运行中的那份开成新标签并带到前台,不再弹“已在运行”(用户报,2026-10-14);先于 R4.11 做。第 1 轮 4 项:令牌被拒时连接重置使重试失效、慢速来者占住门、主窗口关后仍回执、ElyMail 与 ElyAgent 不到前台;已修,各有回归;第 2 轮通过
- [ ] R4.11 验收:真窗口逐项操作,每项功能可用(不求与 Typora 一一相同);帮助、PRD、`agents.md` 随之;逐条核对表
+7
View File
@@ -0,0 +1,7 @@
FAIL
1. `vendor/gpui-pre-windows/src/direct_write.rs:1865` — 排版语言不跟随应用选择的简体中文。Windows 用户区域为 `ja-JP` 时,这里直接保留日文;即使 Ely 设置为简体中文或指定 `ELY_LOCALE=zh-CN`,所有中文标签仍按日文选字体。隔离副本用真实 DirectWrite 和组件的 Inter SemiBold 排版“打开仓库”:`ja-JP` 得到前两字 Yu Gothic UI、字重 600,后两字 Microsoft JhengHei UI、字重 700;`zh-CN` 则四字均为 Microsoft YaHei UI、字重 600,原问题仍可复现。应让中文界面的实际语言到达排版层,并覆盖系统区域与应用语言不一致的测试;仅验证 `han()` 的语言前缀不能发现此问题。证据:`.work/R4.13-r1/probes.log`。
2. `Cargo.toml:22` — 新 pin 的 `FindWidget` 只翻译了关闭提示,查找界面仍有大量固定英文(组件 `src/editor/find.rs:179`、`:325`;ElyMark 在 `apps/ely-mark/src/finding.rs:186` 使用,ElyPDF 在 `apps/ely-pdf/src/search.rs:188` 使用)。简体中文下打开查找,无匹配仍显示 `No results`,有匹配显示 `1 of 2`,展开替换仍显示 `Replace`、`Replace all`;大小写、全词及前后匹配的提示也仍是英文。设置 `I18n::new("zh-CN")` 后的渲染探针直接记录到这些文字。应在共享组件中将计数、按钮和提示全部接入中英目录,再更新 pin;增加中文渲染断言,现有 `shipped.rs` 扫描在这些英文实际显示时仍通过。证据:`.work/R4.13-r1/probes.log`、`.work/R4.13-r1/component-and-windows-baseline.log`。
3. `apps/ely-table/src/data/dialogs.rs:61` — ElyTable 实际使用的排序、筛选组件仍未完成中文显示,`agents.md:87` 记录的这项缺口属于 R4.13 要求覆盖的已使用组件。简体中文下选中数据并打开排序,仍显示 `Add sort`、`Ascending`、`Descending`;打开筛选并添加两条规则,仍显示 `Add rule`、`Match all`、`Match any` 和 `contains` 等条件。组件 `src/tables/builders.rs:227`、`:246`、`:368`、`:398` 及 `src/tables/rules.rs:29` 保留了这些英文,渲染探针已复现。应将这些显示文字接入组件的中英目录,保留条件的内部标识,更新 pin 与缺口记录,并覆盖按钮、分段选项和条件列表的中文渲染测试。证据:`.work/R4.13-r1/probes.log`。
+44 -2
View File
@@ -40,6 +40,16 @@ pub(crate) struct DirectWriteTextSystem {
state: RwLock<DirectWriteState>,
}
/// The app's Han language, set before any window opens.
static APP_LANGUAGE: RwLock<Option<HSTRING>> = RwLock::new(None);
/// Han layout follows `tag` when it is Han.
pub fn set_text_language(tag: &str) {
let chosen = han(tag).then(|| HSTRING::from(tag));
log::info!("text: the app's language is {tag}; Han layout follows it: {}", chosen.is_some());
*APP_LANGUAGE.write() = chosen;
}
struct DirectWriteComponents {
locale: HSTRING,
/// Text's language; picks Han glyphs and fonts.
@@ -559,7 +569,7 @@ impl DirectWriteState {
font_info.font_face.GetStyle(),
DWRITE_FONT_STRETCH_NORMAL,
font_size.as_f32(),
&components.text_locale,
APP_LANGUAGE.read().as_ref().unwrap_or(&components.text_locale),
)?
.cast()?;
if let Some(ref fallbacks) = font_info.fallbacks {
@@ -1859,7 +1869,6 @@ const fn make_direct_write_tag(tag_name: &str) -> DWRITE_FONT_FEATURE_TAG {
DWRITE_FONT_FEATURE_TAG(make_open_type_tag(tag_name))
}
#[inline]
/// The user's locale, or their first Han language.
fn text_locale(user: &HSTRING) -> HSTRING {
if han(&user.to_string()) {
@@ -1900,8 +1909,41 @@ mod han_tests {
assert!(!super::han(tag), "{tag}");
}
}
/// Family and weight of each run laying out `text`.
fn faces(system: &super::DirectWriteTextSystem, text: &str) -> Vec<(String, u32)> {
use gpui::PlatformTextSystem as _;
let font = gpui::Font { weight: gpui::FontWeight::SEMIBOLD, ..gpui::font("Segoe UI") };
let font_id = system.font_id(&font).expect("Segoe UI");
let run = gpui::FontRun { len: text.len(), font_id };
let layout = system.layout_line(text, gpui::px(16.), &[run]);
let state = system.state.read();
let mut faces: Vec<(String, u32)> = Vec::new();
for run in &layout.runs {
let info = &state.fonts[run.font_id.0];
let face = (info.font_family_h.to_string(), unsafe { info.font_face.GetWeight() }.0 as u32);
if !faces.contains(&face) {
faces.push(face);
}
}
faces
}
#[test]
fn the_apps_chinese_leads_layout_under_a_japanese_user() {
let mut system = super::DirectWriteTextSystem::new(None).expect("DirectWrite");
system.components.text_locale = windows::core::HSTRING::from("ja-JP");
super::set_text_language("en-US");
let japanese = faces(&system, "打开仓库");
assert!(japanese.len() > 1, "ja-JP should split the label: {japanese:?}");
super::set_text_language("zh-CN");
let chinese = faces(&system, "打开仓库");
super::set_text_language("en-US");
assert_eq!(chinese.len(), 1, "one face for the label: {chinese:?}");
}
}
#[inline]
fn get_name(string: IDWriteLocalizedStrings, locale: &HSTRING) -> Result<String> {
let mut locale_name_index = 0u32;
let mut exists = BOOL(0);
+1
View File
@@ -38,6 +38,7 @@ pub(crate) use vsync::*;
pub(crate) use window::*;
pub(crate) use wrapper::*;
pub use direct_write::set_text_language;
pub use platform::WindowsPlatform;
pub(crate) use windows::Win32::Foundation::HWND;