feat(pdf): wire live Typst PDF dock with HTML fallback

This commit is contained in:
pj committed 2026-06-20 12:46:19 -04:00
1 parent 29af847179
commit 6769570de3
1 file changed
+50 -2
+50 -2
View File
@@ -1,7 +1,10 @@
import { useMemo } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import { generateHTML } from "@tiptap/core";
import { useBook } from "../store/useBook";
import { editorExtensions } from "../editor/extensions";
import { bookToPdfInputs } from "../export/typst";
import { compilePdf } from "../ipc";
import { PdfPreview } from "./PdfPreview";
const TRIM_LABEL: Record<string, string> = {
"6x9": "6 × 9 in",
@@ -10,11 +13,56 @@ const TRIM_LABEL: Record<string, string> = {
a5: "A5",
};
const inTauri = typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
export function Dock() {
return inTauri ? <PdfDock /> : <HtmlDock />;
}
function PdfDock() {
const book = useBook((s) => s.book);
const [pdf, setPdf] = useState<Uint8Array | null>(null);
const [error, setError] = useState<string | null>(null);
const [busy, setBusy] = useState(false);
const timer = useRef<ReturnType<typeof setTimeout>>(undefined);
useEffect(() => {
clearTimeout(timer.current);
setBusy(true);
timer.current = setTimeout(() => {
const { source, images } = bookToPdfInputs(book);
compilePdf(source, images)
.then((bytes) => {
setPdf(bytes);
setError(null);
})
.catch((e) => setError(String(e)))
.finally(() => setBusy(false));
}, 350);
return () => clearTimeout(timer.current);
}, [book]);
return (
<section className="dock">
<div className="dock-head">
<span className="label">Preview</span>
<span className="meta">{busy ? "updating…" : TRIM_LABEL[book.settings.trim]}</span>
</div>
{error ? (
<pre className="dock-error">{error}</pre>
) : pdf ? (
<PdfPreview data={pdf} />
) : (
<div className="dock-empty">Typesetting…</div>
)}
</section>
);
}
function HtmlDock() {
const chapters = useBook((s) => s.book.chapters);
const trim = useBook((s) => s.book.settings.trim);
const activeChapterId = useBook((s) => s.activeChapterId);
const idx = chapters.findIndex((c) => c.id === activeChapterId);
const chapter = chapters[idx];
const html = useMemo(() => generateHTML(chapter.content, editorExtensions), [chapter.content]);