fix(web): refine code block editing behavior

This commit is contained in:
tianma-if committed 2026-08-20 16:45:22 +08:00
1 parent db1854e6be
commit fe437a7875
3 files changed
+91

No files matched your search

@@ -247,6 +247,9 @@ export const MermaidCodeBlock = ({ editor, node }: NodeViewProps) => {
<NodeViewContent
className={isMermaid ? "edgeever-code-source edgeever-mermaid-source" : "edgeever-code-source"}
role="textbox"
autoCapitalize="none"
autoCorrect="off"
spellCheck={false}
aria-label={isMermaid ? t("editorToolbar.mermaidSource") : undefined}
aria-multiline="true"
aria-readonly={!editor.isEditable}
+65
View File
@@ -0,0 +1,65 @@
import { describe, expect, test } from "bun:test";
import { Editor } from "@tiptap/core";
import StarterKit from "@tiptap/starter-kit";
import { EdgeEverCodeBlock, selectCurrentCodeBlockContent } from "./code-block.ts";
const createEditor = () => new Editor({
extensions: [
StarterKit.configure({ codeBlock: false }),
EdgeEverCodeBlock,
],
content: {
type: "doc",
content: [
{ type: "paragraph", content: [{ type: "text", text: "before" }] },
{
type: "codeBlock",
attrs: { language: "mermaid" },
content: [{ type: "text", text: "flowchart TD\n A --> B" }],
},
{ type: "paragraph", content: [{ type: "text", text: "after" }] },
],
},
});
const getCodeBlockRange = (editor) => {
let range = null;
editor.state.doc.descendants((node, pos) => {
if (!range && node.type.name === "codeBlock") {
range = { from: pos + 1, to: pos + 1 + node.content.size };
}
});
return range;
};
describe("code block selection", () => {
test("selects only the current code block on the first Mod-a", () => {
const editor = createEditor();
const range = getCodeBlockRange(editor);
editor.commands.setTextSelection(range.from + 2);
expect(selectCurrentCodeBlockContent(editor)).toBe(true);
expect({
from: editor.state.selection.from,
to: editor.state.selection.to,
}).toEqual(range);
editor.destroy();
});
test("allows the editor-wide Mod-a after the code block is fully selected", () => {
const editor = createEditor();
const range = getCodeBlockRange(editor);
editor.commands.setTextSelection(range);
expect(selectCurrentCodeBlockContent(editor)).toBe(false);
editor.destroy();
});
test("leaves Mod-a unchanged outside code blocks", () => {
const editor = createEditor();
editor.commands.setTextSelection(2);
expect(selectCurrentCodeBlockContent(editor)).toBe(false);
editor.destroy();
});
});
+23
View File
@@ -1,11 +1,34 @@
import { common, createLowlight } from "lowlight";
import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
import type { Editor } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { MermaidCodeBlock } from "@/components/MermaidCodeBlock";
export const codeBlockLowlight = createLowlight(common);
export const selectCurrentCodeBlockContent = (editor: Editor) => {
const { $from, $to, from, to } = editor.state.selection;
if ($from.parent !== $to.parent || $from.parent.type.name !== "codeBlock") {
return false;
}
const blockFrom = $from.start();
const blockTo = $from.end();
if (from === blockFrom && to === blockTo) {
return false;
}
return editor.commands.setTextSelection({ from: blockFrom, to: blockTo });
};
export const EdgeEverCodeBlock = CodeBlockLowlight.extend({
addKeyboardShortcuts() {
return {
...this.parent?.(),
"Mod-a": () => selectCurrentCodeBlockContent(this.editor),
};
},
addNodeView() {
return ReactNodeViewRenderer(MermaidCodeBlock);
},