Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone 70bace54357d4b57b674108f
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
src/Builder.tsx
// The editor in the landing hero: the resumes on this device (switch, add, copy, rename, delete), each with its
// cover letter; download them (PDF, text, JSON) or share the resume by link. Everything saves on this device as you type.
import { useEffect, useRef, useState } from "react";
import { isEmptyResume, type Resume } from "../functions/lib/resume";
import { navigate } from "./lib/router";
import { Chevron, Dialog, Dots, DownloadIcon, Menu, ask, copy, explain, share, toast, type MenuItem } from "./ui";
import { Editor, type EditorError, type Mode } from "./Editor";
import { downloadJson, downloadLetterText, downloadText, readJsonFile } from "./files";
import { draftName, hashOf, listDrafts, makeDraft, openDraft, removeDraft, saveDraft, type Draft } from "./store";
const date = (at: number) => new Date(at).toLocaleDateString(undefined, { month: "short", day: "numeric" });
export function Builder() {
const [draft, setDraft] = useState<Draft>(openDraft);
const [error, setError] = useState<EditorError | null>(null);
const [busy, setBusy] = useState<"pdf" | "share" | null>(null);
const [shared, setShared] = useState<{ id: string; key: string; updated: boolean } | null>(null);
const [renaming, setRenaming] = useState(false);
// Opening another resume remounts the form, so folded entries start fresh.
const [round, setRound] = useState(0);
const touched = useRef(false);
const fileInput = useRef<HTMLInputElement>(null);
const r = draft.resume;
// An untouched blank resume is not saved, so a visit that writes nothing leaves nothing behind.
useEffect(() => { if (touched.current) saveDraft(draft); }, [draft]);
// Once someone is writing, the PDF maker and its fonts load in the background, so Download is quick.
const warmed = useRef(false);
useEffect(() => {
if (warmed.current || isEmptyResume(r)) return;
warmed.current = true;
const idle = window.requestIdleCallback ?? ((f: () => void) => window.setTimeout(f, 1500));
idle(() => { import("./pdf").then((m) => m.warm()).catch(() => {}); });
}, [r]);
const change = (patch: Partial<Draft>) => {
touched.current = true;
setDraft((d) => ({ ...d, ...patch, at: Date.now() }));
setError(null);
};
/** Opens a resume; a fresh blank one is saved only once something is typed into it. */
const open = (d: Draft, save = true) => {
if (save) saveDraft(d);
touched.current = false;
setDraft(d);
setRound((n) => n + 1);
setError(null);
};
const named = () => {
if (r.basics.name.trim()) return true;
setError({ text: "Add your name first. It prints at the top.", field: "name" });
return false;
};
const download = async (mode: Mode) => {
if (!named()) return;
setError(null);
setBusy("pdf");
try {
const pdf = await import("./pdf");
await (mode === "letter" ? pdf.downloadLetterPdf(r, draft.letter) : pdf.downloadPdf(r));
} catch (err) {
setError({ text: `The PDF could not be made: ${explain(err)}` });
} finally {
setBusy(null);
}
};
const linked = draft.linked;
const upToDate = Boolean(linked && linked.hash === hashOf(r));
/** Saves the resume to its link: the same link every time from this device, a new one the first time. */
const shareLink = async () => {
if (!named()) return;
setError(null);
if (linked && upToDate) return setShared({ id: linked.id, key: linked.key, updated: false });
setBusy("share");
try {
const s = await import("./share");
if (linked && (await s.canUpdate(linked))) {
const doc = await s.readShared(linked.id);
if (doc && doc.updatedAt !== linked.savedAt && !(await ask({
title: "Replace the version on the link?",
body: "This resume's link was changed on another device since you last saved it here. Updating shows this version instead.",
confirm: "Replace it",
}))) return;
const savedAt = await s.updateShared(linked.id, linked.key, r);
change({ linked: { ...linked, savedAt, hash: hashOf(r) } });
setShared({ id: linked.id, key: linked.key, updated: true });
return;
}
const next = await s.createShared(r);
change({ linked: next });
setShared({ id: next.id, key: next.key, updated: false });
} catch (err) {
setError({ text: explain(err) });
} finally {
setBusy(null);
}
};
const startNew = () => {
open(makeDraft());
toast("New resume. Your other resumes are in the list above.");
};
const duplicate = () => {
open(makeDraft(structuredClone(r), `${draftName(draft)} (copy)`, { ...draft.letter }));
toast("Copy made. You are editing the copy.");
};
const remove = () => {
const gone = draft;
removeDraft(gone.id);
const next = listDrafts()[0];
open(next ?? makeDraft(), Boolean(next));
if (!isEmptyResume(gone.resume) || gone.letter.body.trim()) toast("Resume deleted from this device.", { label: "Undo", run: () => open(gone) });
};
const importFile = async (file: File | undefined) => {
if (!file) return;
try {
const imported = await readJsonFile(file);
// Importing into an empty resume fills it; otherwise the file becomes a new resume beside this one.
open(isEmptyResume(r) ? { ...draft, resume: imported, at: Date.now() } : makeDraft(imported));
toast("Imported. Check each section, then download.");
} catch (err) {
toast(explain(err));
}
};
const switcher = (): MenuItem[] => {
const all = listDrafts();
if (!all.some((d) => d.id === draft.id)) all.unshift(draft);
return [
{ heading: "On this device" },
...all.map((d) => ({ label: draftName(d), hint: d.id === draft.id ? "Open now" : `Edited ${date(d.at)}`, checked: d.id === draft.id, onSelect: () => { if (d.id !== draft.id) open(d); } })),
"-",
{ label: "New resume", onSelect: startNew },
{ label: "Make a copy", hint: "To tailor it for another job", onSelect: duplicate },
{ label: "Rename", onSelect: () => setRenaming(true) },
{ label: "Import a JSON Resume file", onSelect: () => fileInput.current?.click() },
"-",
{ label: "Delete this resume", danger: true, onSelect: remove },
];
};
const shareLabel = linked ? (upToDate ? "Link" : "Update link") : "Share link";
const actions = (mode: Mode, phone: boolean) => {
const pdf = (
<button className="btn" type="button" onClick={() => download(mode)} disabled={busy !== null && busy !== "pdf"} aria-busy={busy === "pdf"}>
<DownloadIcon /><span>{phone ? "PDF" : "Download PDF"}</span>
</button>
);
const more: MenuItem[] = mode === "letter"
? [{ label: "Download letter as text", onSelect: () => downloadLetterText(r, draft.letter) }]
: [
{ label: "Download as text", hint: "To paste into application forms", onSelect: () => downloadText(r) },
{ label: "Download JSON Resume", hint: "To keep a copy or move devices", onSelect: () => downloadJson(r) },
];
const shareBtn = mode === "resume" && (
<button className="btn btn-line" type="button" onClick={shareLink} disabled={busy !== null && busy !== "share"} aria-busy={busy === "share"}><span>{phone ? "Share" : shareLabel}</span></button>
);
return (
<>
{pdf}
{shareBtn}
<Menu label={phone ? <Dots /> : <>More<Chevron /></>} className={phone ? "btn btn-line btn-icon" : "btn btn-line menu-btn"} ariaLabel="More downloads" end up={phone} disabled={busy !== null} items={more} />
</>
);
};
const name = draftName(draft);
const top = (
<div className="doc-bar">
<Menu className="btn btn-quiet doc-switch" ariaLabel={`${name}. Switch resume`} items={switcher} end
label={<><span className="doc-switch-name">{name}</span><Chevron /></>} />
<span className="doc-saved">{isEmptyResume(r) ? "Saves on this device" : "Saved on this device"}</span>
</div>
);
const origin = location.origin;
return (
<>
<Editor key={`${draft.id}:${round}`} resume={r} onChange={(resume: Resume) => change({ resume })} actions={actions} error={error} top={top}
letter={{ value: draft.letter, onChange: (letter) => change({ letter }) }} />
<input ref={fileInput} type="file" accept="application/json,.json" hidden onChange={(e) => { importFile(e.target.files?.[0]); e.target.value = ""; }} />
{renaming && (
<Dialog title="Rename this resume" onClose={() => setRenaming(false)} footer={(close) => (
<>
<button className="btn btn-line" type="button" onClick={close}>Cancel</button>
<button className="btn" type="submit" form="rename-form">Save</button>
</>
)}>
{(close) => (
<form id="rename-form" className="field" onSubmit={(e) => {
e.preventDefault();
change({ title: String(new FormData(e.currentTarget).get("title") || "").trim().slice(0, 80) });
close();
}}>
<label className="label" htmlFor="rename-title">Name</label>
<input className="input" id="rename-title" name="title" defaultValue={draft.title || name} maxLength={80} placeholder="Acme application" aria-describedby="rename-hint" />
<small id="rename-hint">Only you see this name, in the list of your resumes. It does not print.</small>
</form>
)}
</Dialog>
)}
{shared && (
<Dialog title={shared.updated ? "Link updated" : "Your resume link"} onClose={() => setShared(null)} footer={(close) => (
<>
<button className="btn btn-line" type="button" onClick={close}>Done</button>
<button className="btn" type="button" onClick={() => { close(); navigate(`/r/${shared.id}`); }}>Open resume page</button>
</>
)}>
<div className="field">
<span className="label">Link to share</span>
<div className="link-row">
<code className="link-code">{`${origin}/r/${shared.id}`}</code>
<button className="btn btn-sm" type="button" onClick={() => share(`${origin}/r/${shared.id}`, `${r.basics.name} resume`)}>Copy</button>
</div>
<small>Anyone with this link can read your resume and download the PDF. No account needed.</small>
</div>
<div className="field">
<span className="label">Your admin link</span>
<div className="link-row">
<code className="link-code">{`${origin}/r/${shared.id}/admin#k=${shared.key}`}</code>
<button className="btn btn-sm btn-line" type="button" onClick={() => copy(`${origin}/r/${shared.id}/admin#k=${shared.key}`, "Admin link copied. Keep it private.")}>Copy</button>
</div>
<small>Opens the shared resume for editing on any device. Keep it to yourself.</small>
</div>
<p className="hint">{shared.updated ? "The link now shows this version." : "The link shows the resume as it is now."} When you change it here, press Update link to show the changes. Your cover letter is not shared.</p>
</Dialog>
)}
</>
);
}
Change history
Quality pass: several resumes, cover letters, phone-readable shared pages
Bounded source sync
Bounded source sync
Bounded source sync