From 6769570de31daedf5d7cef35db2c00b9c7d235e6 Mon Sep 17 00:00:00 2001 From: PJ Date: Sat, 20 Jun 2026 12:46:19 -0400 Subject: [PATCH] feat(pdf): wire live Typst PDF dock with HTML fallback --- src/components/Dock.tsx | 52 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/components/Dock.tsx b/src/components/Dock.tsx index 92cbe2b..79a9a69 100644 --- a/src/components/Dock.tsx +++ b/src/components/Dock.tsx @@ -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 = { "6x9": "6 × 9 in", @@ -10,11 +13,56 @@ const TRIM_LABEL: Record = { a5: "A5", }; +const inTauri = typeof window !== "undefined" && "__TAURI_INTERNALS__" in window; + export function Dock() { + return inTauri ? : ; +} + +function PdfDock() { + const book = useBook((s) => s.book); + const [pdf, setPdf] = useState(null); + const [error, setError] = useState(null); + const [busy, setBusy] = useState(false); + const timer = useRef>(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 ( +
+
+ Preview + {busy ? "updating…" : TRIM_LABEL[book.settings.trim]} +
+ {error ? ( +
{error}
+ ) : pdf ? ( + + ) : ( +
Typesetting…
+ )} +
+ ); +} + +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]);