resume-free.openapps.xyz

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.
Owned app · rev 3f16b2375723
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.
← All files
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
3f16b23757 · 6 hours ago
Bounded source sync
b7eb5b26b5 · 1 day ago
Bounded source sync
adae97536e · 1 day ago
Bounded source sync
3b0cf36b39 · 1 day ago