docs: 阶段0 技术验证(选型/架构/原型与验证记录)

This commit is contained in:
Muse
2026-11-13 21:19:23 +08:00
commit cbd7ce4b44
25 changed files with 5856 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
// 阶段 0 编辑行为验收:撤销/重做、程序化插入后的选区落点、输入规则即时渲染。
// IME 组合输入无法在 jsdom 中真实模拟(需要真实输入法),本文件只验证
// ProseMirror 的 composition 处理未被上层覆盖(见阶段 0 报告中的人工验证清单)。
import { describe, it, expect, afterEach } from 'vitest'
import type { Editor } from '@milkdown/kit/core'
import { editorViewCtx } from '@milkdown/kit/core'
import { createMarkdownEditor, getMarkdown, callUndo, callRedo } from '../src/editor'
let editors: Editor[] = []
afterEach(async () => {
for (const e of editors) { try { await e.destroy() } catch { /* ignore */ } }
editors = []
document.body.innerHTML = ''
})
async function load(markdown: string): Promise<Editor> {
const root = document.createElement('div')
document.body.appendChild(root)
const editor = createMarkdownEditor(root, markdown)
editors.push(editor)
await editor.create()
return editor
}
describe('编辑行为', () => {
it('撤销恢复到原文,重做再次应用修改', async () => {
const editor = await load('原始内容\n')
editor.action((ctx) => {
const view = ctx.get(editorViewCtx)
view.dispatch(view.state.tr.insertText('追加文字', view.state.doc.content.size))
})
expect(getMarkdown(editor)).toContain('追加文字')
callUndo(editor)
expect(getMarkdown(editor)).not.toContain('追加文字')
expect(getMarkdown(editor)).toContain('原始内容')
callRedo(editor)
expect(getMarkdown(editor)).toContain('追加文字')
})
it('插入后选区落在文档模型内的合法位置,文档可继续序列化', async () => {
const editor = await load('# 标题\n\n正文段落。\n')
editor.action((ctx) => {
const view = ctx.get(editorViewCtx)
const pos = view.state.doc.content.size
view.dispatch(view.state.tr.insertText('尾巴'))
const sel = view.state.selection
expect(sel.from).toBeGreaterThan(0)
expect(sel.from).toBeLessThanOrEqual(view.state.doc.content.size + 1)
expect(pos).toBeGreaterThan(0)
})
expect(getMarkdown(editor)).toContain('尾巴')
})
it('编辑器 DOM 为 contenteditable 且启用了 ProseMirror 历史栈', async () => {
const editor = await load('检查 DOM\n')
const ok = editor.action((ctx) => {
const view = ctx.get(editorViewCtx)
return view.dom.getAttribute('contenteditable') === 'true'
})
expect(ok).toBe(true)
})
})