/* global React */ // ============================================================ // PDF export — clones the live, already-paginated preview sheets // into a print root and opens the browser's print dialog set to // true Letter size. The result is pixel-identical to the preview. // ============================================================ const { useState: useStatePR, useEffect: useEffectPR, useRef: useRefPR } = React; // Collect the rendered page nodes from a preview container, in order. function gatherPages(node) { if (!node) return []; const pages = []; node.querySelectorAll(".pg-sheet").forEach((s) => pages.push(s)); // résumé pages node.querySelectorAll(".cover").forEach((c) => pages.push(c)); // appended cover letter if (!pages.length) node.querySelectorAll(".paper").forEach((p) => pages.push(p)); // fallback return pages; } // Build (or rebuild) the hidden print root from cloned page nodes. function buildPrintRoot(pages) { const old = document.getElementById("print-root"); if (old) old.remove(); const root = document.createElement("div"); root.id = "print-root"; pages.forEach((p) => { const page = document.createElement("div"); page.className = "print-page"; const scale = document.createElement("div"); scale.className = "print-scale"; scale.appendChild(p.cloneNode(true)); page.appendChild(scale); root.appendChild(page); }); document.body.appendChild(root); return root; } function sanitizeFileName(name) { return (name || "resume").replace(/[\\/:*?"<>|]+/g, "").replace(/\s+/g, " ").trim() || "resume"; } // Run the print flow: clone → set title (drives the default PDF filename) → print → cleanup. function runPrint(getNode, fileName) { const node = typeof getNode === "function" ? getNode() : getNode; const pages = gatherPages(node); if (!pages.length) { window.alert("Nothing to export yet — add some content first."); return; } buildPrintRoot(pages); const prevTitle = document.title; document.title = sanitizeFileName(fileName); let done = false; const cleanup = () => { if (done) return; done = true; const r = document.getElementById("print-root"); if (r) r.remove(); document.title = prevTitle; window.removeEventListener("afterprint", cleanup); }; window.addEventListener("afterprint", cleanup); // fonts + layout settle, then print; afterprint (or the fallback) tidies up setTimeout(() => { window.print(); setTimeout(cleanup, 1500); }, 80); } // ---- Export modal ---------------------------------------------------------- function ExportModal({ defaultName, kind, getNode, onClose, reason, isPro }) { const [name, setName] = useStatePR(defaultName || "resume"); const [count, setCount] = useStatePR(0); const inputRef = useRefPR(null); useEffectPR(() => { const node = typeof getNode === "function" ? getNode() : getNode; setCount(gatherPages(node).length || 1); const t = setTimeout(() => { if (inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }, 60); const onKey = (e) => { if (e.key === "Escape") onClose(); }; document.addEventListener("keydown", onKey); return () => { clearTimeout(t); document.removeEventListener("keydown", onKey); }; }, []); const go = () => { onClose(); setTimeout(() => runPrint(getNode, name), 30); }; return (
{ if (e.target === e.currentTarget) onClose(); }}>

Export PDF

Print-ready, exactly as you see it.

{count}{count === 1 ? "Page" : "Pages"}
Letter8.5 × 11 in
VectorCrisp text
setName(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") go(); }} placeholder="My résumé" /> .pdf
In the dialog that opens, choose Save as PDF as the destination. For edge-to-edge color, set Margins to None.
{!isPro && reason === "pdf" && (
Your free PDF includes a light get-resume.com watermark. .
)}
); } // ---- drop-in export button (button + its own modal) ------------------------ function ExportButton({ getNode, fileName, label = "Export PDF" }) { const [open, setOpen] = useStatePR(false); return ( <> {open && setOpen(false)} />} ); } // ---- free "copy as text" button (résumé only) ------------------------------ function CopyTextButton({ getText, label = "Copy text" }) { const [done, setDone] = useStatePR(false); const onClick = async () => { const ok = await window.copyText(getText() || ""); if (ok) { setDone(true); setTimeout(() => setDone(false), 1900); } }; return ( ); } // ---- Pro-gated PDF download button ----------------------------------------- function PdfButton({ getNode, fileName, reason = "pdf" }) { const { isPro } = window.useProState(); const [open, setOpen] = useStatePR(false); // Résumé PDF is free for everyone (watermarked); Pro removes the watermark. // Cover-letter PDF stays Pro-only. const allowFree = reason === "pdf"; const canDownload = isPro || allowFree; const locked = !canDownload; const onClick = () => { if (canDownload) setOpen(true); else window.Pro.requestUpgrade(reason); }; return ( <> {open && setOpen(false)} />} ); } // ---- combined controls: free copy-text + gated PDF ------------------------- function ExportControls({ getNode, fileName, getText, reason }) { return ( <> {getText && } ); } Object.assign(window, { ExportButton, ExportModal, ExportControls, PdfButton, CopyTextButton, runPrint });