diff --git a/public/assets/css/editor.css b/public/assets/css/editor.css index d3dbd80d..6a902efa 100644 --- a/public/assets/css/editor.css +++ b/public/assets/css/editor.css @@ -131,6 +131,10 @@ div[data-lexical-editor] { &::highlight(inline-code-sigil) { color: var(--alt-content-color); } + + &.script-block { + font-size: var(--small-font-size); + } } .quote-block { @@ -205,6 +209,7 @@ div[data-lexical-editor] { margin: 0; white-space: pre-wrap; overflow-wrap: break-word; + font-family: inherit; } .script-output-status { diff --git a/src/lib/editor/plugins/ProgrammableNotePlugin.ts b/src/lib/editor/plugins/ProgrammableNotePlugin.ts index b4d4cde6..1fa2c921 100644 --- a/src/lib/editor/plugins/ProgrammableNotePlugin.ts +++ b/src/lib/editor/plugins/ProgrammableNotePlugin.ts @@ -39,6 +39,42 @@ const DEBOUNCE_MS = 500; const PROGRAMMABLE_NOTE_TAG = "programmable-note"; +const SCRIPT_BLOCK_CLASS = "script-block"; + +const getScriptBlockNodeKeys = ( + children: LexicalNode[], +): Set => { + const keys = new Set(); + let i = 0; + + while (i < children.length) { + const child = children[i]; + if ( + $isCodeBlockNode(child) + && child.getTextContent().startsWith("```") + && child.getTextContent().substring(3).trim() === "run" + ) { + keys.add(child.getKey()); + i++; + + while (i < children.length) { + const inner = children[i]; + keys.add(inner.getKey()); + if ( + $isCodeBlockNode(inner) + && inner.getTextContent().trimEnd() === "```" + ) { + break; + } + i++; + } + } + i++; + } + + return keys; +}; + const extractScriptBlocks = ( children: LexicalNode[], ): ScriptBlock[] => { @@ -370,5 +406,39 @@ export default function ProgrammableNotePlugin({ }; }, [editor]); + useEffect(() => { + const applyScriptBlockClasses = () => { + const { scriptKeys, allCodeBlockKeys } + = editor.getEditorState().read(() => { + const root = $getRoot(); + const children = root.getChildren(); + return { + scriptKeys: getScriptBlockNodeKeys(children), + allCodeBlockKeys: children + .filter((c) => $isCodeBlockNode(c)) + .map((c) => c.getKey()), + }; + }); + + for (const key of allCodeBlockKeys) { + const el = editor.getElementByKey(key); + if (el) { + el.classList.toggle( + SCRIPT_BLOCK_CLASS, + scriptKeys.has(key), + ); + } + } + }; + + const unregister = editor.registerUpdateListener(() => { + applyScriptBlockClasses(); + }); + + applyScriptBlockClasses(); + + return unregister; + }, [editor]); + return null; } diff --git a/tests/visual-regression/editor.spec.ts b/tests/visual-regression/editor.spec.ts index f07b14ff..9a5b3578 100644 --- a/tests/visual-regression/editor.spec.ts +++ b/tests/visual-regression/editor.spec.ts @@ -311,6 +311,40 @@ test.describe("Editor", () => { .toMatchSnapshot("editor-note-stats-with-files-light.png"); }); + test("inline script - light", async ({ page }) => { + page.emulateMedia({ colorScheme: "light" }); + const editor = page.locator("div[data-lexical-editor]"); + await expect(editor).toBeFocused(); + await editor.fill( + "# Note with inline script\n" + + "Some paragraph text\n" + + "```run\n" + + "println(\"Hello from script\")\n" + + "```\n" + + "Another paragraph", + ); + await page.locator(".script-output-content").waitFor({ timeout: 10000 }); + expect(await page.locator(".note").screenshot()) + .toMatchSnapshot("editor-inline-script-light.png"); + }); + + test("inline script - dark", async ({ page }) => { + page.emulateMedia({ colorScheme: "dark" }); + const editor = page.locator("div[data-lexical-editor]"); + await expect(editor).toBeFocused(); + await editor.fill( + "# Note with inline script\n" + + "Some paragraph text\n" + + "```run\n" + + "println(\"Hello from script\")\n" + + "```\n" + + "Another paragraph", + ); + await page.locator(".script-output-content").waitFor({ timeout: 10000 }); + expect(await page.locator(".note").screenshot()) + .toMatchSnapshot("editor-inline-script-dark.png"); + }); + test("modal - light", async ({ page }) => { page.emulateMedia({ colorScheme: "light" }); diff --git a/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-dark-chromium-darwin.png b/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-dark-chromium-darwin.png new file mode 100644 index 00000000..51818c95 Binary files /dev/null and b/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-dark-chromium-darwin.png differ diff --git a/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-light-chromium-darwin.png b/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-light-chromium-darwin.png new file mode 100644 index 00000000..88e58c9e Binary files /dev/null and b/tests/visual-regression/editor.spec.ts-snapshots/editor-inline-script-light-chromium-darwin.png differ