docs/BugList.md records the defects found by reading every app and crate, with Calc and Impress behaviour checked against upstream LibreOffice 26.2. It explains the three reported problems: ElyTable clicks landing on the wrong cell (print twips treated as tile twips), ElyShow not repainting on slide change (no tile invalidation on part switch), and ElyPDF editing and drawing failures (tiles dropped on every edit, hit order, refusals). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TycnrQxWJyHRVUSpkoCs7e
54 KiB
缺陷清单
2026-10-09 对首版六个应用(ElyDoc、ElyTable、ElyShow、ElyPDF、ElyMark、ElyTree)与其共用 crate 的全量静态审查。逐行读了 apps/ 六个应用、crates/ 全部源码(含 C 垫片与 ElyMark 网页脚本);Calc 与 Impress 的内核行为对照上游 LibreOffice libreoffice-26-2 分支源码核实(sc/source/ui/view/*.cxx、sd/source/ui/view/*.cxx、desktop/source/lib/init.cxx)。审查容器里没有引擎与组件库,没有运行与构建;凡标“已确认”都是沿调用链读到底的结论,“可能”是路径成立但依赖未能运行核实的条件。
编号前缀:T ElyTable,S ElyShow,P ElyPDF,D ElyDoc,M ElyMark,G ElyTree,K LOK 桥(crates/ely-lok),X 共用外壳(ely-office、ely-files、ely-ui、ely-platform、ely-ipc)。严重度:致命=崩溃、丢数据或功能不可用;高=常见路径结果错或功能坏;中=边缘路径坏或界面误导;低=小问题。
用户报告的三个问题
ElyTable:点一个单元格,光标跳到别处;拖不出多格选区
根因是 T-01:Ely 把 Calc 的“打印 twips”(文档坐标)与“图块 twips”(按内核像素网格对齐的坐标)当成同一套。内核画图块与命中测试都按它自己每行每列取整后的像素网格(ScViewData::ToPixel 截断),而单元格光标、选区、表头几何都以文档 twips 报回;越往右下,两套坐标相差越多,到第 300 行左右相差整整一行。点击落在内核的像素网格上(选中的是鼠标下画出的那格),但光标框与表头按文档 twips 画,出现在相邻的格上,看起来就是“跳到别处”。T-02 让部分可见的格一被点中就把视图滚半屏,拖选到边缘时内容在指针下移动,选区随之错位。T-03、T-04 是同一组问题的两个侧面。
ElyShow:换幻灯片后主视图不变,要拉一下窗口才刷新
根因是 S-01:Impress 在 SwitchPage 期间压掉一切图块作废通知(sd::Window::LogicInvalidate 里 IsInSwitchPage() 直接返回),只发 LOK_CALLBACK_SET_PART;LokView 收到 SET_PART 只加计数,不作废图块,Input::Part 落地时也不作废。旧幻灯片的图块一直显示,直到窗口尺寸变化让适合缩放变了、全部图块退役重画。
ElyPDF:编辑不工作、画不出
鼠标、命中与坐标换算本身是对的(pages.rs 按工具分派到 press/drag_to/release 或 sketch_*,page_at/from_view 与垫片 placed() 互逆)。失败来自状态与时序:P-02 每次编辑落地都清掉全部图块与缩略图,页面先变成空白纸再一块一块重画,刚画的墨迹要等所在图块重画后才出现,重的页面上像“什么也没画出来”;P-05 上一笔还在落地时结束的下一笔被拒并提示“文档已改动”,连续手绘会丢笔画;P-03 “编辑文字和图片”先命中图片再命中文字,整页背景图或扫描件上永远点不到文字,点击稍一抖动就成了移动背景图;P-04 该工具的层读取在每个文字块上重跑一遍整页内容过滤,密集页面要等很久,等待期间点击被静默忽略;P-06 字体默认“原字体”,嵌入 Type 1/Type 3 字体的文件一应用就报原始引擎错误。另有 P-01:写到无权限目录等任何 PermissionDenied 都被拼成不存在的文案键,直接崩溃。
T ElyTable 与 LOK 画布(Calc)
T-01 内核的图块与命中按像素网格,Ely 的光标、选区、表头按文档 twips,越远越错位
- 严重度:致命 | 置信度:已确认(上游源码核实)
- 位置:
crates/ely-lok/src/view/scroll.rs:112(doc_point线性换算)、crates/ely-lok/src/view/render.rs:275-330(图块按TILE_TWIPS线性排布)、apps/ely-table/src/grid.rs:96-121(表头按place()线性换算)、crates/ely-lok/src/view.rs:382-387(单元格光标按打印 twips 直接用) - 现象:滚到几百行之后点一格,名称框显示的地址是鼠标下画出的那格,但光标框画在相邻行列上,表头标号也对不上;拖选时选区高亮与格线错开;行高非默认(如 0.2 cm)的表十几行就错一行。
- 原因:上游
ScGridWindow::PaintTile(sc/source/ui/view/gridwin4.cxx:1459-1600)把图块起点换成像素(fTilePosXPixel = nTilePosX * nOutputWidth / nTileWidth),再用ScPositionHelper/ScBoundsProvider在累加取整的像素网格里找首行首列,以该列的像素起点为原点画;ScModelObj::postMouseEvent(docuno.cxx:852)把 twips 乘GetPPTX()成像素后GetPosFromPixel(viewdata.cxx:2858)逐行ToPixel累加命中。默认行高 256 twips 取整成 17 像素(真值 17.07),每行差 0.067 像素,约 255 行差一行;默认列宽 1280 twips 取整成 85 像素,约 256 列差一列。而sc_print_twips_msgs下 CELL_CURSOR、TEXT_SELECTION、SheetGeometryData都是文档 twips,上游专门有ScViewData::GetPrintTwipsPosFromTileTwips(viewdata.cxx:2637)做两套坐标的换算,Collabora Online 的客户端也以SheetGeometry在两者之间换算;Ely 没有这一步。 - 复现:新建工作簿,键入 1 回车三百次(或打开任何几百行的 xlsx),滚到第 300 行,点 A300:光标框落在 A301 附近,左侧表头标的是 299;拖选 A300:C305,高亮与格线错开。
- 修法:以
.uno:SheetGeometryData为准在客户端建像素网格(按内核同样的ToPixel截断累加),把鼠标与可见区从文档 twips 换成图块 twips 再发给内核,把 CELL_CURSOR、选区、引用标记从文档 twips 换回图块 twips 再画;并调用setClientZoom让内核的 PPTX 与 Ely 的图块比例一致(见 T-04)。
T-02 单元格光标露出边缘即把视图滚到中央,点击与拖选时视图跳动
- 严重度:高 | 置信度:已确认
- 位置:
crates/ely-lok/src/view/scroll.rs:45-64(show),调用处crates/ely-lok/src/view.rs:372-387 - 现象:点一个只露出一半的格(右缘或底缘),整张表横向跳半屏、纵向跳三分之二屏;拖选拖到边缘时内容在指针下滑走,选区落错位置。
- 原因:
show()对任何不完全可见的矩形都重置滚动:scroll.0 = x - fx - vw / 2、scroll.1 = y - fy - vh / 3,不是最小滚动量;而apply_batch对每次光标或单元格光标变化都调它。 - 复现:把窗口缩到只露出 A 到 H 列,点半露的 H 列;或从 A1 向右下拖选到视口边缘。
- 修法:
show()只滚到刚好露出目标(scroll = x + w - vw或x),与一般表格软件一致;拖选期间不因选区变化滚动。
T-03 视口比工作表宽时网格先居中再左移一次
- 严重度:中 | 置信度:已确认
- 位置:
crates/ely-lok/src/view/render.rs:243-253(spare居中)、crates/ely-lok/src/view/scroll.rs:99-108(extent) - 现象:宽屏上打开工作簿,表格先出现在视口中央、两侧是空底色,内核按
setClientVisibleArea扩大平铺区并报DOCUMENT_SIZE_CHANGED后整张表再跳到左边;窗口每次拉宽都重现一次。 - 原因:
prepare把窄文档居中(slack(viewport.0, extent.0)),这对 Writer 页面合理,对 Calc 的网格不合理:Calc 的文档尺寸只是已用区加余量(上游GetTiledRenderingArea),随可见区增长。 - 修法:Calc 文档不居中(
spare = (0, 0)),或以reach先把extent撑到至少一个视口宽。
T-04 从不调用 setClientZoom,内核命中与几何按默认缩放算
- 严重度:中 | 置信度:已确认
- 位置:
crates/ely-lok/src/ffi/document.rs(无对应调用)、crates/ely-lok/src/view/zoom.rs:44-62(缩放只改本地geom) - 现象:Ely 缩放到 125% 或 200% 后,内核的 PPTX 仍是 100% 的 1/15,
getDocumentSize、GetPosFromPixel的像素取整都按 100% 网格算;与 T-01 叠加,缩放越大错位越明显(图块按 Ely 的比例画,命中按内核的比例算,两套像素网格的取整不同)。 - 原因:上游
ScModelObj::setClientZoom(docuno.cxx:1100)是客户端告知像素/twips 比例的唯一入口,Collabora Online 在载入与每次缩放时都调;Ely 的Document没有包装它。 - 修法:载入与每次
zoom_at后发setClientZoom(tile_pixels, tile_pixels, TILE_TWIPS, TILE_TWIPS)。
T-05 文档尺寸按内核像素取整,表尾被裁
- 严重度:低 | 置信度:已确认(上游源码核实)
- 位置:
crates/ely-lok/src/view.rs:360-368(doc_size直接取getDocumentSize) - 现象:
getDocumentSize(上游docuno.cxx:740-780)返回“像素累加后再除 PPTX”的 twips(85 像素×15=1275 twips 一列,而不是 1280),平铺区越大短得越多;滚动到最右最下时最后一列、最后几行露不全。 - 修法:以几何(
SheetGeometryData)算 Calc 的文档范围,不用getDocumentSize。
T-06 滚轮向下被内核平铺区卡住
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-lok/src/view/scroll.rs:20-28(clamp_scroll以extent为界)、render.rs:264-268(可见区在绘制时才发) - 现象:快速向下滚动空白区时每次只能滚到当前文档底,内核收到可见区后再扩 25 行,下一次滚动才能继续;连续滚动一顿一顿。
- 修法:Calc 下
extent至少放到scroll + 2 个视口,或滚动时提前发可见区。
T-07 表头点选、行高列宽拖动是“按编号的命令”,而每次点击与拖选都推进修订,紧接着的表头操作被当作过期拒绝
- 严重度:中 | 置信度:已确认(时序窗口为一次后台读取)
- 位置:
crates/ely-lok/src/commands/sheet.rs:154-164(select.column/select.row的Col/Row在NUMBERED内,commands.rs:63-72)、crates/ely-office/src/lok.rs:187-202(run_in对编号命令取read_at发 EXPECT)、crates/ely-lok/src/worker/revision.rs:51-66(鼠标按下、松开、按键拖动都bump)、apps/ely-table/src/grid.rs:204-211、:306-327、apps/ely-table/src/book.rs:199-246(reread之后read_at才更新) - 现象:点一格(或拖选、键入)后马上点列头或行头、或拖完列边:命令带着上一次读取的修订发 EXPECT,内核修订已被鼠标事件推进,回
stale:,弹“文档在此间已改动,请重试”,整列没有选中。read_at还没来得及由reread更新时(刚打开工作簿、worker 忙于画图块时)必现。 - 原因:列号、行号不会被光标移动改变,EXPECT 的保护在这里是多余的;而所有鼠标按下与松开(含拖选的每一步移动)都计入修订。
- 修法:
select.column、select.row、columns.width、rows.height不作编号命令(或run_in对它们不带at),只保留对工作表、对象、参考线等真会被改动重排的编号做 EXPECT。
S ElyShow(Impress)
S-01 切换幻灯片后主画布不重画
- 严重度:致命 | 置信度:已确认(上游源码核实)
- 位置:
crates/ely-lok/src/events.rs:274(SET_PART只加cell.sheets)、crates/ely-lok/src/view/input.rs:231-235(show_part只排队Input::Part)、crates/ely-lok/src/view/lane.rs:95-124(Input落地不作废图块)、apps/ely-show/src/slides.rs:244-247、apps/ely-show/src/deck.rs:308-314(只在母版视图切换时invalidate) - 现象:在左侧缩略图条点另一张幻灯片(或经批注栏、演示检查跳转),缩略图高亮变了,主画布仍是上一张;拖一下窗口边才变。
- 原因:上游
DrawViewShell::SwitchPage(sd/source/ui/view/drviews1.cxx:1003-1012)只发LOK_CALLBACK_SET_PART;切换期间sd::Window::LogicInvalidate(sd/source/ui/view/sdwindow.cxx:1016-1020)因IsInSwitchPage()直接返回,不发 INVALIDATE_TILES。Collabora Online 的图块请求自带part,切页即换缓存;Ely 的paint_tile不带 part,图块缓存按坐标键保留,所以旧页一直显示。窗口尺寸变化时fit_zoom改了缩放、retire_tiles才重画。Calc 切表经SetTabNo走PaintGrid会发作废,所以 ElyTable 没有这个症状。 - 复现:新建演示,“幻灯片›新建”,点第一张缩略图。
- 修法:
LokView在Input::Part落地(或收到SET_PART)时retire_tiles()并清failed,或把 part 编进图块键。
S-02 deck.rs 超过 500 行
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-show/src/deck.rs(506 行) - 原因:违反规则 5(文件不超过 500 行);工作区内只有这一个文件超限。
- 修法:把
reread的读取闭包或Extra的图片管理拆到独立模块。
D ElyDoc(Writer)
D-01 导出或打印对话框开着时关闭其标签即崩溃
- 严重度:高 | 置信度:已确认
- 位置:
apps/ely-doc/src/actions.rs:84-86(self.tab(*tab).expect("a modal keeps its tab")),对话框由apps/ely-doc/src/output.rs:38-48打开;关闭路径crates/ely-office/src/close.rs:38-42、:142-154、:191-221 - 现象:开着“导出…”或“打印…”对话框按 Cmd+W、点窗口关闭钮或 Cmd+Q,标签被移除后下一帧渲染
expect失败,进程崩溃;Cmd+Q 时先于其余脏标签的保存询问崩溃,这些标签只剩 10 秒一次的恢复点。 - 原因:
Dialog::Output记着标签号,dialog_element每帧无条件解包;没有任何地方在标签关闭时清self.dialog(Docs未实现Host::tab_closed),release_tab还把output()排队的when_settled续动作丢掉。其他对话框(Ask、Link、Reference、外壳的Unsaved)都是防御性写法。 - 复现:打开任一文档 → 文件›导出… → Cmd+W。
- 修法:
dialog_element在tab()为None时清掉对话框并返回None;Docs实现tab_closed丢掉指向该标签的Dialog::Output。
D-02 邮件合并以当前标签为模板,预览后生成的每份都是同一条记录
- 严重度:高 | 置信度:已确认
- 位置:
apps/ely-doc/src/merge.rs:88-94(source()返回core.active)、:152-164(预览)、:178-195(生成);apps/ely-doc/src/merge/run.rs:351-371(预览经load_into开新标签)、crates/ely-office/src/open.rs:231(新标签成为当前) - 现象:选 CSV、插字段、点“预览”(填好的副本开成新标签并成为当前标签,合并面板仍在右侧)再点“生成”:模板成了预览副本,字段早已替换成第 N 条记录,N 个输出文件内容相同,文件名还带预览标签名;再点预览是预览的预览。无任何提示。
- 原因:
Merge不记来源标签,每次点击时取core.active;单测a_merge_keeps_its_source_when_the_tab_changes直接传source,没覆盖界面路径。 - 复现:任一含
«Name»的文档 → 邮件合并 → 三行 CSV → 预览 → 生成。 - 修法:选 CSV 或插字段时记下来源标签号(或在
Extra标记预览标签),面板显示来源名,预览与生成都用它,当前标签是预览副本时拒绝。
D-03 界面线程同步持 worker 锁读内核(样式、字体、书签、公式、分节名)
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-doc/src/insert.rs:318-324(insert_section)、apps/ely-doc/src/refs.rs:81-86(read,书签、交叉引用、公式对话框用)、apps/ely-doc/src/props.rs:116-122(read_fonts)、:133-137(read_styles,每次切标签与Reported都跑) - 现象:保存大文档、邮件合并几百条记录(每条载入、替换、另存、关闭都持锁)期间,切标签或打开“插入›书签…”整窗冻结到锁空出来为止。
- 原因:违反 2026-10-05 决策“界面线程不碰 worker”,这四处直接
worker.lock()而不走read_in/read_off。 - 修法:改为后台读取,回来时核对仍是同一标签与视图。
D-04 一条批注的时间读不出,全部批注与修订都不显示
- 严重度:中 | 置信度:可能(内核对无日期批注写
0-00-00,utl::toISO8601的空日期即如此) - 位置:
apps/ely-doc/src/review.rs:164-167、:373-393(threads遇第一个解析失败即整体Err)、:179-181 - 现象:打开含无日期批注的 DOCX(
w:comment无w:date),批注与修订两栏都空,并弹引擎错误提示;每次审阅事件重读再失败再弹。 - 原因:单条解析失败被提升为整表失败,
and_then连已解析的changes一起丢掉。 - 修法:逐条解析时间,失败的那条用占位时间;
changes不因批注失败而丢。
D-05 邮件合并某条记录失败时内核文档泄漏
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-doc/src/merge/run.rs:464-470 - 原因:
fill/save_as以?提前返回,close_doc不执行,Doc无Drop;每次重试多漏一份,worker 的N documents open计数漂移。 - 修法:先取结果再一律
close_doc,最后传出首个错误。
D-06 导航栏大纲点击按当前列表取下标,后台重读后可能越界
- 严重度:低 | 置信度:可能
- 位置:
apps/ely-doc/src/navigate.rs:277-283(navigator.outline[at]) - 修法:用
get(at),或在渲染时把目标矩形放进行的闭包。
D-07 导航栏隐藏时仍随每次光标移动重读大纲与书签
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-doc/src/chrome.rs:236-245(left_view不看navigating就navigator.show(tab))、apps/ely-doc/src/navigate.rs:79、:91-123 - 现象:关掉导航栏后每次按键仍排两次内核读取,与键入争 worker 锁(规则 9:空闲不做事)。
- 修法:隐藏时
show(None),显示时重读一次。
D-08 Linux、Windows 上“打印…”可用,导出 PDF 后才报“此平台未构建”
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-doc/src/output.rs:28-30、:80-88;crates/ely-platform/src/print.rs:87-89 - 现象:菜单与页码范围对话框都正常,最后一步弹出带临时路径的错误提示。各文档应用同(见 X-13)。
- 修法:平台未实现时禁用菜单项或在对话框里写明;错误文字去掉路径。
G ElyTree(Git)
G-01 打开“文件历史”或“逐行追溯”后,中间区不再响应任何别的选择
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-tree/src/view.rs:185-187(body先于match self.side返回look_pane);入口apps/ely-tree/src/hist_pane.rs:43-66、apps/ely-tree/src/pane.rs:483 - 现象:按下差异标题栏的“文件历史”或“逐行追溯”后,点改动列表里另一个文件、历史里另一个提交、分支或储藏,中间区都不变;只有历史面板的小叉能退出。
- 原因:
show()(pick.rs:302)、pick_commit、pick_place、set_side都不清self.look,只有close_look与 blame 的show_commit清。 - 修法:任何新选择都清
self.look,或只在side == Changes且所选文件不变时画它。
G-02 进行中操作读取失败被吞掉;git am 冲突无提示条无错误
- 严重度:中 | 置信度:已确认(
git am细节为可能) - 位置:
apps/ely-tree/src/ops.rs:81-98(doing)、apps/ely-tree/src/moves.rs:74、apps/ely-tree/src/conflict_pane.rs:62、:106-109 - 现象:
doing()出错时opened.doing = Some(Err(..)),没有任何显示或日志;冲突文件列出来了,却没有“继续/跳过/中止”提示,冲突栏两侧退回泛称。 - 原因:
rebase-apply一律当变基读onto,git am的rebase-apply/没有onto,整个读取NotFound;describe()失败(如MERGE_HEAD指向已被清理的对象)同样整体失败。违反规则 2。 - 复现:终端里
git am停在冲突,在 ElyTree 打开该仓库。 - 修法:
Some(Err)以提示条显示 Git 原话并记日志;无onto的rebase-apply视为git am进行中。
G-03 新建的空仓库在“仓库”页显示两段 Git 致命错误(引用日志、LFS)
- 严重度:中 | 置信度:可能
- 位置:
apps/ely-tree/src/recover.rs:50-69(reflog … HEAD --)、apps/ely-tree/src/structure.rs:137-138(git lfs ls-files)、apps/ely-tree/src/places.rs:40-50、apps/ely-tree/src/side_places.rs:261-264、:279-281 - 现象:“新建仓库…”后仓库页出现 “fatal: bad revision 'HEAD'” 与 “Git can't resolve ref: HEAD” 两块红字;仓库没有问题,空列表才是正确状态。
- 原因:HEAD 未诞生时显式传
HEAD的reflog show以 128 退出,run_or_one不接受;git lfs ls-files同样先解析 HEAD。 - 修法:
status.head.oid为空时两项直接给空列表。
G-04 停止取回/拉取/推送失败的提示写成“克隆没有停下”
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/network.rs:126(stop_remote复用tree.error.stop)、crates/ely-ui/src/i18n/tree.rs:53 - 修法:加一条不限动词的文案。
G-05 合并结果保存失败报成“Git 失败”,tree.error.save 是死文案
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/work.rs:424((_, Err(error)) => failed("tree.error.write", …))、crates/ely-ui/src/i18n/tree_ops.rs:127、:255(定义却无人用,规则 6) - 修法:
written()加(Write::Save, Err)分支用tree.error.save。
G-06 首次提交后紧接着取消暂存,按过期的“已有提交”判定跑成 git rm --cached
- 严重度:低 | 置信度:可能(竞争窗口为一次状态重读)
- 位置:
apps/ely-tree/src/work.rs:352(born取自缓存的opened.status)、:426-427(read异步、next_write同步)、:85-93、:103-112 - 现象:新仓库里暂存、提交,列表刷新前按“全部取消暂存”:第二条写操作仍按
oid: None跑rm --cached -f -r -q -- .,把刚提交的全部文件标为删除(可恢复,但吓人)。 - 修法:写操作执行前在后台
rev-parse --verify -q HEAD判定,不用缓存状态。
G-07 任一引用没有创建日期,整个分支页读不出
- 严重度:低 | 置信度:可能
- 位置:
apps/ely-tree/src/branches.rs:33(%(creatordate:unix))、:106(空串解析失败整体Err)、apps/ely-tree/src/side_refs.rs:227-235 - 复现:
git tag blobtag HEAD:README.md,打开分支页。 - 修法:日期可缺(显示“—”),或跳过非提交对象的引用。
G-08 清理预览后文件被外部删除,对话框只给裸 OS 错误
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/work.rs:270(symlink_metadata(&at)?)、:415-422 - 现象:标题“Git did not do it”,正文 “No such file or directory (os error 2)”,没有路径,Git 并未运行。
- 修法:映射成带路径的“自预览后已变”文案。
G-09 后台读取回来的对话框覆盖用户同时打开的对话框
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/network.rs:186(推送选远程)、:237(强制推送)、apps/ely-tree/src/recovering.rs:116(移动分支) - 现象:按“推送”(无上游)后立刻按“储藏改动…”并开始键入,
git remote回来时储藏对话框连同输入被换掉。 - 修法:只在
tree.ask仍为空(或仍是本次请求)时赋值。
G-10 选中的分支、标签、位置是窗口级的,跨仓库标签串台
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/tree.rs:107-108(picked_ref、picked_place在Tree上)、apps/ely-tree/src/refs_pane.rs:25-33、apps/ely-tree/src/side_refs.rs:238-245 - 现象:仓库 A 选中
main,切到仓库 B,B 的分支页显示它自己的main为已选并带动作按钮。 - 修法:移入
Opened,或activate()时清掉。
G-11 已选边的冲突块再选另一边,提示“结果已手改”
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/conflict.rs:219-224 - 修法:给“该块已选定”单独文案,或支持改选。
G-12 符号链接父目录下,当前打开的工作树被当成别的工作树而可“移除”
- 严重度:低 | 置信度:可能
- 位置:
apps/ely-tree/src/places_pane.rs:253-255、:265-271、apps/ely-tree/src/structure.rs:245-247 - 原因:
here用git worktree list记录的原始路径比对已规范化的opened.repo.root(macOS/tmp→/private/tmp)。 - 修法:比对前
canonicalize。
G-13 非 macOS 平台的死入口
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/running.rs:167-173(停止远程命令非 Unix 报未构建)、apps/ely-tree/src/tools.rs:109-115(“在终端中打开”非 macOS 报未构建)、crates/ely-ui/src/i18n/tree_places.rs:147、:281(“在访达中显示”文案在 Linux、Windows 照样显示) - 修法:按
cfg禁用或隐藏,reveal换平台中性文案。
G-14 repo.rs 的孤立文档注释
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-tree/src/repo.rs:10(“How many repositories the recent list keeps.” 的常量早已移到ely_files::recent,注释挂到了struct Repo上) - 修法:删掉。
P ElyPDF(MuPDF)
P-01 文件系统的 PermissionDenied 被拼成不存在的文案键,应用崩溃
- 严重度:致命 | 置信度:已确认(文案层缺键即失败)
- 位置:
apps/ely-pdf/src/view.rs:123-126(restricted拼pdf.may.{needed})、:228(fail对一切PermissionDenied都走restricted(&error.to_string()))、apps/ely-pdf/src/protect.rs:315、apps/ely-pdf/src/ask.rs:507-509 - 现象:导出、提取、拆分、签名、保护、附件另存写到不可写文件夹,或任何 OS 权限错误,不显示错误而是崩溃。
- 原因:
restricted假定错误文字是 worker 的restricted:<名>(client.rs:297-302),但ely_files::export→save_atomic保留 OS 的错误类别,holder()→same_file→canonicalize同样,annotate()里的user::name()读设置也可能是PermissionDenied;得到的键pdf.may.Permission denied (os error 13)不在任何表里。 - 复现:文件›导出… 到只读文件夹。
- 修法:只在文字是
may::NAMES之一时走restricted,否则走pdf.failed/office.error.save;或给 worker 的拒绝单独的错误类型。
P-02 每次编辑落地清掉全部图块与缩略图,页面空白、刚画的墨迹消失
- 严重度:高 | 置信度:已确认
- 位置:
apps/ely-pdf/src/edits.rs:102-111(refresh清tiles、thumbs)、apps/ely-pdf/src/pages.rs:272-279(每帧只请求一块) - 现象:任何编辑(一笔手绘、勾一个复选框、填一个字段)后可见页面变成空白纸,再以每帧一块 512 像素的速度填回;期间刚加的批注看不见;缩略图也全部重画。图多的页面上每次编辑要几秒,感觉“画了没反应”。
- 原因:LOK 画布作废时旧图继续显示到新图到达(agents.md 已定),PDF 视图没有照做:
retire_tiles()直接清空self.tiles,page_view画bg(paper)加空的tiles。 - 修法:旧图块保留到替换到达时才退役(
paint()的tiles.insert处已这样做),缩略图同。
P-03 “编辑文字和图片”先命中图片对象再命中文字;一点抖动就移动了背景图
- 严重度:高 | 置信度:已确认
- 位置:
apps/ely-pdf/src/content.rs:172-187(命中顺序)、:206(1 点阈值) - 现象:整页背景图、扫描件、包裹整页的表单 XObject 上,点任何文字选中的都是图片(面板显示替换/删除);按下与松开相差 1 个页面点(适合宽度下约 1 像素)即成“移动对象”编辑,把背景图挪走。图下的文字永远编辑不到。
- 原因:
layer.drawn.iter().rposition(|d| inside(..))先于文字块查找,不看目标大小;release()的阈值是页面点不是窗口像素。 - 修法:对象整页或包住文字块时优先文字块;移动阈值按窗口像素(如 3 像素)。
P-04 TEXT_BLOCKS 对每个文字块重跑一遍整页内容过滤,编辑工具长时间无反应
- 严重度:高 | 置信度:可能(代价推断,未计时)
- 位置:
crates/ely-pdf-engine/shim/ely_text.c:227-245、:156-179、:133-145(每块一次pdf_filter_page_contents);apps/ely-pdf/src/content.rs:129-156(逐页读)、:169-171(层未到即静默返回) - 现象:密集页面上开编辑工具很久不出框,点击被忽略,图块与缩略图也停住(worker 单线程被占满)。
- 修法:每页一遍干跑记下全部字形原点与字符,再逐块比对;或只对点中的块判定可编辑。
P-05 连续快画或快点被拒:“文档在此间已改动,未执行”
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-pdf/src/annotate.rs:189-203(settled_edit)、apps/ely-pdf/src/content.rs:209-214、apps/ely-pdf/src/writes.rs:176-190、apps/ely-pdf/src/edits.rs:56-93 - 现象:上一笔还在落地(
annotate+重读页数、页框、书签)时结束的下一笔、下一个矩形、下一个复选框点击被丢弃,并把原因说成“文档改动”;多笔手绘丢笔。 - 原因:
settled()是队列为空且不在编辑中;按坐标的批注不会被在途编辑作废(按编号的才会,worker 已以stale:拒绝),却被一律拒绝而不是排队。 - 修法:按坐标的批注进编辑队列;拒绝只留给按编号的编辑,并改正文案。
P-06 默认字体“原字体”在 Type 1/Type 3 字体的文件上失败,只给引擎原话
- 严重度:中 | 置信度:可能(
pdf_font_writing_supported只认 TrueType/CFF/OpenType) - 位置:
apps/ely-pdf/src/editing.rs:77、crates/ely-pdf-engine/shim/ely_write.c:61-67、apps/ely-pdf/src/writes.rs:115、:138-142 - 现象:LaTeX 或旧 PDF 上“应用”报“previewing text failed: the document's font cannot write new text”,不提示换字体(缺字形的情形倒有
pdf.text.missing提示)。 - 修法:像
Wrote::Missing一样在面板里提示换字体,或读块时预判可写性、默认 Helvetica。
P-07 含连字的文字块永远不可编辑
- 严重度:中 | 置信度:可能
- 位置:
crates/ely-pdf-engine/shim/ely_text.c:91-109(block_targets按 stext 字符计数)、:112-130(take_glyph按字形计数)、:167 - 现象:含 fi、fl、ffi 连字的段落不出编辑框、点击无效;真放过去
ely_remove_block也会报“N of M glyphs were found”。 - 修法:按同一原点的字符分组计字形,或每字形按
ucslen多计命中。
P-08 页面读取失败后每帧重试、每帧弹提示
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-pdf/src/annotate.rs:76-110(ask_notes)、:113-134、apps/ely-pdf/src/content.rs:129-156(ask_layers)、apps/ely-pdf/src/attach.rs:184-205、apps/ely-pdf/src/sign.rs:49-75 - 现象:某页的批注、层、附件、名称或签名读一次失败后,
fail弹提示、notify重绘、重绘再问、再失败,没完没了;worker 死掉后每个可见页每帧一条错误提示,直到按“重新打开”。 - 原因:失败不记页(图块有
failed/thumbs_failed,这些没有)。 - 修法:各类读取加按页的失败集合(
refresh()清),引擎已结束时不再问。
P-09 识别文字的写入被拒后仍弹“成功”
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-pdf/src/ocr.rs:510-513(toast(.., Success)在on_same之后无条件执行) - 修法:成功提示移进
on_same的闭包。
P-10 预览回复被丢弃时“应用”静默无事
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-pdf/src/writes.rs:72-86、apps/ely-pdf/src/view.rs:241-247(read丢弃编辑前的回复而不调then) - 修法:改用带版本核对的
off,不符即提示pdf.object.changed。
P-11 撤销或重做在 MuPDF 侧失败后 worker 的步数不再同步
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-pdf-engine/src/bin/ely-pdf-worker/main.rs:62-86、:161-165 - 修法:失败时
doc.sync(&self.mupdf, None)。
P-12 unhook 中途抛出时泄漏保留的对象
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-pdf-engine/shim/ely_insert.c:17-49、:352-356(n只在返回时发布,fz_always以 0 释放) - 修法:以
int *n传入并逐个递增。
P-13 优化导出的“之前”大小取磁盘文件而非当前文档
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-pdf/src/output.rs:217-224 - 修法:量 worker 的快照,或标签有改动时不写“之前”。
P-14 文字块含控制字符时 JSON 回复不可解析
- 严重度:低 | 置信度:可能
- 位置:
crates/ely-pdf-engine/shim/ely_text.c:240(%q不是 JSON 转义;别处用的是ely_json_string) - 现象:ToUnicode 坏掉的文件文字里有 U+0001–U+001F,
text_blocks解析失败,编辑工具永远读不到该页,并落入 P-08 的循环。 - 修法:
text、font用ely_json_string写。
M ElyMark(CEF)
M-01 键入期间外部以相同内容重写文件,丢掉上次停顿之后键入的全部文字
- 严重度:高 | 置信度:已确认
- 位置:
apps/ely-mark/src/outside.rs:223-224((true, _) | (false, false) => take_disk)、:243-268(take_disk)、apps/ely-mark/src/view.rs:184-196(refresh把changed、reported归零)、apps/ely-mark/web/src/main.js:52-59(全文停手 300 毫秒才送)、:179-187(refresh清掉待送) - 现象:连续键入时外部进程以相同字节重写文件(
touch、同内容的git checkout/stash pop、云同步重盖、格式化器输出相同),编辑器以旧文字重建,上次停顿以来键入的字全部消失,标签还显示已保存;无提示。 - 原因:页面只在停手 300 毫秒后送全文,键入中 Rust 侧
doc.current是停顿前的文字;disk.text == current的分支不看dirty(它含edit_pending),直接take_disk。与 agents.md “有未保存内容(含页面还没送回的编辑)即不动磁盘与编辑器”相悖。 - 复现:打开
a.md连续键入几秒,期间在终端touch a.md。 - 修法:
disk.text == current而视图未落定时不take_disk:等when_settled后重判,或当作Outside::Changed;落定且相同时只换指纹不动编辑器。
M-02 refresh 归零版本计数却不换代,在途的一键让标签一直“未落定”
- 严重度:中 | 置信度:可能
- 位置:
apps/ely-mark/src/view.rs:193、:264-272(Loaded路径有换代)、apps/ely-mark/web/src/main.js:179-187、crates/ely-web-content/src/engine.rs:127-143;受影响crates/ely-office/src/close.rs:96-99、save.rs:325-326、apps/ely-mark/src/compare.rs:381-382 - 现象:干净标签被外部改动刷新后,Cmd+S、Cmd+W、Cmd+Q、比较与自动保存都静静等
when_settled,直到再键入一个字才一起执行。 - 原因:刷新前排队的一键在渲染进程里先产生
changed = N+1,再执行refresh丢掉待送全文;Rust 已归零又收到changed,没有text跟上,settled()为假;refresh不像Loaded那样换代,旧回报没被丢。 - 修法:
refresh自己换一代(self.load += 1传给ely.refresh(text, load))。
M-03 干净标签接过磁盘文字后关闭仍问“是否保存”
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-mark/src/outside.rs:243-268(take_disk不更新tab.mark)、apps/ely-mark/src/view.rs:192、apps/ely-mark/src/engine.rs:133-136、crates/ely-office/src/close.rs:107-128 - 现象:文件在别处被改、ElyMark 静默重载后,Cmd+W 对没动过的标签弹未保存询问;比较对话框选“用磁盘上的”后同。
- 原因:
request_close比较Engine::history(磁盘文字散列)与tab.mark(载入或上次保存时的散列),不等即问。 - 修法:
take_disk(与合并采用后)同步tab.mark = mark(&disk.text)。
M-04 第二次打印或导出 PDF 悄悄取消第一次,没有文件也没有错误
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-mark/src/export.rs:93-97、:276(打印与导出共用print-{id}.html)、:315-334(self.printing = Some(..)覆盖) - 原因:
Desk.printing只有一个槽,赋新值即丢掉上一个的隐藏WebView与订阅,第一份的Event::Printed无人接。 - 修法:按作业编号保存多份,或一份未完时拒绝第二份并提示。
M-05 点页面里的链接什么也不发生
- 严重度:中 | 置信度:已确认
- 位置:
apps/ely-mark/src/view.rs:274(Event::Leave(url) => log::info!(..))、crates/ely-web-content/src/fence.rs:319-324 - 现象:预览与可视模式里点
http(s)/mailto链接:不跳转、不开浏览器、不提示。PRD 允许用户主动打开外部链接,agents.md 说“交给应用”,应用没处理。 - 修法:
Leave时http、https、mailto交系统打开,其他协议提示拒绝(同 ElyPDF 的链接策略)。
M-06 on_app_quit 无条件删恢复点与崩溃锁,AppKit 直接终止时丢未保存内容
- 严重度:中 | 置信度:可能(依赖 GPUI 对非 Quit 动作的终止也跑
on_app_quit) - 位置:
apps/ely-mark/src/desk.rs:244-248、apps/ely-mark/src/restore.rs:145-155(Keeping::close) - 现象:注销、关机或 Dock 的“退出”经
terminate:结束进程,proceed(After::Quit)的未保存流程不跑,退出观察者却删掉session.lock与recovery/;下次启动读到“干净结束”,什么也不回来。外壳的Recovering::end有同样假设(见 X-01)。 - 修法:退出观察者只在无标签(或全部干净)时清理,否则留下锁与恢复点。
M-07 崩溃恢复的未命名副本按“第一个显示的未命名标签”配对,内容可能落错标签
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/src/restore.rs:195-201、apps/ely-mark/src/desk.rs:226-228 - 修法:
Waiting条目记下为它新建的标签号,restore_shown按号配对。
M-08 页面重载后悬着的渲染回调永不完成,导出既不成功也不报错
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/src/view.rs:264-272(Loaded不清renders)、:210-220、apps/ely-mark/src/export.rs:165-185 - 修法:
Loaded、Failed时把renders全部以错误结束。
M-09 导出的保存面板出错被当作取消
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/src/export.rs:129-131(let Ok(Ok(Some(path))) = chosen.await else { return log::info!(..) }) - 修法:照
Desk::open_picked分三种情形,错误走panel_failed。
M-10 关闭查找框只清当前标签的高亮
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/src/finding.rs:116-124(close_find只对active_view发unfind)、:60-69、apps/ely-mark/web/src/main.js:132、:270-273 - 修法:对每个标签的视图发
unfind。
M-11 页面把搜索词写进应用日志
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/web/src/main.js:246(console.warn带place.query)、crates/ely-web-content/src/handlers.rs:272-275(控制台警告进log::warn!) - 原因:违反规则 4(日志不含正文)。
- 修法:只记行号与是否标题。
M-12 浏览器创建完成前 WebView 已释放,浏览器泄漏
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-web-content/src/view.rs:582-587(Drop时还没有浏览器)、crates/ely-web-content/src/handlers.rs:349-354(on_after_created无条件存入)、crates/ely-web-content/src/engine.rs:165-170 - 现象:开了标签在约 100 毫秒内关掉,渲染进程留到退出。
- 修法:
on_after_created发现surface.closed即close_browser(true)。
M-13 编辑器构建失败时页面空白且不上报
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/web/src/main.js:117-139(show无try/catch)、apps/ely-mark/web/src/visual.js:106-109 - 现象:Milkdown
create()或newTable抛出时没有shown也没有Failed,#document空着,MarkView.shown永为假(跳转、外观等一直等)。 - 修法:
build包try/catch,可视失败退回源码并tell({kind:'failed'})。
M-14 render_pdf 在界面线程内嵌图片并写文件
- 严重度:低 | 置信度:已确认
- 位置:
apps/ely-mark/src/export.rs:276-282(渲染回调里inline、read_to_string、fs::write);对照:172-176的write_output已在后台 - 修法:暂存步骤移到
background_spawn。
K LOK 桥(crates/ely-lok 的 worker、客户端、事件)
T-01、T-04、T-07 与 S-01 的根源都在这一层,这里只列此外的问题。
K-01 非 macOS 的 worker 不运行 VCL 主循环,内核的回调与异步输入永远不送达
- 严重度:高 | 置信度:可能(代码已读;Linux、Windows 未运行)
- 位置:
crates/ely-lok/src/worker.rs:149-164(非 macOS 的serve只是阻塞的read_frame循环)、patches/libreoffice/0001-lok-macos-tile-painting.patch(CallbackFlushHandler::scheduleFlush非 MACOSX 分支仍Application::PostUserEvent) - 现象:Linux、Windows 上 worker 能答同步请求(载入、画图块、保存),但作废、光标、状态、对话框关闭等回调一条也推不出来,键盘与鼠标(内核经
SfxLokHelper::post*EventAsync以用户事件派发)也到不了文档——编辑器是一张静止的图。树莓派是明确目标。 - 原因:用户事件只在
Application::Yield/lo_runLoop里派发;只有 macOS 路径以 GCD 加run_loop跑循环。 - 修法:各平台都以 unipoll 的
runLoop加轮询回调驱动请求队列,照 macOS 的做法。
K-02 图块的代际在画完之后才取,画到一半的作废被盖掉
- 严重度:中 | 置信度:可能
- 位置:
crates/ely-lok/src/worker/input.rs:76-81(painted:先paint,再events::generation(id))、crates/ely-lok/src/view.rs:466(tile.generation >= self.generation即接受) - 现象:
paintTile期间内核发出作废(Writer 画时懒排版、Impress 空闲任务),返回的像素是作废前的,却带着新代际;视图接受并不再重画,旧内容留到下一次无关的作废。 - 修法:
paint()之前先读代际并盖到图块上。
K-03 rect_of 宽容解析把读不出的载荷悄悄吞掉
- 严重度:低 | 置信度:可能
- 位置:
crates/ely-lok/src/events/parse.rs:25-34,用于events.rs:212、:217-218、:221、:248 - 现象:不是 “x, y, w, h…” 的载荷(如 Impress/Writer 表格多选时 GRAPHIC_SELECTION 的 JSON)要么被静默丢掉(选区框与手柄消失、不报 fault),要么凑出四个数字就被误读。与规则 2 相悖。
- 修法:严格解析开头的数字字段,读不出像
found_of一样进faults。
K-04 Sheets.current 用 as u64 转有符号的 part
- 严重度:低 | 置信度:可能
- 位置:
crates/ely-lok/src/worker.rs:377(doc.part() as u64、count as u64)、crates/ely-lok/src/client/doc.rs:40 - 现象:
getPart()返回 -1(没有视图外壳,关闭或重排期间)时current成了 2^64-1;今天的应用用.get不会 panic,但列表与标签页会错。 - 修法:
try_from,为负即报错。
X 共用外壳与平台(ely-office、ely-files、ely-ui、ely-platform、ely-ipc)
X-01 设置窗口在前时 Cmd+Q 直接退出,跳过未保存询问并抹掉恢复点
- 严重度:致命 | 置信度:已确认
- 位置:
crates/ely-ui/src/run.rs:77(应用级on_action(Quit → cx.quit()))、crates/ely-ui/src/commands.rs:170(secondary-q不带键位上下文)、:150(菜单项)、crates/ely-ui/src/settings/window.rs(设置窗口根上没有Quit处理)、crates/ely-office/src/recover.rs:59-71、crates/ely-files/src/crash.rs:29-46 - 现象:文档应用开着设置窗口(Cmd+,)时按 Cmd+Q 或选应用菜单“退出”,进程立即结束:未保存文档没有询问;
on_app_quit跑Keeping::end()把这次运行标为干净结束,下次启动Crash::check读到干净、prune删光恢复点,内容也找不回。 - 原因:动作先沿聚焦窗口的树派发,没人处理才落到应用级监听;文档宿主只在自己窗口根上截
Quit(走Closing::proceed(After::Quit)),设置窗口的Shell不截,于是直接cx.quit()。 - 复现:ElyDoc 新建文档键入几字,Cmd+, 打开设置,Cmd+Q。
- 修法:全局
Quit改为让每个文档窗口走自己的退出流程(遍历cx.windows()派发),或设置窗口把Quit转发给主窗口宿主;Keeping::end只在全部标签干净时删恢复点。
X-02 共用 settings.json 读不出时 Doc、Table、Show 根本起不来
- 严重度:高 | 置信度:已确认
- 位置:
crates/ely-office/src/lok.rs:135(启动任务里set_author(&user::name()?))、:82(restart同)、crates/ely-platform/src/user.rs:7(Shared::load()?) - 现象:
<数据目录>/ElyOffice/settings.json不能解析、version大于 1 或某字段类型不对时,三个 LOK 应用都以“the document engine did not start: …settings.json…”的致命对话框退出(码 1);引擎崩溃后的“重新打开文档”同样失败。agents.md 规定设置读不出“即用默认值、记下原因”,settings::install也确实这样做,唯独这条路径把它变成致命错误。 - 复现:往 settings.json 写
{"version": 2},启动 ElyDoc。 - 修法:
start/restart里设置读不出时退回user::account()并记日志(设置窗口已有 Banner 说明);或让user::name()用ely_ui::settings::current已读好的值。
X-03 第二个实例启动即清掉运行中实例的工作目录与恢复点
- 严重度:高 | 置信度:机制已确认;频率为可能(Linux 桌面每个文件起一个进程;macOS
open -n或开发构建) - 位置:
crates/ely-office/src/lib.rs:219-226(fresh_work无条件remove_dir_all),调用crates/ely-office/src/lok.rs:128、apps/ely-pdf/src/viewer.rs:102、apps/ely-mark/src/desk.rs:162;crates/ely-office/src/recover.rs:59-71与crates/ely-files/src/crash.rs:40-46(锁里 pid 活着=干净结束) - 现象:实例 B 启动时 (a) 删掉
<data>/work——A 的每个tab.work——此后 A 的每次保存都在消失的目录里写save.{format}失败(files.rs:48-50),状态变“保存失败”、自动保存停止;(b)Crash::check见 A 的 pid 活着判为干净,prune删掉 A 的全部恢复点与index.json;(c)Crash::open把session.lock改成 B 的 pid,A 正常退出时删掉 B 的锁,B 之后崩溃被当作干净结束。 - 复现:Linux 从文件管理器连开两个 .docx;macOS
open -n -a ElyDoc;然后在第一个实例 Cmd+S。 - 修法:数据目录加单实例锁(flock),已有实例在时把文件交给它;或工作目录按进程分(
work/<pid>)、恢复点按锁 pid 存活判定不清理。
X-04 属性栏字号菜单的 expect("sizes are numbers") 是脆弱点,但当前不可达
- 严重度:低 | 置信度:已确认(上游源码核实)
- 位置:
crates/ely-office/src/panel.rs:220-228、:283-285 - 说明:上游
FontHeightPayload(sfx2/source/control/unoctitm.cxx:960-966)只发=<数字>(禁用时为 0),不发disabled;混合选区经补丁 0003 的 JSON 走MIXED被过滤。所以这里不会 panic。保留为脆弱点:任何将来改动内核状态格式都会让整个属性栏崩溃,points应改为可失败。
X-05 恢复点写不成时每 10 秒弹一次提示
- 严重度:中 | 置信度:已确认
- 位置:
crates/ely-office/src/recover.rs:227-231、crates/ely-files/src/recovery.rs:105-116 - 现象:快照超过恢复点容量上限(默认 64 MB,可设到 32 MB;带视频的 PPTX、大 XLSX),或磁盘满、数据目录只读,只要标签还脏,每个计时周期都弹“office.recovery.failed”。
- 原因:失败时
slot.written不变、也没有像keeping.behind那样记住上次失败,下个周期due又含该标签。 - 修法:按槽记上次失败文字,变了或成功才再提示;超容量时写
written = Some(edited)并解释一次。
X-06 session.lock 空或损坏即永久关闭恢复功能
- 严重度:中 | 置信度:已确认
- 位置:
crates/ely-files/src/crash.rs:21-25(非原子写)、:29-50(其他内容返回InvalidData)、crates/ely-office/src/recover.rs:59、:134-141 - 现象:断电截断
session.lock后,每次启动都显示“office.recovery.off: bad session lock”、不写恢复点;没有任何代码重写该文件,用户得手删。 - 修法:解析不出当作
Marker::Crashed(给出恢复点并重写锁),锁文件原子写。
X-07 加密文档的恢复点每次写入都解密全部已有点,且持 worker 锁
- 严重度:中 | 置信度:可能
- 位置:
crates/ely-files/src/recovery.rs:120-121、:164-190、:193-199、:225-237;调用crates/ely-office/src/recover.rs:201-214(在read_in内) - 现象:带密码的 PDF 每 10 秒写点时读并 AES-GCM 解密每个
.enc(至多 20 份×文档大小)只为列出;RecoveryKey::load()(钥匙串加 1 秒锁循环)也每次都跑;PDF 视图排队的编辑与读取随之停顿(树莓派上更久)。 - 修法:按文件名列点,恢复时才解密;密钥每次运行读一次。
X-08 另存为在等待关闭或退出时被拒绝,关闭链悬空,ElyMark 的会话清单冻结
- 严重度:中 | 置信度:已确认
- 位置:
crates/ely-office/src/save.rs:219-246(identity出错、office.error.format、office.error.taken、office.error.convert四处提前返回不调close_cancelled),对照:191-194、:419-424有调;apps/ely-mark/src/desk.rs:85-88、apps/ely-mark/src/restore.rs:282-285、:341-343、:362-366 - 现象:退出或关窗途中对未命名标签答“保存”、选了不可保存的扩展名或别的标签正打开的文件:弹一条提示,退出静静停住;ElyMark 的
keeping.leaving留在true,keep()不再写session.json,到下次尝试退出前打开、关闭、改名的标签都不进清单,崩溃后恢复缺它们。 - 复现:ElyMark 新建未命名文档,Cmd+Q → 保存 → 选
notes.txt。 - 修法:
write_as的每个提前返回在then.is_some()时调close_cancelled。
X-09 保存期间落地的编辑可能让自动保存停摆
- 严重度:低 | 置信度:可能(与事件次序有关)
- 位置:
crates/ely-office/src/open.rs:241-251(只在ModifiedStatus假→真时session.edit())、crates/ely-office/src/save.rs:68-70、:131-132、crates/ely-files/src/session.rs(票据按写入时的修订) - 现象:
save_owned清掉内核 modified、随后导出期间键入的=true若在导出续动作调ely_files::save之前处理,reported看到(true, true)不调edit(),finish_save置Saved;tab.dirty()为真而state != Dirty,自动保存不再触发直到手动保存。 - 修法:导出开始时取票据,或每次
Engine::edited递增都session.edit()。
X-10 别的标签的文件所在卷消失时,本标签的另存为失败并被标为保存失败
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-office/src/save.rs:32-41(holder对每个标签same_file(open, target)?)、:231-234、:429-450(report→save_failed()) - 修法:
holder里解析不出的别的标签路径视为“不是同一文件”并记日志;写入前的拒绝不调save_failed()。
X-11 worker 的 stderr 中继遇到第一行非 UTF-8 即永久停止
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-ipc/src/relay.rs:31-34(lines()出错即return) - 现象:LibreOffice 或 MuPDF 打出一行非 UTF-8(Linux 上 Latin-1 文件名)后,之后的结构化日志、崩溃栈与
Relay::last_error全丢。 - 修法:
read_until(b'\n')加from_utf8_lossy,出错继续。
X-12 恢复路径在界面线程做文件 I/O
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-office/src/recover.rs:89-98(flush_index的暂存写加sync_all,keep_tab/forget_tab调)、:287、:316(restore同步调钥匙串与拷贝) - 现象:每次开关标签都在界面线程 fsync
index.json;崩溃后首启在界面线程查钥匙串(最多等 1 秒锁)并逐个拷贝恢复点。慢介质(树莓派、SD 卡)上可见卡顿(规则 9)。 - 修法:移到后台执行器,
in_window续。
X-13 非 macOS 构建里仍提供只会报“未构建”的入口
- 严重度:低 | 置信度:已确认
- 位置:
crates/ely-platform/src/print.rs:87-89(打印,各文档应用“文件›打印…”与 Cmd+P 照常)、crates/ely-platform/src/trust.rs:23(PDF 签名对话框)、crates/ely-platform/src/media.rs:52(ElyShow 插入媒体)、crates/ely-files/src/rename.rs:63(改名) - 修法:按
cfg或能力标志禁用菜单项,或补齐平台实现。
X-14 agents.md 引用的 docs/engineering-risks.md 不存在
- 严重度:低 | 置信度:已确认
- 位置:
agents.md(三处引用“证据见docs/engineering-risks.mdR-01”等);docs/下无此文件 - 修法:补回文件或改引用。