fix(sheet): align cell editor with rendered cells
**PR:**fix(sheet): align cell editor with rendered cells (#4124)
source_automation: “voyager-merged-pr” run_date: “2026-07-03” anchor_pr_number: 4124 pr_number: 4124 pr_title: “fix(sheet): align cell editor with rendered cells” pr_url: “https://github.com/adastralab-ai/voyager/pull/4124 ↗” author: “Whitewater / lawvs” merged_at: “2026-07-02T14:27:20Z” modules: [“packages/sheet/src/overlay/cells”,“packages/sheet/src/editors”,“packages/docs/src/core”] files_changed: 5 learning_tags: [“sheet-editor”,“coordinate-model”,“prose-mirror”,“viewport-scale”,“editor-overlay”,“regression-test”] business_line: null related_prs: [4217] line_stage: null open_questions: [] feishu_doc_url: “https://my.feishu.cn/docx/D8OAdb56DoXBKHxR9zscLXHpnkf ↗”#
正文来源:飞书学习文档 ↗。以下为通过个人 Feishu API 获取并转换后的完整 Markdown 正文。
今日选择#
**PR:**fix(sheet): align cell editor with rendered cells (#4124)
**作者:**Whitewater / lawvs
**Merge 时间:**2026-07-02T14:27:20Z(2026-07-03 00:27:20 Australia/Melbourne)
链接:https://github.com/adastralab-ai/voyager/pull/4124 ↗
**模块:**packages/sheet/src/overlay/cells, packages/sheet/src/editors, packages/docs/src/core
**学习标签:**sheet-editor, coordinate-model, prose-mirror, viewport-scale, editor-overlay, regression-test
为什么值得学#
- 它修的是 sheet 单元格进入编辑态时的“文字跳动”,但真正改动点是布局模型:编辑层现在用和渲染单元格一致的 scaled coordinate model,而不是混合 scaled position 和自身 scale。
- 它把 editable content 和 selection border 分离。选择框不再作为内容盒子的 border 参与几何计算,避免 border width、padding、scale 一起影响编辑器内容位置。
- 它把 ProseMirror 编辑器字体上下文拉回 cell wrapper:.ProseMirror.editor-sheet 继承 font-family/font-size/line-height,减少显示态和编辑态之间的指标漂移。
- 测试没有验证截图或样式细节,而是锁定坐标模型的关键不变量:非首行/首列 + zoom=1.25 时,编辑 wrapper 的 transform 必须是 translate(100px, 37.5px) scale(1.25)。
关键代码#
1. 坐标先缩放,再组合为同一个 transform#
const scaledLeft = left * scale;
const scaledTop = top * scale;
const scaledWidth = width * scale;
const scaledHeight = height * scale;plaintext这是 packages/sheet/src/overlay/cells/CellEditor.tsx:61-64。代码先把 layout-space 的 cell metrics 转成 viewport-space,再用于内容层和边框层。设计点是明确区分未缩放的 cell 尺寸和缩放后的屏幕坐标。
2. 编辑内容层只负责内容,不背负选择框几何#
<div
className="pointer-events-auto absolute top-0 left-0 flex origin-top-left bg-bg p-2 text-[14px]"
style={{
width,
height,
background: cell?.styles?.background,
alignItems: verticalAlignment,
textAlign: alignment,
transform: `translate(${scaledLeft}px, ${scaledTop}px) scale(${scale})`,
}}
>plaintext这是 packages/sheet/src/overlay/cells/CellEditor.tsx:103-112。内容层保留未缩放 width/height,但 transform 使用缩放后的 left/top 再 scale。这样 ProseMirror 内容和显示 cell 的坐标系对齐,padding 和文本布局不会被外层 border 算法污染。
3. 选择框成为独立 overlay#
<div
className="pointer-events-none absolute border-2 border-solid border-editor-selection"
style={{
transform: `translate(${scaledLeft - editorBorderOffset}px, ${scaledTop - editorBorderOffset}px)`,
width: scaledWidth + EDITOR_BORDER_WIDTH,
height: scaledHeight + EDITOR_BORDER_WIDTH,
}}
/>plaintext这是 packages/sheet/src/overlay/cells/CellEditor.tsx:139-145。选择框在 viewport-space 里单独画,不接收 pointer,也不改变 editable content 的盒模型。这个边界拆分比继续调 BORDER_DELTA 更稳。
4. ProseMirror 继承 sheet wrapper 的字体指标#
.ProseMirror.editor-sheet {
font-family: inherit;
font-size: inherit;
line-height: inherit;
}plaintext这是 packages/docs/src/core/base.sheet.css:2-6。编辑器内部不再自带一套可能偏离 cell wrapper 的字体指标,让“进入编辑态不跳动”不只依赖坐标,还依赖文本测量上下文一致。
和最近学习记录的关系#
本地日志里只有 2026-07-02 学过的 #4156 worker-token PR,主题是 backend callback authz / workspace scope,和今天的 sheet editor 坐标模型没有直接模块或 API 关系。
不过今天候选里的 #4217 是 #4124 的直接后续链路:#4124 先让 cell editor 的坐标和文本指标稳定,#4217 再把 formula bar click 的 viewport coordinates 传给 RichTextEditor,让点击位置能落到接近的文本光标位置。二者都围绕 sheet editing 的“用户点击坐标如何进入 ProseMirror 选择状态”。
我会怎么吸收#
- 处理 overlay/editor 这类 UI bug 时,先命名坐标空间:layout-space、viewport-space、editor-local-space。不要让一个 DOM 节点同时承担内容布局、缩放、边框绘制和交互捕获。
- 视觉边框、selection chrome、resize handle 这类 UI 装饰应尽量独立 overlay,避免进入内容盒模型。这样后续改 border/padding 不会改变文本位置。
- 测试应锁住工程不变量,而不是截图式验证。这里选择“leading row/column + scale=1.25”的 case,比 top-left/scale=1 更能防止坐标模型退化。
边界/风险#
未看到明显风险。主要残余风险是 DOM/CSS 继承链仍可能被其他上层样式影响,尤其是字体 fallback、line-height 和 ProseMirror 内部节点样式;PR 已通过 base.sheet.css 和坐标测试降低了这个风险,但如果未来引入更多富文本样式,仍需要用实际渲染/浏览器层面的回归覆盖。
候选说明#
按 Australia/Melbourne 的 2026-07-03,当天窗口是 2026-07-02T14:00:00Z 到 2026-07-03T14:00:00Z。今天看到 2 个已合入 main 且未记录过的候选:#4124 和 #4217。最终选择 #4124,因为它建立底层编辑器坐标模型,#4217 更像在这个链路上的后续坐标传递增量。昨天窗口未用于最终选择;去重只跳过了日志里已有的 #4156。