Something went wrong. Try again.
[mirror] A local-first desktop markdown writer with grammar checking, at proto integration, and more github.com/stormlightlabs/writer/releases
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102import type { Cmd } from "$ports";import { none, renderMarkdown } from "$ports";import type { AppError, DocRef, RenderResult } from "$types";import { useCallback, useMemo } from "react";import { useCmdLoop } from "./useCmdLoop";
export type PreviewModel = { docRef: DocRef | null; renderResult: RenderResult | null; isLoading: boolean; error: AppError | null; syncLine: number;};
export type UsePreviewReturn = { model: PreviewModel; dispatch: (msg: PreviewMsg) => void; render: (docRef: DocRef, text: string) => void; syncLine: (line: number) => void; setDoc: (docRef: DocRef | null) => void;};
export const initialPreviewModel: PreviewModel = { docRef: null, renderResult: null, isLoading: false, error: null, syncLine: 1,};
export type PreviewMsg = | { type: "RenderRequested"; docRef: DocRef; text: string } | { type: "RenderFinished"; success: boolean; result?: RenderResult; error?: AppError } | { type: "ScrollToLine"; line: number } | { type: "SyncFromEditor"; line: number } | { type: "DocChanged"; docRef: DocRef | null };
function isPreviewMsg(value: unknown): value is PreviewMsg { return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";}
export function updatePreview(model: PreviewModel, msg: PreviewMsg): [PreviewModel, Cmd] { switch (msg.type) { case "RenderRequested": { return [ { ...model, docRef: msg.docRef, isLoading: true, error: null }, renderMarkdown( msg.docRef.location_id, msg.docRef.rel_path, msg.text, undefined, (result: RenderResult) => ({ type: "RenderFinished", success: true, result }), (error) => ({ type: "RenderFinished", success: false, error }), ), ]; }
case "RenderFinished": { return [{ ...model, renderResult: msg.success ? (msg.result ?? null) : null, isLoading: false, error: msg.error ?? null, }, none]; }
case "ScrollToLine": { return [{ ...model, syncLine: msg.line }, none]; }
case "SyncFromEditor": { return [{ ...model, syncLine: msg.line }, none]; }
case "DocChanged": { return [{ ...model, docRef: msg.docRef, renderResult: null, isLoading: false, error: null, syncLine: 1 }, none]; }
default: { return [model, none]; } }}
export function usePreview(): UsePreviewReturn { const { model, dispatch } = useCmdLoop(initialPreviewModel, updatePreview, isPreviewMsg);
const render = useCallback((docRef: DocRef, text: string) => { dispatch({ type: "RenderRequested", docRef, text }); }, [dispatch]);
const syncLine = useCallback((line: number) => { dispatch({ type: "SyncFromEditor", line }); }, [dispatch]);
const setDoc = useCallback((docRef: DocRef | null) => { dispatch({ type: "DocChanged", docRef }); }, [dispatch]);
return useMemo(() => ({ model, dispatch, render, syncLine, setDoc }), [model, dispatch, render, syncLine, setDoc]);}