feat: add preferences.qrCode for a header QR matrix linking to basics.url#88
feat: add preferences.qrCode for a header QR matrix linking to basics.url#88smur89 wants to merge 4 commits into
Conversation
d5b3f8b to
612cdf7
Compare
📝 WalkthroughWalkthroughAdds an optional ChangesHeader QR Code Feature
Sequence Diagram(s)sequenceDiagram
participant User as Document Author
participant alta as alta()
participant _header as _header()
participant _resolve_qr_url as _resolve_qr_url()
participant _qr_code as _qr_code()
participant zebra as `@preview/zebra` qrcode
User->>alta: preferences.qrCode = "url" | string | none
alta->>alta: validate qrCode (panic if invalid)
alta->>_header: qr-code = preferences.qrCode
_header->>_resolve_qr_url: qr-code, basics
_resolve_qr_url-->>_header: resolved URL or none
_header->>_qr_code: url, accent, size
_qr_code->>zebra: qrcode(url, fill: accent)
zebra-->>_qr_code: QR matrix content
_qr_code-->>_header: link(url, QR element)
_header-->>alta: header with QR in grid layout
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
5ed3c04 to
ce56256
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/header_qr_code.typ (1)
1-76: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winConsider adding test coverage for
imagePosition: "center"with QR code.The current test suite covers QR with
imagePosition: "left"and"right", but not"center". According to the implementation inlib.typ(lines 698-728), whenimagePositionis"center", the QR sits beside the header text row while the photo stacks above/below. This layout mode is not explicitly tested.📋 Suggested test case
`#pagebreak`() `#alta`( (basics: ( name: "QR + Centred Portrait", label: "imagePosition: \"center\", QR beside text", email: "qr@example.com", url: "https://example.com/cv", image: read("../examples/avatar-placeholder.svg", encoding: none), )), preferences: (qrCode: "url", imagePosition: "center"), )🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/header_qr_code.typ` around lines 1 - 76, Add a new test case to the header_qr_code.typ file that exercises the imagePosition "center" layout with QR code enabled. The existing tests cover QR with default/right positioning and with left positioning, but do not cover the center positioning mode. Insert a new `#pagebreak`() followed by an `#alta`() call that sets both preferences.qrCode to "url" and preferences.imagePosition to "center" while including a portrait image in the basics, mirroring the structure of the other test cases in the file to ensure this layout mode is properly tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@tests/header_qr_code.typ`:
- Around line 1-76: Add a new test case to the header_qr_code.typ file that
exercises the imagePosition "center" layout with QR code enabled. The existing
tests cover QR with default/right positioning and with left positioning, but do
not cover the center positioning mode. Insert a new `#pagebreak`() followed by an
`#alta`() call that sets both preferences.qrCode to "url" and
preferences.imagePosition to "center" while including a portrait image in the
basics, mirroring the structure of the other test cases in the file to ensure
this layout mode is properly tested.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a5e75577-8adf-42c0-958f-ef40e5ef8c18
⛔ Files ignored due to path filters (1)
examples/tests/header_qr_code.pdfis excluded by!**/*.pdf
📒 Files selected for processing (3)
README.mdlib.typtests/header_qr_code.typ
ce56256 to
f33912b
Compare
….url A printed CV loses the clickability of digital PDFs. The new `preferences.qrCode` preference renders a small accent-coloured QR matrix in the header on the side opposite the portrait, so a phone camera can take a reader straight to the candidate's homepage. `qrCode: "url"` encodes `basics.url` (now consumed for the first time by the template); any other string is encoded verbatim, useful when the printed CV should point at a tracked landing page distinct from the canonical homepage. Default `none` keeps the existing layout untouched. QR generation is delegated to `@preview/zebra` — a small, MIT-licensed generator that emits native Typst vector paths, so the matrix renders crisply at any size and inherits the document's accent colour via `fill`. This is the first third-party Typst package altacv depends on; documented in the README. Closes #59
2e49bef to
5b2c4ac
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tests/header_qr_code.typ (1)
17-76: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winAdd a centered-portrait QR scenario to cover the dedicated center-layout branch.
Current fixture misses the
imagePosition: "center"path (with QR present), which has separate composition logic in_header.Suggested test addition
+#pagebreak() + +#alta( + (basics: ( + name: "QR + Portrait (Centered)", + label: "Centered photo, QR beside text row", + email: "qr@example.com", + url: "https://example.com/cv", + image: read("../examples/avatar-placeholder.svg", encoding: none), + )), + preferences: ( + qrCode: "url", + imagePosition: "center", + imageStackOrder: "above", + ), +)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/header_qr_code.typ` around lines 17 - 76, Add a new test case to the header_qr_code.typ file that covers the `imagePosition: "center"` scenario when QR code is enabled. Create a new alta() call with a basics section containing name, label, email, url, and an image field (using the same avatar-placeholder.svg as the other portrait examples), and a preferences section that includes both qrCode: "url" and imagePosition: "center" to exercise the dedicated center-layout composition logic in _header.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib.typ`:
- Around line 665-668: The _qr_code function sets quiet-zone to 0, which
violates the ISO/IEC 18004 QR code standard and causes scanning failures in
print environments. Change the quiet-zone parameter in the qrcode call from 0 to
4 to comply with the standard and ensure proper scanning reliability in printed
outputs.
In `@README.md`:
- Line 306: The documentation contains contradictory statements about how
imagePosition behaves when qrCode is enabled but no portrait is present. The
qrCode table entry and the "Header QR code" section have conflicting
descriptions. Review both the imagePosition documentation and the "Header QR
code" section above line 306 in README.md, then update the qrCode description to
clarify the exact behavior of imagePosition when no portrait exists but qrCode
is enabled. Ensure the statements are consistent and accurately reflect whether
imagePosition is used, ignored, or conditionally applied in this scenario.
---
Outside diff comments:
In `@tests/header_qr_code.typ`:
- Around line 17-76: Add a new test case to the header_qr_code.typ file that
covers the `imagePosition: "center"` scenario when QR code is enabled. Create a
new alta() call with a basics section containing name, label, email, url, and an
image field (using the same avatar-placeholder.svg as the other portrait
examples), and a preferences section that includes both qrCode: "url" and
imagePosition: "center" to exercise the dedicated center-layout composition
logic in _header.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 861fe52f-24f0-41d7-8888-589191ebeb02
⛔ Files ignored due to path filters (2)
examples/preview.pngis excluded by!**/*.pngexamples/tests/header_qr_code.pdfis excluded by!**/*.pdf
📒 Files selected for processing (3)
README.mdlib.typtests/header_qr_code.typ
| #let _qr_code(url, size, accent) = link( | ||
| url, | ||
| qrcode(url, width: size, quiet-zone: 0, fill: accent), | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
For @preview/zebra:0.1.0 qrcode(), what is the recommended quiet-zone for reliable scanning, and does QR guidance still require a 4-module quiet zone?
💡 Result:
For the @preview/zebra:0.1.0 package in Typst, the recommended quiet zone for reliable scanning is 4 modules wide on all sides [1][2]. Regarding the QR code standard, yes, the requirement for a 4-module quiet zone remains active and essential [3][4][5]. The ISO/IEC 18004 specification (including the most recent 2024 version) explicitly defines the quiet zone as a region that must be free of all other markings, surrounding the symbol on all four sides with a width of 4 modules (often denoted as 4X) [3][4][6]. In the context of the zebra package: - The quiet-zone argument accepts an integer, a boolean, or none (defaulting to 0) [1][2]. - When set to true, the package automatically applies the recommended quiet zone of 4 modules for QR codes (and 1 module for Data Matrix) [1][2]. Ignoring this margin is a frequent cause of scanning failures, as the quiet zone is critical for the scanner to detect the boundaries of the code against its background [7][8]. Ensure this area remains clear of text, logos, or other design elements [7][9].
Citations:
- 1: https://typst.app/universe/package/zebra/
- 2: https://github.com/rojul/typst-zebra
- 3: https://gcore.jsdelivr.net/gh/tonycrane/tonycrane.github.io/p/409d352d/ISO_IEC18004-2015.pdf
- 4: https://cdn.standards.iteh.ai/samples/83389/dee29007cb62437f9767323e6af02f90/ISO-IEC-18004-2024.pdf
- 5: https://standards.iteh.ai/catalog/standards/iso/1a0badd4-8d6c-47e7-98fa-ccaddd7cd9a6/iso-iec-18004-2024
- 6: https://cdi.mecon.gob.ar/bases/docelec/az2182.pdf
- 7: https://www.the-qrcode-generator.com/blog/qr-code-quiet-zone
- 8: https://www.qr-insights.com/blog/2026-02-24-qr-code-size-guide-minimum-dimensions
- 9: https://qrcodenova.com/en/blog/qr-code-design-rules
Restore a non-zero QR quiet zone for print reliability.
Setting quiet-zone: 0 violates the ISO/IEC 18004 QR code standard, which requires a 4-module quiet zone on all sides. This is a frequent cause of scanning failures, especially in print/PDF viewers. The @preview/zebra package recommends a 4-module quiet zone for QR codes. Adjust as shown below.
Suggested change
`#let` _qr_code(url, size, accent) = link(
url,
- qrcode(url, width: size, quiet-zone: 0, fill: accent),
+ qrcode(url, width: size, quiet-zone: 4, fill: accent),
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| #let _qr_code(url, size, accent) = link( | |
| url, | |
| qrcode(url, width: size, quiet-zone: 0, fill: accent), | |
| ) | |
| `#let` _qr_code(url, size, accent) = link( | |
| url, | |
| qrcode(url, width: size, quiet-zone: 4, fill: accent), | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib.typ` around lines 665 - 668, The _qr_code function sets quiet-zone to 0,
which violates the ISO/IEC 18004 QR code standard and causes scanning failures
in print environments. Change the quiet-zone parameter in the qrcode call from 0
to 4 to comply with the standard and ensure proper scanning reliability in
printed outputs.
| | `imagePosition` | `"right"` | Where the portrait sits in the header — `"left"` or `"right"` (two-column header) or `"center"` (portrait on its own centred row, stacked with the text block). Ignored when no image is supplied. | | ||
| | `imageStackOrder` | `"above"` | Stack order when `imagePosition` is `"center"` — `"above"` puts the portrait above the name/label/contact block; `"below"` puts it underneath (the "photo as sign-off" look). Ignored for `"left"` / `"right"` positions. | | ||
| | `headerTextAlign` | `"left"` | Horizontal alignment of the header text (name, label, contact bar). Applies whether or not `basics.image` is set, so it also centres the header on image-less CVs. One of `"left"`, `"right"`, `"center"`. The default keeps every line starting at the same edge regardless of which side the photo is on; flip to `"right"` for the mirrored "text hugs the opposite edge" look. | | ||
| | `qrCode` | `none` | Renders a small accent-coloured QR matrix in the header opposite the portrait. `none` (default) skips it. `"url"` encodes `basics.url` (panicking if it's missing or empty). Any other string is encoded verbatim, useful when the printed CV should point at a tracked landing page distinct from `basics.url`. See "Header QR code" above for layout details. | |
There was a problem hiding this comment.
Clarify imagePosition interaction when qrCode is enabled and no portrait is present.
The table text says imagePosition is ignored without basics.image, but QR docs now describe no-portrait QR placement as depending on imagePosition. Please align those statements to avoid contradictory behavior docs.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@README.md` at line 306, The documentation contains contradictory statements
about how imagePosition behaves when qrCode is enabled but no portrait is
present. The qrCode table entry and the "Header QR code" section have
conflicting descriptions. Review both the imagePosition documentation and the
"Header QR code" section above line 306 in README.md, then update the qrCode
description to clarify the exact behavior of imagePosition when no portrait
exists but qrCode is enabled. Ensure the statements are consistent and
accurately reflect whether imagePosition is used, ignored, or conditionally
applied in this scenario.
|
Superseded by #126 — rebuilt against the post-refactor module layout ( |
….url (#126) ## Summary Adds `preferences.qrCode` — opt-in, default `none` — which renders a small accent-coloured QR matrix in the header on the side opposite the portrait. `qrCode: auto` encodes `basics.url`; any non-empty string is encoded verbatim, useful when the printed CV should point at a tracked landing page distinct from the canonical homepage. Closes #59. Supersedes #88 (PR predates the `lib.typ` → `internal/`/`sections/` split + the `internal/labels-en.toml` extraction). ## Notes for the reviewer **New third-party dependency.** QR generation is delegated to [`@preview/zebra:0.1.0`](https://typst.app/universe/package/zebra) — MIT, ~170 LOC + one WASM blob, no transitive Typst deps, emits native vector paths rather than rasterised images. This is the first third-party Typst package altacv depends on, so callers will see a one-time download on first compile after upgrading. Picked zebra over `@preview/cades` (pulls in `@preview/jogs`, a JS interpreter) and `@preview/tiaoma` (Zint-via-WASM, larger). Worth a vet on your side. **Implementation lives in `internal/qr.typ`** so the zebra import stays a one-file change if a different backend becomes preferable. `internal/header.typ` consumes the resolved data via a small module API. **The QR matrix is wrapped in `link()`** so a digital PDF reader can click through to the same destination the QR encodes — print users scan, screen users click. **`auto` sentinel, not `"url"`.** The "encode `basics.url`" path uses Typst's `auto` keyword — the language's idiomatic "derive from context" value — so the API splits cleanly into `none` / `auto` / `"<verbatim URL>"` and the literal three-letter string `"url"` stays available as a valid verbatim payload. **Centred-photo + QR keeps text page-centred.** Under `imagePosition: "center"` + `headerTextAlign: "center"` + a QR, the text-row grid uses `(1fr, auto, 1fr)` with an empty spacer mirroring the QR column, so the header text sits on the page midline instead of drifting left of it. ## Test plan - [x] `make test` — `tests/header_qr_code.typ` plus all pre-existing fixtures green. - [x] Nine-page fixture covers: 1. `auto` form, no portrait 2. Explicit URL string, no portrait 3. QR + portrait (default — photo right, QR left) 4. QR + portrait (`imagePosition: "left"` — flipped) 5. QR themed by a custom `accent` 6. Centred portrait + QR + `headerTextAlign: "center"` 7. QR with no portrait + `imagePosition: "center"` 8. Centred portrait + QR + `imageStackOrder: "below"` 9. Centred portrait + QR + default `headerTextAlign: "left"` (orthogonal axes) - [x] Tracked `examples/tests/header_qr_code.pdf` regenerated to match. - [x] README updated — preference table row, dedicated "Header QR code" subsection, cross-reference from `basics.url`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Added an optional header QR code, controlled by `preferences.qrCode` (`none`, `auto`, or any non-empty string). * With `auto`, the QR encodes `basics.url`; otherwise the provided value is used verbatim. * The QR renders as a clickable link and automatically positions alongside portrait and `imagePosition`, including centred header layouts and accent styling. * **Documentation** * Updated the README with QR configuration, placement behaviour, and preview text. * **Tests** * Added additional scenarios covering more portrait/QR alignment combinations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Shane Murphy <shane@swissborg.com>
Summary
Adds
preferences.qrCode— opt-in, defaultnone— which renders a small accent-coloured QR matrix in the header on the side opposite the portrait.qrCode: "url"encodesbasics.url; any other string is encoded verbatim, useful when the printed CV should point at a tracked landing page distinct from the canonical homepage. Closes #59.Rendered output
QR + portrait (default
imagePosition: "right") — QR lands on the left, photo on the right, header text in the middle. With no portrait the QR still lands on the side oppositeimagePosition. WithimagePosition: "center", the photo stays stacked above/below the text block and the QR sits beside the text row. The matrix is wrapped inlink()so a digital PDF reader can click through to the same destination the QR encodes — the QR is for print, the click is for screen. CI uploads theexample-pdfartifact as usual.Test plan
typst compile --root . examples/example.typ examples/example.pdfsucceedstests/*.typfixture compilestests/header_qr_code.typ) covering"url"form, explicit URL, QR + portrait on each side, and accent-themed QRbasics.urllineNotes for the reviewer
New third-party dependency. QR generation is delegated to
@preview/zebra:0.1.0— a small, MIT-licensed generator that emits native Typst vector paths. It's the first third-party Typst package altacv depends on, so callers will see a one-time download on first compile after upgrading. I picked zebra over@preview/cades(which pulls in@preview/jogs, a JS interpreter) and@preview/tiaoma(Zint-via-WASM, larger) — zebra is ~170 lines + one WASM blob, no transitive Typst deps, and the only one that emits vector paths rather than rasterised images. Worth a vet on your side.Default size is hardcoded at
3.5em. The issue spec calls for~3em. I didn't add aqrSizepreference to keep scope tight — happy to follow up if you'd like one.basics.urlis also rendered in the contact bar (separate feature already onmain). The QR path reuses the same field viaqrCode: "url", so a singlebasics.urlvalue drives both the contact-bar link and the QR — no duplication.QR colour defaults to
preferences.accent. Built-in accent (#00796B, dark teal) gives plenty of contrast on white. Very pale custom accents could in principle scan worse; deferred until anyone complains.JSON Resume alignment
QR rendering is a template-side presentation preference, not part of the JSON Resume schema. When
qrCode: "url", the matrix encodes the schema's canonicalbasics.url; otherwise it encodes any caller-supplied string. No schema keys are introduced or repurposed.Summary by CodeRabbit
New Features
preferences.qrCodewithnone,"url"(usesbasics.url), or any non-empty URL string.imagePosition, rendering alongside the portrait/image as appropriate.Documentation
qrCodepreference, accepted values, QR placement behavior, and render defaults.Tests