v2(Phase 2): port ModernProfessional preset#48
Merged
Conversation
Closes the single-column-only regression that v2 had vs. the legacy
CvBuilder.place(slot, name) API. Single PR, no engine changes, no v1
edits, no behavioural change for existing single-column callers.
What's new
----------
- data/Slot.java — enum {MAIN, SIDEBAR, FOOTER}. MAIN is the default
when a section is built without an explicit slot.
- data/CvDocument refactor: canonical record is now
(CvIdentity, List<Placement>) where Placement(Slot, CvSection).
Custom sections() method still returns the flat list (source-order)
so debug/inspect call sites keep working. New sectionsIn(Slot) and
slotOf(CvSection) accessors.
- CvDocument.Builder gains:
.section(Slot slot, CvSection section)
.sections(Slot slot, CvSection... values)
The pre-existing zero-slot calls (.section(s), .sections(s1, s2))
default to MAIN — every existing call site keeps compiling and
rendering identically.
- @deprecated CvDocument.ofMainSections(identity, list) — migration
helper for any caller that used a direct two-arg constructor. The
Builder is the recommended path forward.
What's updated
--------------
- presets/BoxedSections + presets/MinimalUnderlined now iterate
doc.sectionsIn(Slot.MAIN) instead of doc.sections(). Sidebar /
footer placements are silently dropped by single-column presets —
documented in AUTHORS.md.
- AUTHORS.md gains Recipe 6 ("place sections in slots") with both the
builder-side and the preset-side patterns. Old Recipe 6 (conditional
sections) renumbered to 7.
- package-info.java gains a "Slots — placing sections in columns"
section in the root v2 package.
- data/package-info.java mentions Placement and Slot.
Tests
-----
- New CvDocumentSlotTest with 10 cases (default MAIN, explicit slot,
source-order across slots, sectionsIn filter, slotOf, deprecated
ofMainSections, null guards).
- All existing v2 tests stay green (BoxedSectionsSmokeTest,
MinimalUnderlinedSmokeTest, CvDecorationTest, CvNameTest,
CvContactTest).
- Full mvn test: 881/881 pass.
- cv-boxed-sections-v2.pdf and cv-minimal-underlined.pdf render
pixel-identical to baseline (sample data is all MAIN today).
Second preset on the v2 architecture — proves the compose-don't-subclass pattern works for a visually-different style on the same data + same body renderers. Single-column, no slots needed (those are exercised in Phase 3). What's new ---------- - presets/ModernProfessional.java — Helvetica-based preset with right-aligned big slate-blue display name, flat bright-blue bold section titles (no banner panels), pipe-separated contact + links on the right. Visual signature ported from the v1 preset of the same name. - theme/CvTheme.modernProfessional() factory — Helvetica typography, tighter spacing, classic palette + decoration. Body renderers (ParagraphRenderer, RowRenderer, EntryRenderer, SectionDispatcher) consumed unchanged. - theme/CvTypography.modernProfessional() — Helvetica scale: 28pt name, 17.4pt section title, 10pt body. - theme/CvSpacing.modernProfessional() — tighter spacing for single-page-friendly proportions. - examples/CvModernV2Example — renders cv-modern-professional-v2.pdf against the shared sample data. - ModernProfessionalSmokeTest — 4 tests (identity, default theme, custom theme, theme portability). Architectural notes ------------------- - The preset's three preset-specific colours (slate-blue name, bright-blue section title, royal-blue link) live as private static finals inside the preset class, NOT in CvPalette. The palette has four fields today (ink/muted/rule/banner); adding three more for a single preset would pollute the shared theme record. When a second preset reaches for the same accent colours, extract them to CvPalette and update both call sites. - Section title rendering is an inline private method (renderSectionTitle), same pattern as MinimalUnderlined. Once 3+ presets need a flat-title rendering, factor out a SectionTitleRenderer component with style variants. Until then, per-preset inline keeps each preset readable end-to-end. - Preset reads doc.sectionsIn(Slot.MAIN) — sidebar content is silently dropped, consistent with the single-column convention established in Phase 1. Test results ------------ - 38/38 v2 tests pass (4 new ModernProfessional + 34 prior). - cv-modern-professional-v2.pdf renders cleanly with the v1 visual signature (slate-blue name right, bright-blue section titles, Helvetica body). One row of content spills onto page 2 vs the v1 single-page render — minor spacing fidelity gap, not blocking. Engine and v1 surface untouched.
This was referenced May 24, 2026
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
Second preset on the v2 architecture. Proves the
compose-don't-subclass pattern works for a visually different
style on the same data + same body renderers.
Single-column, no slots needed — slots will be exercised in Phase 3
when porting
TwoColumnSidebar-style multi-column presets.What's new
presets/ModernProfessional.java— Helvetica-based preset:right-aligned big slate-blue display name, flat bright-blue bold
section titles (no banner panels), pipe-separated contact + links
on the right. Visual signature matches v1
ModernProfessional.CvTheme.modernProfessional()factory — Helvetica typography,tighter spacing, classic palette + decoration. Body renderers
(
ParagraphRenderer,RowRenderer,EntryRenderer,SectionDispatcher) consumed unchanged — the win this PR wasbuilt to prove.
CvTypography.modernProfessional()— Helvetica scale (28ptname, 17.4pt section title, 10pt body).
CvSpacing.modernProfessional()— tighter spacing forsingle-page-friendly proportions.
CvModernV2Example— renderscv-modern-professional-v2.pdf.ModernProfessionalSmokeTest— 4 tests (identity, defaulttheme, custom theme, theme portability).
Architectural lessons learned
Documented in the preset's class-level JavaDoc:
Preset-specific colours stay in the preset. Slate-blue name,
bright-blue section title, royal-blue link are private static
finals inside
ModernProfessional, not onCvPalette. Thepalette has four fields today (ink/muted/rule/banner); adding
three more for a single preset would bloat the shared theme
record. When a second preset wants the same accents, extract them
then.
Section title rendering is inlined per-preset. Same pattern as
MinimalUnderlined(Phase 0). Once 3+ presets need flat-titlerendering, factor out a
SectionTitleRendererwith stylevariants. Until then, per-preset inline keeps each preset
readable end-to-end.
Slot filter consistent. Preset reads
doc.sectionsIn(Slot.MAIN)— sidebar content silently dropped,per the convention established in v2(Phase 1): slot mapping — CvDocument supports multi-column placement #47.
What's NOT changed
purely additive in
theme/andpresets/.Test plan
ModernProfessionalSmokeTest— 4 new tests passBoxedSectionsSmokeTest,MinimalUnderlinedSmokeTest,CvDecorationTest,CvDocumentSlotTest,CvNameTest,CvContactTestcv-modern-professional-v2.pdfrendered, matches v1 visualsignature (slate-blue name right, bright-blue section titles,
Helvetica body). One row of content spills onto page 2 vs the
v1 single-page render — minor spacing fidelity gap, not
blocking.
Next phase
Phase 3 — port
TimelineMinimal: two-column with slots (uses #47),vertical timeline rail with circle dots between sections (new
decorative component). First real test of the slot model on a
multi-column layout.