184 lines
No EOL
4.3 KiB
Text
184 lines
No EOL
4.3 KiB
Text
// Resume template — reads from ../data/resume.yaml
|
|
// Build: typst compile --root . --font-path resume/ resume/main.typ static/resume.pdf
|
|
|
|
#let data = yaml("../data/resume.yaml")
|
|
|
|
#set page(paper: "a4", margin: (x: 0.5in, y: 0.35in))
|
|
#set document(title: data.name + "'s Resume", author: data.name)
|
|
#set par(leading: 0.55em)
|
|
#set text(font: "Source Sans 3", size: 10pt)
|
|
#set underline(offset: 2.5pt)
|
|
#show heading: set text(weight: "semibold")
|
|
#show heading: it => { v(4pt); it; v(2pt) }
|
|
|
|
// --- Markdown bold **text** → Typst bold *text* ---
|
|
#let md(s) = {
|
|
let converted = s.replace(regex("\*\*(.+?)\*\*"), m => "*" + m.captures.at(0) + "*")
|
|
eval(converted, mode: "markup")
|
|
}
|
|
|
|
// --- Icon helper ---
|
|
#let icon(name, shift: 1.5pt) = {
|
|
box(
|
|
baseline: shift,
|
|
height: 10pt,
|
|
image("icons/" + name + ".svg", alt: name)
|
|
)
|
|
h(2pt)
|
|
}
|
|
|
|
// --- Links bar ---
|
|
#let linksBar(links) = {
|
|
let icon = icon.with(shift: 2.5pt)
|
|
links.map(l => {
|
|
icon(l.icon)
|
|
if "display" in l.keys() {
|
|
link(l.url)[#{l.display}]
|
|
} else {
|
|
link(l.url)
|
|
}
|
|
}).join(h(5pt))
|
|
}
|
|
|
|
// --- Work experience entry ---
|
|
#let workExperience(body, company: "", role: "", start: "", end: "", location: "", stack: ()) = {
|
|
grid(
|
|
columns: (1fr, auto),
|
|
row-gutter: (0.5em, ),
|
|
align(left)[
|
|
#set text(weight: "semibold")
|
|
#company
|
|
],
|
|
align(right)[
|
|
#location
|
|
],
|
|
align(left)[
|
|
#set text(weight: "semibold")
|
|
#role
|
|
],
|
|
align(left)[
|
|
#start - #end
|
|
],
|
|
if stack.len() > 0 {
|
|
grid.cell(colspan: 2, align: left)[
|
|
#set text()
|
|
Tech Stack: #stack.join(", ")
|
|
]
|
|
}
|
|
)
|
|
body
|
|
}
|
|
|
|
// --- Project entry ---
|
|
#let project(body, name: "", stack: ()) = {
|
|
text(weight: "semibold")[#name] + if stack.len() > 0 {
|
|
[ | #stack.join(", ")]
|
|
}
|
|
body
|
|
}
|
|
|
|
// --- Education entry ---
|
|
#let educationQualification(body, school: "", course: "", start: "", end: "", location: "") = {
|
|
grid(
|
|
columns: (1fr, auto),
|
|
row-gutter: (0.5em, ),
|
|
align(left)[
|
|
#set text(weight: "semibold")
|
|
#school
|
|
],
|
|
align(right)[
|
|
#location
|
|
],
|
|
align(left)[
|
|
#set text(weight: "semibold")
|
|
#course
|
|
],
|
|
align(left)[
|
|
#start - #end
|
|
]
|
|
)
|
|
body
|
|
}
|
|
|
|
// =============================================
|
|
// RENDER
|
|
// =============================================
|
|
|
|
// --- Header ---
|
|
#text(size: 28pt, weight: "semibold")[#data.name] \
|
|
#v(9pt, weak: true)
|
|
#text(size: 14pt)[Systems & Software Engineer]
|
|
|
|
// Contact links — first row
|
|
#linksBar((
|
|
(icon: "phone", url: data.contact.phone.url, display: data.contact.phone.display),
|
|
(icon: "email", url: data.contact.email.url),
|
|
(icon: "website", url: data.contact.website.url, display: data.contact.website.display),
|
|
(icon: "linkedin", url: data.contact.linkedin.url, display: data.contact.linkedin.display),
|
|
))
|
|
#linebreak()
|
|
// Contact links — second row
|
|
#linksBar((
|
|
(icon: "git", url: data.contact.git.url, display: data.contact.git.display),
|
|
(icon: "github", url: data.contact.github.url, display: data.contact.github.display),
|
|
))
|
|
#line(length: 100%)
|
|
|
|
// --- Experience ---
|
|
= Experience
|
|
|
|
#for exp in data.experience {
|
|
workExperience(
|
|
company: exp.company,
|
|
role: exp.role,
|
|
start: exp.start,
|
|
end: exp.end,
|
|
location: exp.location,
|
|
stack: exp.at("stack", default: ()),
|
|
)[
|
|
#for bullet in exp.at("bullets", default: ()) {
|
|
[- #md(bullet)]
|
|
}
|
|
#for l in exp.at("links", default: ()) {
|
|
[- #underline(link(l.url)[#l.display])]
|
|
}
|
|
]
|
|
}
|
|
|
|
// --- Skills ---
|
|
= Skills
|
|
#for skill in data.skills {
|
|
[#text(weight: "semibold")[#skill.type]: #skill.items.join(", ") \ ]
|
|
}
|
|
|
|
// --- Projects ---
|
|
= Projects
|
|
#for p in data.projects {
|
|
project(name: p.name, stack: p.at("stack", default: ()))[
|
|
#for bullet in p.at("bullets", default: ()) {
|
|
[- #md(bullet)]
|
|
}
|
|
#let links = p.at("links", default: ())
|
|
#if links.len() > 0 {
|
|
[- #links.map(l => underline(link(l.url)[#l.display])).join(", ")]
|
|
}
|
|
]
|
|
}
|
|
|
|
// --- Education ---
|
|
= Education
|
|
#for edu in data.education {
|
|
educationQualification(
|
|
school: edu.school,
|
|
course: edu.course,
|
|
location: edu.location,
|
|
start: edu.start,
|
|
end: edu.end,
|
|
)[]
|
|
}
|
|
|
|
// --- Certifications ---
|
|
= Certifications
|
|
#for cert in data.certifications {
|
|
[- #underline(link(cert.url)[#cert.name])]
|
|
} |