52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
import type { EditorView } from '@codemirror/view';
|
|
|
|
interface Opts {
|
|
getView: () => EditorView | null;
|
|
}
|
|
|
|
/** Live markdown preview pane: visibility, mobile tab, debounced render. */
|
|
export function useLivePreview({ getView }: Opts) {
|
|
const [showPreview, setShowPreview] = useState(false);
|
|
const [mobileView, setMobileView] = useState<'edit' | 'preview'>('edit');
|
|
const previewRef = useRef<HTMLDivElement>(null);
|
|
const renderMarkdownRef = useRef<((src: string) => string) | null>(null);
|
|
const previewTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
const updatePreviewRef = useRef<() => void>(() => {});
|
|
|
|
const updatePreview = useCallback(async () => {
|
|
const view = getView();
|
|
if (!showPreview || !view || !previewRef.current) return;
|
|
if (!renderMarkdownRef.current) {
|
|
const mod = await import('../../../../lib/markdown');
|
|
renderMarkdownRef.current = mod.renderMarkdown;
|
|
}
|
|
const content = view.state.doc.toString();
|
|
previewRef.current.innerHTML = renderMarkdownRef.current(content);
|
|
}, [showPreview, getView]);
|
|
|
|
useEffect(() => {
|
|
updatePreviewRef.current = updatePreview;
|
|
}, [updatePreview]);
|
|
|
|
useEffect(() => {
|
|
if (showPreview) updatePreview();
|
|
}, [showPreview, updatePreview]);
|
|
|
|
// Debounced refresh — called from the CodeMirror update listener.
|
|
const schedulePreview = useCallback(() => {
|
|
if (previewTimerRef.current) clearTimeout(previewTimerRef.current);
|
|
previewTimerRef.current = setTimeout(() => updatePreviewRef.current(), 300);
|
|
}, []);
|
|
|
|
return {
|
|
showPreview,
|
|
setShowPreview,
|
|
mobileView,
|
|
setMobileView,
|
|
previewRef,
|
|
updatePreview,
|
|
schedulePreview,
|
|
};
|
|
}
|