Skip to content

feat(cv-v2): migrate Sidebar Portrait preset (final V1 → v2 port)#66

Merged
DemchaAV merged 3 commits into
developfrom
feature/cv-v2-sidebar-portrait-preset
May 28, 2026
Merged

feat(cv-v2): migrate Sidebar Portrait preset (final V1 → v2 port)#66
DemchaAV merged 3 commits into
developfrom
feature/cv-v2-sidebar-portrait-preset

Conversation

@DemchaAV

Copy link
Copy Markdown
Owner

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)

  • Pale-beige left sidebar (34% of page width) bleeding edge-to-edge thanks to RECOMMENDED_MARGIN=0 + multi-rect page chrome.
  • Circular Crimson Text portrait (98pt), icon-driven contact stack, sidebar accent-rule headings → Education / Key Skills / Languages.
  • Right column: hero strip with serif name + spaced-caps subtitle (positioned so its vertical centre lines up with the photo via HERO_TOP_OFFSET), Professional Profile, Experience timeline, Projects list.

Architecture highlights

  • Two-column page chrome via 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).
  • Options API (sidebarFillColor / mainFillColor / accentColor) — same pattern as NordicClean.Options / MonogramSidebar.Options.
  • Hero strip mathHERO_PADDING_TOP=19 + HERO_PADDING_BOTTOM=17 renders the strip 1.4× the V1 height while HERO_TOP_OFFSET=59 keeps the on-page centre aligned with the photo axis.
  • Projects section mirrors the Profile/Experience visual grammar — bold title + italic stack context via ProjectLabel.parse(...), body via MarkdownInline.appendTrimmed.
  • PROJECT_LIMIT=2 documented in code — flow.addRow is atomic by engine contract (see RowBuilder.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-review skill against this branch — 0 FAILs, 4 WARNs. Three doc WARNs addressed in commit 0d238d81:

  • Inline comment on the hero name paragraph explains why Headline.uppercaseCentered is bypassed.
  • Javadoc on addContactBlock documents the icon-driven inline rationale + extraction path for future presets.
  • SIDEBAR_INNER_WIDTH / PHOTO_DIAMETER / SIDEBAR_HEADER_RULE_WIDTH / MAIN_SECTION_RULE_WIDTH gain 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 approve
  • Visual review of examples/target/generated-pdfs/templates/cv/cv-sidebar-portrait-v2.pdf (3 iterations, final approved)
  • Migration review via graphcompose-cv-v2-migration-review skill — 0 FAILs, WARNs addressed
  • CI green

Commits

  • 1d53e4bf — initial baseline (preset + theme + smoke + parity + example)
  • c462ed26 — Projects section added to main column
  • 0d238d81 — review WARN follow-ups (doc comments)

DemchaAV added 3 commits May 28, 2026 01:48
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).
@DemchaAV DemchaAV merged commit 3e3b6fd into develop May 28, 2026
9 checks passed
@DemchaAV DemchaAV deleted the feature/cv-v2-sidebar-portrait-preset branch May 28, 2026 01:45
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