import { useCallback, useEffect, useState } from "react"; import type { Editor as TiptapEditor } from "@tiptap/react"; import { Sidebar } from "./Sidebar"; import { Dock } from "./Dock"; import { Icon } from "./Icon"; import { Settings } from "./Settings"; import { Editor } from "../editor/Editor"; import { FloatingToolbar } from "../editor/FloatingToolbar"; import { useBook } from "../store/useBook"; import type { Book } from "../model/book"; import { saveBook } from "../library"; import { isDesktop } from "../ipc"; import { exportEpub, exportPdf } from "../export/exporters"; const DESKTOP_ONLY = 'Export runs in the desktop app only — open the window from "pnpm tauri dev" (you are viewing the browser preview).'; export function EditorView() { const book = useBook((s) => s.book); const activeChapterId = useBook((s) => s.activeChapterId); const dirty = useBook((s) => s.dirty); const setChapterContent = useBook((s) => s.setChapterContent); const setChapterTitle = useBook((s) => s.setChapterTitle); const markSaved = useBook((s) => s.markSaved); const [editor, setEditor] = useState(null); const [dock, setDock] = useState(true); const [settingsOpen, setSettingsOpen] = useState(false); const [exportOpen, setExportOpen] = useState(false); const [notice, setNotice] = useState(null); const saveNow = useCallback(() => { const current = useBook.getState().book; if (current) saveBook(current).then(markSaved).catch((e) => setNotice(`Save failed: ${e}`)); }, [markSaved]); useEffect(() => { if (!book || !dirty || !isDesktop) return; const timer = setTimeout(saveNow, 800); return () => clearTimeout(timer); }, [book, dirty, saveNow]); useEffect(() => { const onKey = (e: KeyboardEvent) => { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "s") { e.preventDefault(); saveNow(); } }; window.addEventListener("keydown", onKey); return () => window.removeEventListener("keydown", onKey); }, [saveNow]); useEffect(() => { if (!notice) return; const timer = setTimeout(() => setNotice(null), 4000); return () => clearTimeout(timer); }, [notice]); const runExport = async (label: string, fn: (book: Book) => Promise) => { setExportOpen(false); if (!isDesktop) { setNotice(DESKTOP_ONLY); return; } const current = useBook.getState().book; if (!current) return; try { await fn(current); } catch (e) { setNotice(`${label} export failed: ${e}`); } }; if (!book) return null; const idx = book.chapters.findIndex((c) => c.id === activeChapterId); const chapter = book.chapters[idx] ?? book.chapters[0]; return (
{exportOpen && ( <>
setExportOpen(false)} />
)}
Chapter {idx + 1}
setChapterTitle(chapter.id, e.target.value)} />
setChapterContent(chapter.id, content)} onReady={setEditor} />
{dock && }
{settingsOpen && setSettingsOpen(false)} onSave={saveNow} />} {notice && (
setNotice(null)}> {notice}
)}
); }