fix(editor): runnable 块 classList.add 抛 InvalidCharacterError

CodeBlockNodeView 构造时把完整 info string 原样拼进 class
(`language-python runnable {...}`),classList.add 拒绝含空格 token,
导致编辑模式回填含 runnable 块的文章时崩溃(setMarkdown → addNode)。

改用 extractLang 只挂纯语言名 class。高亮靠 CodeBlockLowlight decoration,
本就不依赖此 class。补回归测试。
This commit is contained in:
xfy 2026-07-07 09:41:05 +08:00
parent 05460f1257
commit c8b2248066
2 changed files with 17 additions and 3 deletions

View File

@ -86,6 +86,17 @@ describe('CodeBlockNodeView', () => {
expect(view.contentDOM?.tagName).toBe('CODE'); expect(view.contentDOM?.tagName).toBe('CODE');
}); });
it('runnable 块的 code class 只含纯语言名(不抛 InvalidCharacterError)', () => {
// 回归:完整 info string `python runnable {...}` 含空格,
// classList.add 会拒绝含空格的 token。必须用 extractLang 提取首 token。
const view = new CodeBlockNodeView({
node: mockNode('python runnable {"timeout_secs":10}'),
editor: mockEditor(),
} as any);
expect(view.contentDOM?.classList.contains('language-python')).toBe(true);
expect(view.contentDOM?.classList.contains('runnable')).toBe(false);
});
it('ignoreMutation 返回 true(工具栏不触发编辑事务)', () => { it('ignoreMutation 返回 true(工具栏不触发编辑事务)', () => {
const view = new CodeBlockNodeView({ const view = new CodeBlockNodeView({
node: mockNode('python'), node: mockNode('python'),

View File

@ -73,9 +73,12 @@ export class CodeBlockNodeView {
// pre > codecontentDOMdecoration 在此生效) // pre > codecontentDOMdecoration 在此生效)
this.pre = document.createElement('pre'); this.pre = document.createElement('pre');
this.code = document.createElement('code'); this.code = document.createElement('code');
const lang = (this.node.attrs.language as string) ?? ''; // 只挂纯语言名的 classextractLang 提取首个 token
if (lang) { // 不能用完整 info string——`python runnable {...}` 含空格classList.add 会抛
this.code.classList.add(`language-${lang}`); // InvalidCharacterError。高亮靠 CodeBlockLowlight 的 decoration不依赖此 class。
const langClass = extractLang((this.node.attrs.language as string) ?? '');
if (langClass) {
this.code.classList.add(`language-${langClass}`);
} }
this.pre.appendChild(this.code); this.pre.appendChild(this.code);
this.container.appendChild(this.pre); this.container.appendChild(this.pre);