diff --git a/apps/web/src/components/MermaidCodeBlock.tsx b/apps/web/src/components/MermaidCodeBlock.tsx index bae7ea04..db684990 100644 --- a/apps/web/src/components/MermaidCodeBlock.tsx +++ b/apps/web/src/components/MermaidCodeBlock.tsx @@ -247,6 +247,9 @@ export const MermaidCodeBlock = ({ editor, node }: NodeViewProps) => { 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(); + }); +}); diff --git a/apps/web/src/lib/code-block.ts b/apps/web/src/lib/code-block.ts index a884f651..816af1ff 100644 --- a/apps/web/src/lib/code-block.ts +++ b/apps/web/src/lib/code-block.ts @@ -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); },