Files
new-markdown/docs/31-图片编辑体验.md
Muse 492051cef0 feat(editor): 图片右键换精简专用菜单(docs/31)
用户要求去掉多余菜单。右键命中图片时只出:超链接…/调整图片
大小…/上传到 S3(未上传图才有)/剪切/复制/粘贴;加粗、斜体、
删除线、行内代码等文本格式项与全选、源码模式对图片没意义,
不再出现(文本右键菜单不变)。配套:右键图片先把它设为节点
选区,描边/手柄随之出现,剪切/复制/Delete 都作用在这张图上。
vitest 153+2、构建通过。
2026-11-15 13:49:19 +08:00

5.6 KiB
Raw Permalink Blame History

31 图片编辑体验(选中 / 换行 / 大小 / 链接)

用户真机反馈三件事:①右键图片无法编辑大小;②右键图片点「超链接」是 空的;③图片无法选中、没法在图片后面输入文本(按回车只能在前面换行)。

③ 选中与换行(根因)

无头 Chrome 实测复现(真实鼠标点击):

  • 点击图片本身能形成 NodeSelection、点图片右侧也能把光标落在图片后—— 但选中态没有任何视觉提示(PM 给的 ProseMirror-selectednode 类没有样式),用户感知就是「选不中」。
  • 真正的死结在 Enter:图片被选中时按 Enter,默认命令链走 splitBlock, 空段落插到图片前面、选区仍停在图片上;接着打字会把图片替换掉。 这就是「只能在前面的文本处换行」的由来。

修法(createImageEnterPlugin,plugins.ts):

  • 插件 view 生命周期里给编辑器 DOM 挂捕获阶段 keydown 监听(预置 Enter keymap 插件排在本插件之前,props.handleKeyDown 轮不到); 只接管「图片 NodeSelection + 无修饰 Enter」:在图片所在文本块之后 插入空段落并把光标移进去,其余 Enter 全部放行。
  • 注意 createParagraphNear 用不上:它对行内节点直接返回 false (只处理块级选区),故插入逻辑自己写(resolve 到文本块深度、 after(depth) 处插段落)。
  • CSS 给 img.ProseMirror-selectednode 加 2px 主题色描边,选中可见。

实测(无头 Chrome,真实点击):点击→NodeSelection+描边;Enter→新段落 在图片之后、光标在其中;打字落在新段落、图片完好。

① 图片大小

Markdown 没有尺寸语法,通行写法是 HTML。方案(imageSchema.ts):

  • 包裹 preset 的 imageSchema:节点加 width 属性(字符串,300=px、 50%=百分比)。
  • 解析:remark 插件(条目形状必须是 { plugin, options },裸函数不 生效)把孤立的 <img …> mdast html 节点换成 image 节点,width 存 data;根级(块级)要包一层 paragraph,行内原地替换。属性值做一遍 实体解码(mdast 保留源码原文,如 &quot;)。
  • 序列化:有 width 时发 <img src alt title width> html 节点(属性 转义),无 width 时仍发标准 ![alt](src),普通文档零影响。
  • NodeView 按 width 设置 img 的 CSS 宽度(纯数字补 px)。
  • UI:右键图片 →「调整图片大小…」弹窗(数值 + 像素/百分比分段钮, 可恢复原始大小)。带尺寸的图片在源文件里就是 HTML 形式,弹窗里 有明示。

往返实测:<img src="b.png" alt="乙" width="300"> 解析/序列化逐字 一致;普通图片、非 img 的 HTML 均不受影响(tests/image-size.test.ts)。

拖拽缩放(同日用户追问后补):NodeView 改为包装结构 span.img-wrap > img + span.img-resize-handle,图片选中时右下角出现 手柄,拖动实时预览(只改 CSS 宽度),松手才提交节点属性(px 值, 上限为段落宽度、下限 20px)。两个坑:

  • PM 用 mouse 事件:手柄上的 mousedown/mouseup/click 要吞掉,否则 PM 会按点击位置捣乱;
  • setNodeMarkup 提交后 PM 会按 DOM 选区回读、把 NodeSelection 塌 成文本光标(手柄随之消失、无法连续拖)——提交事务里必须同带 setSelection(NodeSelection) 把选区设回去; updateImageAtElement(弹窗改尺寸/链接)同理保选区。 无头 Chrome 真实拖拽实测:240 → 拖 +80 得 width="320"、再拖 −30 得 "290",选区全程不丢。

② 图片超链接

原「超链接」是文本链接:对选区套 link 标记,预填只有 https://, 对着图片点开自然是「空的」。图片的链接在 Markdown 里是包裹式 [![alt](src)](href)——实测这在本编辑器里可往返(link 标记挂在 图片节点上,解析/序列化都保留)。

修法(第一版理解偏了):先做了「预填包裹链接」的弹窗,用户随即 指出「没有带出来这张图片的 url」——用户要的是图片自己的地址 出现在弹窗里。更正后的 ImageLinkModal:主字段「图片地址」打开 即带出该图的 src(本地相对路径或网络地址),可查看可改,提交后 patch 到节点;包裹链接降为第二字段「点击时打开的链接(可空)」, 提交时一条事务同时处理 src 与 link 标记(updateImageAtElement)。 文本选区的链接流程不变。

配套的编辑器接口(EditorPaneApi):imageInfoForElement(读 src/alt/width/linkHref)、updateImageForElement(按元素定位节点 改属性,右键菜单打开时记住元素与标签 id)。

图片右键菜单精简(同日用户要求)

右键命中图片时换成专用小菜单,只留与图片有关的项: 超链接… / 调整图片大小… / 上传到 S3(未上传图才有)/ 分隔线 / 剪切 / 复制 / 粘贴。加粗、斜体、删除线、行内代码这些文本格式项, 以及全选、源码模式切换,对图片没有意义,一律不出现(文本右键菜单 不变)。配套:右键图片时先把该图片设为节点选区 (selectImageAtElement),描边与手柄随之出现,菜单里的剪切/复制 和随后的 Delete 都作用在这张图上,与常见编辑器一致。

待真机验

  • WebView2 里点击图片的选中手感(本机为 Chromium 实测);
  • 调整大小后图片在文档中的实际观感与保存后重开的还原;
  • 图片在列表/引用/表格里时 Enter 新段落的位置是否符合预期。