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 { CoverView } from "./CoverView"; import { Editor } from "../editor/Editor"; import { FloatingToolbar } from "../editor/FloatingToolbar"; import { COVER_ID, useBook } from "../store/useBook"; import { useTheme } from "../store/useTheme"; import { bodyNumber, chapterKind } from "../model/book"; import { saveBook } from "../library"; import { isDesktop } from "../ipc"; import { runExport } from "../export/run"; export function EditorView() { const book = useBook((s) => s.book); const activeChapterId = useBook((s) => s.activeChapterId); const dirty = useBook((s) => s.dirty); const exporting = useBook((s) => s.exporting); const notice = useBook((s) => s.notice); const setNotice = useBook((s) => s.setNotice); const setChapterContent = useBook((s) => s.setChapterContent); const setChapterTitle = useBook((s) => s.setChapterTitle); const markSaved = useBook((s) => s.markSaved); const theme = useTheme((s) => s.theme); const toggleTheme = useTheme((s) => s.toggle); const [editor, setEditor] = useState(null); const [dock, setDock] = useState(true); const [settingsOpen, setSettingsOpen] = useState(false); const [exportOpen, setExportOpen] = useState(false); 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 handleExport = (format: "pdf" | "epub") => { setExportOpen(false); runExport(format); }; if (!book) return null; const coverActive = activeChapterId === COVER_ID; const idx = book.chapters.findIndex((c) => c.id === activeChapterId); const chapter = book.chapters[idx] ?? book.chapters[0]; const realIdx = book.chapters.findIndex((c) => c.id === chapter?.id); const kind = chapter ? chapterKind(chapter) : "body"; const eyebrow = kind === "body" ? `Chapter ${bodyNumber(book.chapters, realIdx) ?? ""}` : kind === "front" ? "Front matter" : "Back matter"; return (
{exportOpen && ( <>
setExportOpen(false)} />
)}
{coverActive ? ( ) : ( <>
{eyebrow}
setChapterTitle(chapter.id, e.target.value)} />
setChapterContent(chapter.id, content)} onReady={setEditor} />
)}
{dock && }
{settingsOpen && setSettingsOpen(false)} onSave={saveNow} />} {exporting && (

Exporting {exporting}…

)} {notice && (
setNotice(null)}> {notice}
)}
); }