diff --git a/apps/client/src/widgets/type_widgets/code/Code.tsx b/apps/client/src/widgets/type_widgets/code/Code.tsx index 4f34edd96b..0f99d37e07 100644 --- a/apps/client/src/widgets/type_widgets/code/Code.tsx +++ b/apps/client/src/widgets/type_widgets/code/Code.tsx @@ -2,6 +2,7 @@ import "./code.css"; import { default as VanillaCodeMirror, getThemeById } from "@triliumnext/codemirror"; import { NoteType } from "@triliumnext/commons"; +import { RefObject } from "preact"; import { useEffect, useRef, useState } from "preact/hooks"; import appContext, { CommandListenerData } from "../../../components/app_context"; @@ -31,6 +32,8 @@ export interface EditableCodeProps extends TypeWidgetProps, Omit void; placeholder?: string; + /** Optional external ref to the underlying CodeMirror `EditorView`. Populated once the editor has initialized. */ + editorRef?: RefObject; } export function ReadOnlyCode({ note, viewScope, ntxId, parentComponent }: TypeWidgetProps) { @@ -81,8 +84,9 @@ function formatViewSource(note: FNote, content: string) { return content; } -export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentComponent, updateInterval, noteType = "code", onContentChanged, dataSaved, placeholder, ...editorProps }: EditableCodeProps) { - const editorRef = useRef(null); +export function EditableCode({ note, ntxId, noteContext, debounceUpdate, parentComponent, updateInterval, noteType = "code", onContentChanged, dataSaved, placeholder, editorRef: externalEditorRef, ...editorProps }: EditableCodeProps) { + const internalEditorRef = useRef(null); + const editorRef = externalEditorRef ?? internalEditorRef; const containerRef = useRef(null); const [ vimKeymapEnabled ] = useTriliumOptionBool("vimKeymapEnabled"); const [ noteTabWidth ] = useNoteLabelInt(note, "tabWidth"); diff --git a/apps/client/src/widgets/type_widgets/code/Markdown.css b/apps/client/src/widgets/type_widgets/code/Markdown.css index 7392c3e5f8..fdfe0dcfff 100644 --- a/apps/client/src/widgets/type_widgets/code/Markdown.css +++ b/apps/client/src/widgets/type_widgets/code/Markdown.css @@ -3,12 +3,11 @@ padding-block: 0.25em; } - .note-detail-split-preview { + .markdown-preview { + box-sizing: border-box; + height: 100%; overflow: auto; - - .markdown-preview { - padding: 0.5em; - } + padding: 0.5em; } } diff --git a/apps/client/src/widgets/type_widgets/code/Markdown.tsx b/apps/client/src/widgets/type_widgets/code/Markdown.tsx index b42162a0b5..3d6e1ab2a5 100644 --- a/apps/client/src/widgets/type_widgets/code/Markdown.tsx +++ b/apps/client/src/widgets/type_widgets/code/Markdown.tsx @@ -15,13 +15,15 @@ export default function Markdown(props: TypeWidgetProps) { const [ content, setContent ] = useState(""); const html = useMemo(() => DOMPurify.sanitize(renderWithSourceLines(content), { ADD_ATTR: [ "data-source-line" ] }), [ content ]); const previewRef = useRef(null); + const editorRef = useRef(null); - useSyncedScrolling(previewRef); + useSyncedScrolling(editorRef, previewRef); return ( ) { +function useSyncedScrolling(editorRef: RefObject, previewRef: RefObject) { useEffect(() => { - let rafId = 0; - let scroller: HTMLElement | null = null; - let cmEditor: HTMLElement | null = null; - let preview: HTMLElement | null = null; + const view = editorRef.current; + const preview = previewRef.current; + if (!view || !preview) return; + + const scroller = view.scrollDOM; function onScroll() { - if (!scroller || !cmEditor || !preview) return; - const view = VanillaCodeMirror.findFromDOM(cmEditor); - if (!view) return; - + if (!view || !preview) return; const topLine = view.state.doc.lineAt(view.lineBlockAtHeight(scroller.scrollTop).from).number; - const blocks = previewRef.current?.querySelectorAll("[data-source-line]"); - if (!blocks?.length) return; + const blocks = preview.querySelectorAll("[data-source-line]"); + if (!blocks.length) return; let before: HTMLElement | null = null; let after: HTMLElement | null = null; @@ -81,24 +81,9 @@ function useSyncedScrolling(previewRef: RefObject) { preview.scrollTop = beforeOffset + (afterOffset - beforeOffset) * ratio; } - function tryAttach() { - const split = previewRef.current?.closest(".note-detail-split"); - scroller = split?.querySelector(".cm-scroller") ?? null; - cmEditor = split?.querySelector(".cm-editor") ?? null; - preview = split?.querySelector(".note-detail-split-preview") ?? null; - if (!scroller || !cmEditor || !preview) { - rafId = requestAnimationFrame(tryAttach); - return; - } - scroller.addEventListener("scroll", onScroll, { passive: true }); - } - tryAttach(); - - return () => { - cancelAnimationFrame(rafId); - scroller?.removeEventListener("scroll", onScroll); - }; - }, [ previewRef ]); + scroller.addEventListener("scroll", onScroll, { passive: true }); + return () => scroller.removeEventListener("scroll", onScroll); + }, [ editorRef, previewRef ]); } /**