Fix PDF export to always include the full book

The previous export printed the CodeMirror editor, which only keeps a
virtualized window of lines in the DOM; on larger books the PDF could come
out truncated (only what was rendered around the viewport). Replace the
print path with a dedicated #printBook container: the renderer renders each
chapter of the assembled manuscript to plain HTML with markdown-it, and the
print stylesheet hides the entire app and shows that container instead.
Plain HTML paginates reliably regardless of editor viewport or scroll
position, every chapter after the first still starts on its own page, and
output is a properly formatted manuscript instead of raw markdown. Keep the
screen-only chapter-break separator in the Full Book editor.

Extend the e2e selftest to assert #printBook holds every chapter, is hidden
on screen, and that a 1500-line chapter reaches the print DOM and exported
PDF in full.
This commit is contained in:
avi 2026-08-18 10:41:56 -05:00
commit d2118f080f
4 changed files with 151 additions and 58 deletions

View file

@ -270,7 +270,7 @@ const BOOK2_DIR = __BOOKDIR2_VALUE__;
const rawCss = await (await fetch("./index.css")).text();
out.printBreakRule = /break-before\s*:\s*page/.test(rawCss);
out.printBreakFallback = /page-break-before\s*:\s*always/.test(rawCss);
out.printBreakScoped = /#fullBookHost\s+\.cm-line\.chapter-break/.test(rawCss);
out.printBreakScoped = /\.print-chapter\s*{[^}]*break-before\s*:\s*page/.test(rawCss);
document.getElementById("copyAllBtn").click();
await new Promise((r) => setTimeout(r, 200));
@ -435,6 +435,55 @@ const BOOK2_DIR = __BOOKDIR2_VALUE__;
pdfRes.bytes > 500 &&
pdfRes.pdfOk === true;
// The printed book comes from the plain-HTML #printBook container (the
// print stylesheet hides the whole app and shows #printBook instead), so
// the PDF can never be limited to the CodeMirror viewport. Verify the
// container is hidden on screen and carries every chapter.
const printBookEl = document.getElementById("printBook");
out.printBookHiddenOnScreen = getComputedStyle(printBookEl).display === "none";
const printChapters = () =>
Array.from(document.querySelectorAll("#printBook .print-chapter"));
const pcsAtSmallBook = printChapters();
out.printBookChapterCount = pcsAtSmallBook.length;
out.printBookHasAllChapters =
pcsAtSmallBook.length === 3 &&
pcsAtSmallBook.some((s) => s.textContent.includes("Gamma text")) &&
pcsAtSmallBook.some((s) => s.textContent.includes("Beta body without heading")) &&
pcsAtSmallBook.some((s) => s.querySelector("h1")?.textContent === "Empty");
// Long book: every line must reach the print DOM — the guard against the
// "PDF does not show the full book" bug where a long manuscript was cut
// off after the first screenful.
const longLines = Array.from(
{ length: 1500 },
(_, i) => `Line ${i} of a very long chapter that must all reach the printed book.`
);
const veryLongId = (await window.folio.createChapter("Very Long")).id;
await window.folio.saveChapter(veryLongId, longLines.join("\n"));
await new Promise((r) => setTimeout(r, 500));
document.getElementById("fullBookBtn").click();
await new Promise((r) => setTimeout(r, 400));
const printBookText = printBookEl.textContent;
out.printBookHasFirstLongLine = printBookText.includes("Line 0 of a very long chapter");
out.printBookHasLastLongLine = printBookText.includes("Line 1499 of a very long chapter");
out.printBookLongChapterCount = printChapters().length;
out.printBookLongFull =
out.printBookHasFirstLongLine &&
out.printBookHasLastLongLine &&
printChapters().length === 4;
const pdfLongRes = await window.folio.exportPdf();
out.pdfLongExport =
pdfLongRes && "filePath" in pdfLongRes
? { filePath: pdfLongRes.filePath, bytes: pdfLongRes.bytes, pdfOk: pdfLongRes.pdfOk }
: pdfLongRes;
out.pdfLongExportOk =
!!pdfLongRes &&
!("error" in pdfLongRes) &&
"filePath" in pdfLongRes &&
pdfLongRes.bytes > pdfRes.bytes * 2 &&
pdfLongRes.pdfOk === true;
const okFlags = [
"dndMoved",
"kbMoved",
@ -484,6 +533,10 @@ const BOOK2_DIR = __BOOKDIR2_VALUE__;
"pdfExportPdfVisible",
"pdfStatusShown",
"pdfExportOk",
"printBookHiddenOnScreen",
"printBookHasAllChapters",
"printBookLongFull",
"pdfLongExportOk",
];
out.all = okFlags.every((f) => out[f] === true);
} catch (e) {