From c7a023ff6a06b47540cf104cc67d1785ad6da302 Mon Sep 17 00:00:00 2001 From: Paul T Date: Wed, 28 May 2025 22:50:54 -0400 Subject: [PATCH] fix: resume skill item grid alignment (#121) --- .github/workflows/tests.yml | 32 ++++++++++++---------- lib.typ | 53 +++++++++++++++++++++++++++++------- scripts/install-source-sans | 10 +++++++ template/resume.typ | 40 +++++++++++++++++++++++++-- tests/resume/ref/1.png | Bin 32624 -> 32656 bytes 5 files changed, 108 insertions(+), 27 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index de4a78e..2dda06f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,9 +1,9 @@ name: Tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: tests: @@ -37,29 +37,31 @@ jobs: with: crate: just - - name: Install tytanic from github - uses: baptiste0928/cargo-install@v3 + - name: Install tytanic + uses: taiki-e/cache-cargo-install-action@v2 with: - crate: tytanic + tool: tytanic@0.2.2 - name: Setup typst uses: typst-community/setup-typst@v3 with: typst-version: ${{ matrix.typst-version }} - + - name: Install fonts run: | - sudo apt update - sudo apt-get install fonts-roboto - ./scripts/install-fontawesome - ./scripts/install-source-sans - typst fonts + sudo apt update + sudo apt-get install fonts-roboto + ./scripts/install-fontawesome + ./scripts/install-source-sans + typst fonts - - name: Install locally - run: just install + - name: Uninstall previous local install + run: just uninstall - name: Run test suite - run: just test + run: | + just install + tt run --font-path ~/.fonts --no-fail-fast - name: Archive diffs uses: actions/upload-artifact@v4 @@ -75,7 +77,7 @@ jobs: - name: Build docs if: ${{ matrix.doc }} run: just doc - + - name: Upload docs uses: actions/upload-artifact@v4 with: diff --git a/lib.typ b/lib.typ index 776e0dd..2ad9176 100644 --- a/lib.typ +++ b/lib.typ @@ -477,6 +477,25 @@ justified-header(certification, date) } +/// Styling for resume skill categories. +/// - category (string): The category +#let resume-skill-category(category) = { + align(left)[ + #set text(hyphenate: false) + == #category + ] +} + +/// Styling for resume skill values/items +/// - values (array): The skills to display +#let resume-skill-values(values) = { + align(left)[ + #set text(size: 11pt, style: "normal", weight: "light") + // This is a list so join by comma (,) + #values.join(", ") + ] +} + /// Show a list of skills in the resume under a given category. /// - category (string): The category of the skills /// - items (list): The list of skills. This can be a list of strings but you can also emphasize certain skills by using the `strong` function. @@ -486,16 +505,30 @@ pad[ #grid( - columns: (20fr, 80fr), + columns: (3fr, 8fr), gutter: 10pt, - align(right)[ - #set text(hyphenate: false) - == #category - ], - align(left)[ - #set text(size: 11pt, style: "normal", weight: "light") - #items.join(", ") - ], + resume-skill-category(category), resume-skill-values(items), + ) + ] +} + +/// Show a grid of skill lists with each row corresponding to a category of skills, followed by the skills themselves. The dictionary given to this function should have the skill categories as the dictionary keys and the values should be an array of values for the corresponding key. +/// - categories_with_values (dictionary): key value pairs of skill categories and it's corresponding values (skills) +#let resume-skill-grid(categories_with_values: (:)) = { + set block(below: 1.25em) + set pad(top: 2pt) + + pad[ + #grid( + columns: (auto, auto), + gutter: 10pt, + ..categories_with_values + .pairs() + .map(((key, value)) => ( + resume-skill-category(key), + resume-skill-values(value), + )) + .flatten() ) ] } @@ -700,7 +733,7 @@ stroke: 0pt, radius: 2cm, width: 4cm, - height: 4cm, + height: auto, profile-picture, ) ], diff --git a/scripts/install-source-sans b/scripts/install-source-sans index cde6371..4115e93 100755 --- a/scripts/install-source-sans +++ b/scripts/install-source-sans @@ -8,4 +8,14 @@ mkdir -p ~/.fonts find ~/source-sans-fonts -type f -name "*.otf" -exec cp {} ~/.fonts \; rm ~/source-sans.zip rm -rf ~/source-sans-fonts + +wget -O ~/source-sans-pro.zip https://github.com/adobe-fonts/source-sans-pro/archive/2.020R-ro/1.075R-it.zip +mkdir -p ~/source-sans-pro-fonts +unzip ~/source-sans-pro.zip -d ~/source-sans-pro-fonts +mkdir -p ~/.fonts +find ~/source-sans-pro-fonts -type f -name "*.otf" -exec cp {} ~/.fonts \; +# clean up +rm ~/source-sans-pro.zip +rm -rf ~/source-sans-pro-fonts + fc-cache -f -v diff --git a/template/resume.typ b/template/resume.typ index 49df1ce..948017b 100644 --- a/template/resume.typ +++ b/template/resume.typ @@ -20,7 +20,7 @@ "Developer", ), ), - profile-picture: none, + profile-picture: image("profile.png"), date: datetime.today().display(), language: "en", colored-headers: true, @@ -94,10 +94,11 @@ = Skills #resume-skill-item( - "Languages", + "Programming Languages", ( strong("C++"), strong("Python"), + "Rust", "Java", "C#", "JavaScript", @@ -114,6 +115,41 @@ "Visual Studio", ), ) +// spacing fix, not needed if you use `resume-skill-grid` +#block(below: 0.65em) + +// An alternative way of list out your resume skills +// #resume-skill-grid( +// categories_with_values: ( +// "Programming Languages": ( +// strong("C++"), +// strong("Python"), +// "Rust", +// "Java", +// "C#", +// "JavaScript", +// "TypeScript", +// ), +// "Spoken Languages": ( +// strong("English"), +// "Spanish", +// "Greek", +// ), +// "Programs": ( +// strong("Excel"), +// "Word", +// "Powerpoint", +// "Visual Studio", +// "git", +// "Zed" +// ), +// "Really Really Long Long Long Category": ( +// "Thing 1", +// "Thing 2", +// "Thing 3" +// ) +// ), +// ) = Education diff --git a/tests/resume/ref/1.png b/tests/resume/ref/1.png index 7f4da68a8e0f0b7d8d04618a5daa2493599550a1..a8b39b63bbbc9cc9804965c5e3ea7bf7b2ba0bd3 100644 GIT binary patch delta 8446 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 delta 8458 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