mirror of
https://github.com/zadam/trilium.git
synced 2026-05-06 12:37:04 +02:00
feat(markdown): basic block highlighting in preview
This commit is contained in:
@@ -7,9 +7,19 @@
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
padding: 0.5em;
|
||||
padding: 0.5em 1em;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.markdown-preview [data-source-line] {
|
||||
border-left: 2px solid transparent;
|
||||
padding-left: 0.5em;
|
||||
margin-left: -0.5em;
|
||||
}
|
||||
|
||||
[data-source-line].markdown-preview-active {
|
||||
border-left-color: var(--main-text-color);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ export default function Markdown(props: TypeWidgetProps) {
|
||||
const editorRef = useRef<VanillaCodeMirror>(null);
|
||||
|
||||
useSyncedScrolling(editorRef, previewRef);
|
||||
useSyncedHighlight(editorRef, previewRef, html);
|
||||
|
||||
return (
|
||||
<SplitEditor
|
||||
@@ -86,6 +87,44 @@ function useSyncedScrolling(editorRef: RefObject<VanillaCodeMirror>, previewRef:
|
||||
}, [ editorRef, previewRef ]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlights the preview block that corresponds to the editor's active line,
|
||||
* matching the built-in `cm-activeLine` behavior. Re-runs when the rendered
|
||||
* HTML changes so newly inserted blocks pick up the current cursor position.
|
||||
*/
|
||||
function useSyncedHighlight(editorRef: RefObject<VanillaCodeMirror>, previewRef: RefObject<HTMLDivElement>, html: string) {
|
||||
useEffect(() => {
|
||||
const view = editorRef.current;
|
||||
const preview = previewRef.current;
|
||||
if (!view || !preview) return;
|
||||
|
||||
let current: HTMLElement | null = null;
|
||||
|
||||
function update() {
|
||||
if (!view || !preview) return;
|
||||
const activeLine = view.state.doc.lineAt(view.state.selection.main.head).number;
|
||||
|
||||
const blocks = preview.querySelectorAll<HTMLElement>("[data-source-line]");
|
||||
let match: HTMLElement | null = null;
|
||||
for (const el of blocks) {
|
||||
if (parseInt(el.dataset.sourceLine!, 10) <= activeLine) match = el;
|
||||
else break;
|
||||
}
|
||||
|
||||
if (match === current) return;
|
||||
current?.classList.remove("markdown-preview-active");
|
||||
match?.classList.add("markdown-preview-active");
|
||||
current = match;
|
||||
}
|
||||
|
||||
update();
|
||||
const unsubscribe = view.addUpdateListener((v) => {
|
||||
if (v.selectionSet || v.docChanged) update();
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [ editorRef, previewRef, html ]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render markdown and tag each top-level block with its 1-indexed source line,
|
||||
* so the preview can be scrolled to match the editor. Marked does not emit
|
||||
|
||||
@@ -104,16 +104,14 @@ export default class CodeMirror extends EditorView {
|
||||
];
|
||||
}
|
||||
|
||||
extensions.push(EditorView.updateListener.of((v) => this.#onDocumentUpdated(v)));
|
||||
|
||||
if (!config.readOnly) {
|
||||
// Logic specific to editable notes
|
||||
if (config.placeholder) {
|
||||
extensions.push(placeholder(config.placeholder));
|
||||
}
|
||||
|
||||
if (config.onContentChanged) {
|
||||
extensions.push(EditorView.updateListener.of((v) => this.#onDocumentUpdated(v)));
|
||||
}
|
||||
|
||||
extensions.push(historyCompartment.of(history()));
|
||||
} else {
|
||||
// Logic specific to read-only notes
|
||||
@@ -142,6 +140,21 @@ export default class CodeMirror extends EditorView {
|
||||
if (v.docChanged) {
|
||||
this.config.onContentChanged?.();
|
||||
}
|
||||
for (const listener of this.#updateListeners) listener(v);
|
||||
}
|
||||
|
||||
#updateListeners: Array<(v: ViewUpdate) => void> = [];
|
||||
|
||||
/**
|
||||
* Subscribe to view updates (doc changes, selection changes, viewport changes, etc.).
|
||||
* Returns an unsubscribe function. The listener will not fire after the view is destroyed.
|
||||
*/
|
||||
addUpdateListener(listener: (v: ViewUpdate) => void): () => void {
|
||||
this.#updateListeners.push(listener);
|
||||
return () => {
|
||||
const i = this.#updateListeners.indexOf(listener);
|
||||
if (i >= 0) this.#updateListeners.splice(i, 1);
|
||||
};
|
||||
}
|
||||
|
||||
getText() {
|
||||
|
||||
Reference in New Issue
Block a user