feat(editor): 图片右键换精简专用菜单(docs/31)

用户要求去掉多余菜单。右键命中图片时只出:超链接…/调整图片
大小…/上传到 S3(未上传图才有)/剪切/复制/粘贴;加粗、斜体、
删除线、行内代码等文本格式项与全选、源码模式对图片没意义,
不再出现(文本右键菜单不变)。配套:右键图片先把它设为节点
选区,描边/手柄随之出现,剪切/复制/Delete 都作用在这张图上。
vitest 153+2、构建通过。
This commit is contained in:
Muse
2026-11-15 13:49:19 +08:00
parent 1c0dd00088
commit 492051cef0
7 changed files with 99 additions and 25 deletions

View File

@@ -52,6 +52,20 @@ describe('编辑区右键菜单', () => {
remote.container.remove()
})
it('命中图片时是精简专用菜单:无格式项/全选/源码模式,有超链接与调整大小', async () => {
const { container, root, actions } = await renderMenu({ imageSrc: 'https://cdn.example.com/a.png', hasSelection: true })
for (const t of ['加粗', '斜体', '删除线', '行内代码', '全选', '源码模式', '上传到 S3']) {
expect(btn(container, t)).toBeUndefined()
}
for (const t of ['超链接…', '调整图片大小…', '剪切', '复制', '粘贴']) {
expect(btn(container, t)).toBeTruthy()
}
await act(async () => { btn(container, '调整图片大小…')?.dispatchEvent(new MouseEvent('click', { bubbles: true })) })
expect(actions).toEqual(['imageSize'])
await act(async () => { root.unmount() })
container.remove()
})
it('源码模式只出编辑命令与「即时渲染」', async () => {
const { container, root } = await renderMenu({ sourceMode: true, hasSelection: true })
expect(btn(container, '加粗')).toBeUndefined()

View File

@@ -1,6 +1,7 @@
import { describe, it, expect } from 'vitest'
import { editorViewCtx } from '@milkdown/kit/core'
import { createMarkdownEditor, getMarkdown, imageInfoAtElement, updateImageAtElement } from '../src/editor/editor'
import { createMarkdownEditor, getMarkdown, imageInfoAtElement, updateImageAtElement, selectImageAtElement } from '../src/editor/editor'
import { NodeSelection } from '@milkdown/kit/prose/state'
import { parseImgTag, widthToCss } from '../src/editor/imageSchema'
async function makeEditor(md: string) {
@@ -69,6 +70,17 @@ describe('图片尺寸往返(docs/31)', () => {
})
})
describe('右键选中图片(docs/31)', () => {
it('selectImageAtElement 把图片设为节点选区', async () => {
const { root, editor } = await makeEditor('前文 ![图](a.png) 后文\n')
const img = root.querySelector('img')!
expect(selectImageAtElement(editor, img)).toBe(true)
const sel = editor.action((ctx) => ctx.get(editorViewCtx).state.selection)
expect(sel instanceof NodeSelection).toBe(true)
await editor.destroy(); root.remove()
})
})
describe('parseImgTag / widthToCss', () => {
it('只认孤立 img 且必须有 src', () => {
expect(parseImgTag('<img src="a.png" width="300">')).toEqual({ src: 'a.png', width: '300' })