Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ tailoring, job tracker, and applications stripped out.
titles with right-aligned dates, italic subtitles, automatic multi-page
pagination that keeps headings with their first entry.
- **Layout & spacing controls** — page zoom (also ⌘/Ctrl +/- / 0),
Compact/Normal/Relaxed presets, fine sliders for line height and
section/entry/line gaps. Settings persist to `localStorage`.
- **Typography controls** — toggle uppercase, underline, and bold section
headings; bold titles, italic subtitles/dates, bold skill labels; and a
Compact/Normal/Relaxed presets, and fine sliders for line height and
header/section/entry/list gaps tucked behind a "Fine-tune spacing" disclosure.
Save the current spacing as a reusable **Custom** preset. Settings persist to
`localStorage`.
- **Typography controls** — grouped by element (Headings / Entries / Skills /
Contact): section-heading **case** (small caps / uppercase / normal) plus bold
and underline; bold titles, italic subtitles; bold skill labels; and a
configurable contact divider (quick-pick glyphs or custom 1-2 char input).
Includes a one-click reset.
One-click reset to Jake's defaults.
- **Exports** (each download opens a rename dialog pre-filled with a
resume-derived file name)
- **PDF - LaTeX** — the resume rendered through the Jake's template and compiled
Expand Down Expand Up @@ -79,6 +82,10 @@ src/
editor/ the editable on-page resume (sections, entries, bullets, skills)
Resume*.tsx read-only document + off-screen print layer
styles/ design tokens, resume document/editor CSS, app shell
public/
favicon.svg forge brand mark (anvil app-icon, also the sidebar lockup)
fonts/ embedded LM Roman faces for PDF-faithful on-page rendering
CNAME GitHub Pages custom-domain config (jakeforge.xinyiklin.com)
server/
latex/ Jake's template renderer, resume parser, Tectonic wrapper
docx.mjs DOCX text extractor (zero-dep; shells to unzip)
Expand Down
6 changes: 6 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="theme-color" content="#1b674c" />
<meta
name="description"
content="jakeforge — a local-first resume editor that renders and exports the single-column Jake Gutierrez LaTeX style."
/>
<title>jakeforge — Jake's-style resume editor</title>
</head>
<body>
Expand Down
17 changes: 17 additions & 0 deletions public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
36 changes: 21 additions & 15 deletions server/latex/templates/jakes.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ const DEFAULTS = {
boldTitles: true,
boldSkillLabels: true,
italicSubtitles: true,
italicDates: true,
// Section-heading look — small-caps (\scshape) is the default; the Uppercase
// toggle forces ALL CAPS, and the rule under the heading is the titlerule.
uppercaseHeadings: false,
// Section-heading case — small-caps (\scshape) is Jake's default; "uppercase"
// forces ALL CAPS (\MakeUppercase) and "none" leaves Title Case. The rule under
// the heading is the titlerule.
headingCase: "smallcaps",
sectionRule: true
};

Expand Down Expand Up @@ -143,8 +143,14 @@ function styleToLatex(docStyle) {
boldTitles: bool(docStyle.boldTitles, DEFAULTS.boldTitles),
boldSkillLabels: bool(docStyle.boldSkillLabels, DEFAULTS.boldSkillLabels),
italicSubtitles: bool(docStyle.italicSubtitles, DEFAULTS.italicSubtitles),
italicDates: bool(docStyle.italicDates, DEFAULTS.italicDates),
uppercaseHeadings: bool(docStyle.uppercaseHeadings, DEFAULTS.uppercaseHeadings),
// headingCase replaced the old `uppercaseHeadings` boolean; honor a legacy
// client that still sends `uppercaseHeadings: true`.
headingCase:
docStyle.headingCase === "uppercase" || docStyle.headingCase === "none"
? docStyle.headingCase
: docStyle.uppercaseHeadings === true
? "uppercase"
: DEFAULTS.headingCase,
sectionRule: bool(docStyle.sectionRule, DEFAULTS.sectionRule)
};
}
Expand Down Expand Up @@ -227,9 +233,10 @@ function buildPreamble(style) {
% Section gaps are pure separators: \titlespacing "before" is the WHOLE gap
% above the heading (= sectionGap), "after" is the rule→first-row gap
% (= sectionEntryGap). titlesec drops the before-glue at the top of a page, so
% the first section's gap is set by the header \vspace instead. \scshape gives
% small-caps; the Uppercase toggle uppercases the heading text in renderSection.
\titleformat{\section}{\scshape\raggedright\large${style.boldHeadings ? "\\bfseries" : ""}}{}{0em}{}[\color{black}${style.sectionRule ? "\\titlerule" : ""}]
% the first section's gap is set by the header \vspace instead. Heading case:
% \scshape (small caps), \MakeUppercase before-code (full caps), or neither
% (plain Title Case as stored).
\titleformat{\section}{${style.headingCase === "smallcaps" ? "\\scshape" : ""}\raggedright\large${style.boldHeadings ? "\\bfseries" : ""}}{}{0em}{${style.headingCase === "uppercase" ? "\\MakeUppercase" : ""}}[\color{black}${style.sectionRule ? "\\titlerule" : ""}]
\titlespacing*{\section}{0pt}{${fmtPt(style.sectionGapPt)}}{${fmtPt(style.sectionEntryGapPt)}}

\ifdefined\pdfgentounicode\pdfgentounicode=1\fi
Expand Down Expand Up @@ -258,12 +265,12 @@ function buildPreamble(style) {
\item
\resumeRow{${bf("#1", style.boldTitles)}}{#2}%
\vspace{${fmtPt(style.titleSubVSpacePt)}}%
\resumeRow{${it("\\small#3", style.italicSubtitles)}}{${it("\\small #4", style.italicDates)}}%
\resumeRow{${it("\\small#3", style.italicSubtitles)}}{${it("\\small #4", style.italicSubtitles)}}%
}

\newcommand{\resumeSubSubheading}[2]{%
\item
\resumeRow{${it("\\small#1", style.italicSubtitles)}}{${it("\\small #2", style.italicDates)}}%
\resumeRow{${it("\\small#1", style.italicSubtitles)}}{${it("\\small #2", style.italicSubtitles)}}%
}

\newcommand{\resumeProjectHeading}[2]{%
Expand Down Expand Up @@ -367,10 +374,9 @@ ${item.bullets.map((bullet) => ` \\resumeItem{${escapeTex(bullet)}}`).joi

function renderSection(section, style) {
const hasAnyHeading = section.items.some((item) => item.title || item.subtitle || item.meta || item.location);
// Headings are small-caps (\scshape) by default; the Uppercase toggle forces
// ALL CAPS by uppercasing the text (\scshape is then a no-op on the caps).
const titled = titleCase(section.heading);
const heading = style.uppercaseHeadings ? titled.toUpperCase() : titled;
// Heading case is applied by \titleformat (\\scshape / \\MakeUppercase / none),
// so the text is just stored Title Case here.
const heading = titleCase(section.heading);

// Summary-like headings render their rows as plain small paragraphs instead of
// the skills block (which draws \\ line breaks and bolds "Label:" prefixes).
Expand Down
Loading