/* cover-letter.jsx — cover letter editor + live preview */ var CL_DEFAULTS = { date: "", toName: "", toTitle: "", toCompany: "", toAddress: "", opening: "Dear Hiring Manager,", body: "", closing: "Sincerely,", }; var CL_FONT_STACKS = { geist: '"Geist", -apple-system, "Segoe UI", sans-serif', ubuntu: '"Ubuntu", sans-serif', lato: '"Lato", sans-serif', roboto: '"Roboto", sans-serif', opensans:'"Open Sans", sans-serif', mono: '"Geist Mono", monospace', auto: '"Geist", -apple-system, "Segoe UI", sans-serif', }; function CoverLetterPreview({ cl, basics, accent, design }) { basics = basics || {}; accent = accent || "#1C9BE6"; design = design || {}; var fontKey = design.font || "auto"; var fontStack = CL_FONT_STACKS[fontKey] || CL_FONT_STACKS.auto; var date = cl.date || new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); var toLines = [cl.toName, cl.toTitle, cl.toCompany, cl.toAddress].filter(Boolean); var body = cl.body || ""; var paras = body.split("\n\n").filter(Boolean); return (
{/* Sender */}
{basics.name || "Your Name"}
{basics.title && (
{basics.title}
)}
{[basics.email, basics.phone, basics.location, basics.website].filter(Boolean).map(function(c, i) { return {c}; })}
{/* Date */}
{date}
{/* Recipient */} {toLines.length > 0 && (
{toLines.map(function(ln, i) { return (
{ln}
); })}
)} {/* Opening */} {cl.opening &&
{cl.opening}
} {/* Body */} {paras.length > 0 ? ( paras.map(function(para, i) { return

{para}

; }) ) : (

Your letter body will appear here. Write focused paragraphs about why you're a great fit for this role.

)} {/* Closing */}
{cl.closing || "Sincerely,"}
{basics.name || "Your Name"}
{basics.title && (
{basics.title}
)}
); } function CoverLetterEditor({ cl, onChange, data, showToast }) { var set = function(k, v) { onChange(Object.assign({}, cl, { [k]: v })); }; var today = new Date().toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); const { useState: S, useRef: R } = React; const [jd, setJd] = S(""); const [showJd, setShowJd] = S(false); const [generating, setGenerating] = S(false); const bodyRef = R(null); const generate = () => { if (generating || !window.groqStream) return; setGenerating(true); const ctx = window.buildResumeCtx ? window.buildResumeCtx(data || {}) : ""; const company = cl.toCompany || "the company"; const role = cl.toTitle || "the role"; const prompt = "Write a targeted cover letter body using the résumé below" + (jd.trim() ? " and the job description provided" : "") + ".\n\n" + "RÉSUMÉ:\n" + ctx + "\n\n" + (jd.trim() ? "JOB DESCRIPTION:\n" + jd.trim() + "\n\n" : "") + "Company: " + company + "\n" + "Role: " + role + "\n\n" + "Write 3 paragraphs separated by a blank line (\\n\\n):\n" + "1. Opening — genuine enthusiasm for " + company + ", briefly who I am and my strongest credential.\n" + "2. Body — 2 specific, quantified achievements from my résumé that directly match the role's needs.\n" + "3. Closing — confident call to action, invite for an interview.\n\n" + "Under 350 words. Professional but warm. First-person. " + "Return ONLY the three paragraphs — no salutation, no date, no sign-off, no subject line."; let streamed = ""; window.groqStream({ messages: [ { role: "system", content: window.GROQ_SYSTEM || "" }, { role: "user", content: prompt }, ], maxTokens: 700, onChunk: (_, full) => { streamed = full; set("body", full); }, onDone: (full) => { setGenerating(false); set("body", (full || streamed).trim()); if (showToast) showToast("Cover letter generated!"); }, onError: (err) => { setGenerating(false); if (err === "free_limit_reached") { if (showToast) showToast("Free AI limit reached — upgrade to Pro for unlimited use."); } else { if (showToast) showToast("AI error: " + err); } }, }); }; return (

Cover Letter

A targeted cover letter significantly improves your callback rate. Aim for three focused paragraphs — opening, body, close.

{/* ── AI generation panel ─────────────────────────────── */}
Generate with AI
Uses your résumé data{cl.toCompany ? " · " + cl.toCompany : ""} {jd.trim() ? " · JD pasted" : ""}
{showJd && (
Job description optional — improves targeting