Skip to content

feat(templates): add the NavySidebar CV preset and a portrait on CvIdentity - #614

Merged
DemchaAV merged 2 commits into
feature/template-promotionfrom
feat/navy-sidebar-cv-preset
Aug 30, 2026
Merged

DemchaAV merged 2 commits into
feature/template-promotionfrom
feat/navy-sidebar-cv-preset

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

Why

The second CV bundle in the promotion queue is the first one with a face in it. The family had nowhere to put one: CvIdentity carried the name, the title, the contact triple and the links, and SidebarPortrait — the preset named for portraits — draws a packaged silhouette because there was no field to read.

So this promotion is two things: the sheet, and the one model change the CV family actually needed.

What changed

  • CvIdentity carries an optional portrait. Optional<DocumentImageData> portrait, set through CvIdentity.Builder.portrait(...) — the image itself, because a photograph is caller-supplied content rather than template chrome, and because it belongs to the person rather than to one preset's options. The four- and three-argument constructors are kept explicitly, so existing calls compile and link unchanged; CvIdentityPortraitTest pins that, along with the null normalisation.
  • cv/presets/NavySidebar — a DocumentTemplate<CvDocument> via create(), split for the 500-LOC rule into package-private NavySidebarStyles (the measured geometry and the Lato scale), NavySidebarWidgets (the two headings and the indented list), NavySidebarAside, NavySidebarMain and NavySidebarIcons. It sets A4 with no margin and paints the navy plate as a page background.
  • The roles hang on a timeline rail: the rail is the section's left accent, which runs the full height of what the section holds — so the last role's body is composed in a second section outside the rail, and the line stops at the last marker instead of running to the foot of the block. Each entry head is as tall as its marker, with the title and the dates pulled up by half their own overhang to sit on the marker's axis; those two overflows are derived in NavySidebarStyles so the ends of the band cannot drift apart.
  • Languages are a RowsSection, not levelled skills. This design writes the proficiency out — "Native", "Advanced", "Intermediate" — which a number could not carry back.
  • The preset upper-cases for the author: the name, the role, every degree and every heading. That is the sheet, and a document written in title case still reads as it.
  • The seven marks ship inside the templates artifact under templates/cv/navy-sidebar/icons/, each at the point size the design draws it at.

Verification

Full reactor gate (the eight CI modules) → BUILD SUCCESS; the preset's qa suite plus the new data test 15/15; examples suite 75/75 including CommittedAssetDriftTest with the new preview; javadoc:javadoc → BUILD SUCCESS with no warning naming a new file.

Port parity, verified against the published template itself: the preset fed the template's own fixture is pixel-identical to the standalone render — 0 / 2,173,720 differing pixels, and the layout snapshot matches node for node — 87 of 87, zero geometry delta. The only difference in the two snapshots is that eight education paragraphs the bundle left unnamed are named here. The bundle's geometry is the same on its pinned 2.2.0 and on this branch (87/87 nodes, zero deltas > 0.01pt); the two engines differ only by the released ligature fix (#601), so the current-engine render is the parity target.

Notes

  • The sheet holds one page. Its two columns are a single row, and a row is atomic, so a CV longer than the sheet raises AtomicNodeTooLargeException rather than flowing onto a second page. It caps nothing, which is the deliberate half: the capped sidebar presets drop entries silently, and a CV that quietly loses a job is worse than one that refuses to compose. Documented on the class, in using-templates.md beside the caps table, and pinned by a test — the same contract ProfessionalSidebar landed with.
  • The contact rows carry no link targets, because the published sheet draws them as text. Making them clickable would be a change to the design rather than a port of it, and it belongs in a follow-up that owns the baseline move. The packaged set also has one network mark, so every link is drawn behind it; the class says so.
  • SidebarPortrait still draws its packaged silhouette and does not read the new field — teaching it to would move its pixel baseline, which is not this PR's business. Worth a follow-up.
  • The example's portrait is a flat silhouette drawn for the repository rather than a photograph, and the qa fixture draws its own at fixture time: a licensed face guarding a layout is a liability, and what the gates need is only that the disc has something in it.

Lane: canonical (templates.cv.data + templates.cv.presets + qa + examples) — no engine changes.

The sample addresses in both sidebar CV examples were wider than the
sidebar's text column, so the row wrapped and left the mark alone on its
first line. A channel is one paragraph by design — the mark and the value
share a line — so the fix is a value that fits, and both preset classes now
say how much room there is.
@DemchaAV
DemchaAV merged commit 6ad4018 into feature/template-promotion Aug 30, 2026
12 checks passed
@DemchaAV
DemchaAV deleted the feat/navy-sidebar-cv-preset branch August 30, 2026 22:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant