feat(cv-v2): migrate Sidebar Portrait preset (final V1 → v2 port)#66
Merged
Conversation
Ports the legacy Sidebar Portrait CV preset onto the v2 layered architecture. Initial baseline — Projects support comes in a follow-up. Preset * CvTheme.sidebarPortrait() factory wires CvPalette.sidebarPortrait() (near-black ink, mid-grey muted, mid-grey rule, pale-beige sidebar fill), CvTypography.sidebarPortrait() (Crimson Text serif + Lato, 28/8.3/12.0/10.0/8.4/8.0/9.4, 1.35 line spacing), and CvSpacing.sidebarPortrait() (0pt page-flow, 0.55pt accent rule). * Two-column layout (weights 0.34 / 0.66) with RECOMMENDED_MARGIN=0 so the sidebar fill bleeds to the page edge. * Page chrome via DocumentSession.pageBackgrounds: leftColumn(0.34, sidebarFill) + rightColumn(0.66, mainFill=WHITE). The engine paints both fills on every page automatically, so multi-page CVs keep the same visual structure without any preset-side filler logic. * Sidebar — circular portrait photo, contact stack with inline icons, Education entries, Key Skills tokens, Languages. * Main column — hero strip with Crimson Text 28pt name + spaced-caps subtitle (margin-top 70 + fillColor sidebarFill creates the half-on-sidebar / half-on-hero portrait effect), Professional Profile, Experience timeline. * Options API (sidebarFillColor / mainFillColor / accentColor) matches the NordicClean / MonogramSidebar pattern for caller-side colour overrides. * Reuses /templates/cv/sidebar-portrait/portrait.png + icons/*.png resources unchanged from V1. Tests * CvV2VisualParityTest exercises 15 presets (added sidebar_portrait); 15/15 pass at the existing 50k pixel-diff budget against the new visual-baselines/cv-v2-layered/sidebar_portrait-page-0.png baseline. * SidebarPortraitSmokeTest covers stable identity + default-factory and custom-theme render paths.
Mirrors the Profile / Experience structure — section heading + rule followed by stacked project rows. Each project carries a bold title, optional italic stack context parsed via ProjectLabel, and a body paragraph rendered through MarkdownInline.appendTrimmed. PROJECT_LIMIT is capped at 2 because flow.addRow is atomic by engine contract (see RowBuilder line 224 — "tables are splittable and would conflict with the row's atomic pagination"), so the sidebar + main row has to fit on a single page. The canonical sample data with full Profile + 2 experience entries + 2 projects measures within page capacity; raising the limit reintroduces AtomicNodeTooLargeException. True multi-page side-by-side scaling requires a future splittable-row engine primitive — documented inline on PROJECT_LIMIT. Pixel baseline regenerated.
Addresses the WARNs surfaced by graphcompose-cv-v2-migration-review: * Inline comment on the hero name paragraph explains why the Headline.uppercaseCentered widget is bypassed (it would overwrite the hero strip's HERO_PADDING_TOP / BOTTOM via host.padding(theme.spacing().headlinePadding()) and break the on-axis alignment with the photo). * Javadoc on addContactBlock documents the icon-driven inline rationale and points the next caller at extracting an IconContactLine widget once a second preset needs the shape. * SIDEBAR_INNER_WIDTH / PHOTO_DIAMETER / SIDEBAR_HEADER_RULE_WIDTH / MAIN_SECTION_RULE_WIDTH magic constants gain V1-token citations and geometry explanations so a future reader can trace each value back to the canonical SidebarPortrait composition. Pixel baseline regenerated (comment-only diff, but the test framework re-emits the PNG when approve mode runs).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the legacy Sidebar Portrait CV preset onto the v2 layered architecture. With this merge, all 6 V1 CV presets are on v2 — single-column references (BoxedSections, MinimalUnderlined, ModernProfessional) plus the 6 ported designs (Executive, Panel, TimelineMinimal, EngineeringResume, MonogramSidebar, SidebarPortrait).
Visual signature (preserved from V1)
RECOMMENDED_MARGIN=0+ multi-rect page chrome.HERO_TOP_OFFSET), Professional Profile, Experience timeline, Projects list.Architecture highlights
DocumentSession.pageBackgrounds(List.of(PageBackgroundFill.leftColumn(0.34, sidebarFill), PageBackgroundFill.rightColumn(0.66, mainFill)))— engine paints both fills on every page automatically (multi-page-safe).sidebarFillColor/mainFillColor/accentColor) — same pattern asNordicClean.Options/MonogramSidebar.Options.HERO_PADDING_TOP=19+HERO_PADDING_BOTTOM=17renders the strip 1.4× the V1 height whileHERO_TOP_OFFSET=59keeps the on-page centre aligned with the photo axis.ProjectLabel.parse(...), body viaMarkdownInline.appendTrimmed.PROJECT_LIMIT=2documented in code —flow.addRowis atomic by engine contract (seeRowBuilder.java:224"tables are splittable and would conflict with the row's atomic pagination"), so the sidebar + main row must fit on one page. A future splittable-row primitive will lift this limit.Theme additions — additive
CvPalette.sidebarPortrait()— near-black ink, mid-grey muted/rule, pale-beige banner fill.CvTypography.sidebarPortrait()— Crimson Text + Lato, 28/8.3/12.0/10.0/8.4/8.0/9.4, 1.35 line spacing.CvSpacing.sidebarPortrait()— 0pt page-flow, 0.55pt accent rule.CvTheme.sidebarPortrait()factory.Review-driven follow-ups
Ran the new
graphcompose-cv-v2-migration-reviewskill against this branch — 0 FAILs, 4 WARNs. Three doc WARNs addressed in commit0d238d81:Headline.uppercaseCenteredis bypassed.addContactBlockdocuments the icon-driven inline rationale + extraction path for future presets.SIDEBAR_INNER_WIDTH/PHOTO_DIAMETER/SIDEBAR_HEADER_RULE_WIDTH/MAIN_SECTION_RULE_WIDTHgain V1-token citations.The 4th WARN (945-line preset class > 600-line reference bar) stays as a deferred optimisation — Sidebar Portrait is the most feature-rich v2 preset (portrait photo + icon contact stack + 4 sidebar blocks + hero strip + 3 main blocks + Options API + 14 style facade methods). If/when a second preset shares the icon-contact or sidebar-portrait shape, the helpers move into
cv/v2/components.Test plan
./mvnw test -Dtest=com.demcha.compose.document.templates.cv.v2.presets.SidebarPortraitSmokeTest -pl .— 3/3./mvnw test -Dtest=com.demcha.compose.document.templates.cv.v2.presets.CvV2VisualParityTest -Dgraphcompose.visual.approve=true -pl .— 15/15 first-time baseline approval./mvnw test -Dtest=com.demcha.compose.document.templates.cv.v2.presets.CvV2VisualParityTest -pl .— 15/15 without approveexamples/target/generated-pdfs/templates/cv/cv-sidebar-portrait-v2.pdf(3 iterations, final approved)graphcompose-cv-v2-migration-reviewskill — 0 FAILs, WARNs addressedCommits
1d53e4bf— initial baseline (preset + theme + smoke + parity + example)c462ed26— Projects section added to main column0d238d81— review WARN follow-ups (doc comments)