feat(editor): 图片可选中换行、可调大小、右键链接针对图片(docs/31)
用户反馈三件:图片选不中没法在后面打字、右键不能改大小、图片的 超链接是空的。无头 Chrome 真实点击复现根因:点击能形成节点选区 但无任何视觉提示;图片选中时按 Enter 走 splitBlock,新段落插到 图片前面且选区仍停在图片上,接着打字会把图片替换掉。 - Enter 修复:捕获阶段 keydown 只接管图片选区,在图片所在文本块后 插空段落并移入光标(createParagraphNear 对行内节点无效,自己写); 选中图片加主题色描边。 - 大小:图片节点加 width 属性,带尺寸时序列化为 <img … width>、 否则标准语法;remark 插件把孤立 <img> 解析回图片节点(实体解码、 块级包段落),往返逐字一致;右键「调整图片大小…」弹窗 px/% 可调。 - 链接:右键图片的「超链接…」改为图片模式,预填包裹链接、提交对 图片节点挂/摘 link 标记([](href) 可往返)。 验证:vitest 148+2(新增 image-size/image-enter 共 12 例)、构建通过、 无头 Chrome 实测点击/Enter/打字全流程。
This commit is contained in:
44
tests/image-enter.test.ts
Normal file
44
tests/image-enter.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { editorViewCtx } from '@milkdown/kit/core'
|
||||
import { NodeSelection } from '@milkdown/kit/prose/state'
|
||||
import { createMarkdownEditor, getMarkdown } from '../src/editor/editor'
|
||||
|
||||
describe('图片选中时按 Enter(docs/31)', () => {
|
||||
it('在图片段落后新建空段落并把光标移入,图片保留', async () => {
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
const editor = createMarkdownEditor(root, '前文\n\n\n\n后文\n')
|
||||
await editor.create()
|
||||
const after = editor.action((ctx) => {
|
||||
const view = ctx.get(editorViewCtx)
|
||||
let pos = -1
|
||||
view.state.doc.descendants((n, p) => { if (n.type.name === 'image') pos = p; return true })
|
||||
view.dispatch(view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)))
|
||||
view.dom.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true }))
|
||||
const blocks: string[] = []
|
||||
view.state.doc.forEach((n) => blocks.push(n.type.name + ':' + n.textContent + (n.content.size && n.firstChild?.type.name === 'image' ? '[img]' : '')))
|
||||
return { blocks, selType: view.state.selection.constructor.name, selFrom: view.state.selection.from }
|
||||
})
|
||||
expect(after.blocks).toEqual(['paragraph:前文', 'paragraph:[img]', 'paragraph:', 'paragraph:后文'])
|
||||
expect(after.selType).toContain('TextSelection')
|
||||
expect(getMarkdown(editor)).toContain('')
|
||||
await editor.destroy(); root.remove()
|
||||
})
|
||||
|
||||
it('普通文本处按 Enter 不被接管(仍是原有分段行为)', async () => {
|
||||
const root = document.createElement('div')
|
||||
document.body.appendChild(root)
|
||||
const editor = createMarkdownEditor(root, '你好世界\n')
|
||||
await editor.create()
|
||||
editor.action((ctx) => {
|
||||
const view = ctx.get(editorViewCtx)
|
||||
// 光标放文本中间(非图片选区),捕获监听应放行——这里只断言监听不误伤:
|
||||
// jsdom 里默认 Enter 链未必完整执行,至少文档不能被插入多余段落到图片逻辑位置
|
||||
view.dom.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true, cancelable: true }))
|
||||
return null
|
||||
})
|
||||
// 不抛错、编辑器仍可用即可(默认链行为由无头浏览器实测覆盖)
|
||||
expect(getMarkdown(editor)).toContain('你好世界')
|
||||
await editor.destroy(); root.remove()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user