diff --git a/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java b/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java index 8d1581dca..e1fc61a1e 100644 --- a/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java +++ b/examples/src/main/java/com/demcha/examples/support/ExampleDataFactory.java @@ -11,7 +11,6 @@ import com.demcha.compose.document.templates.cv.v2.data.SkillsSection; import com.demcha.compose.document.templates.data.common.EmailYaml; import com.demcha.compose.document.templates.data.common.Header; -import com.demcha.compose.document.templates.data.coverletter.CoverLetterDocumentSpec; import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec; import com.demcha.compose.document.templates.data.cv.CvDocumentSpec; import com.demcha.compose.document.templates.data.proposal.ProposalDocumentSpec; @@ -65,33 +64,6 @@ public static Header sampleHeader() { .build(); } - public static String sampleCoverLetter() { - return """ - Hiring team at ${companyName}, - - I am excited to share my interest in the Senior Platform Engineer role. My recent work has focused on building reusable document-generation systems that balance public API design, render quality, and maintainability. - - I enjoy translating fuzzy workflow requirements into clear template abstractions, reliable test coverage, and examples that make adoption easier for the rest of the team. - - I would welcome the opportunity to bring that same mix of engineering rigor and product thinking to your platform group. - """; - } - - public static CoverLetterDocumentSpec sampleCoverLetterDocument() { - return CoverLetterDocumentSpec.builder() - .header(sampleHeader()) - .letter(sampleCoverLetter()) - .job(job -> job - .url("https://northwind.example/jobs/platform") - .title("Senior Platform Engineer") - .company("Northwind Systems") - .location("London / Remote") - .description("Lead reusable internal platform capabilities.") - .seniorityLevel("Senior") - .employmentType("Full-time")) - .build(); - } - public static InvoiceDocumentSpec sampleInvoice() { return InvoiceDocumentSpec.builder() .title("Invoice") diff --git a/src/main/java/com/demcha/compose/document/templates/coverletter/builder/CoverLetterBuilder.java b/src/main/java/com/demcha/compose/document/templates/coverletter/builder/CoverLetterBuilder.java deleted file mode 100644 index 6b976eece..000000000 --- a/src/main/java/com/demcha/compose/document/templates/coverletter/builder/CoverLetterBuilder.java +++ /dev/null @@ -1,260 +0,0 @@ -package com.demcha.compose.document.templates.coverletter.builder; - -import com.demcha.compose.document.api.DocumentSession; -import com.demcha.compose.document.node.ContainerNode; -import com.demcha.compose.document.node.DocumentNode; -import com.demcha.compose.document.node.ParagraphNode; -import com.demcha.compose.document.node.TextAlign; -import com.demcha.compose.document.style.DocumentInsets; -import com.demcha.compose.document.style.DocumentTextStyle; -import com.demcha.compose.document.templates.api.DocumentTemplate; -import com.demcha.compose.document.templates.components.Header; -import com.demcha.compose.document.templates.core.text.MarkdownText; -import com.demcha.compose.document.templates.coverletter.layouts.LetterFormat; -import com.demcha.compose.document.templates.coverletter.spec.CoverLetterHeader; -import com.demcha.compose.document.templates.coverletter.spec.CoverLetterSpec; -import com.demcha.compose.document.templates.themes.Spacing; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Fluent builder for assembling a Templates v2 cover-letter - * {@link DocumentTemplate}. - * - *
A cover-letter preset wraps one builder call inside its - * {@code create(BusinessTheme)} factory — the same flat-recipe - * pattern used by CV presets. Each preset shares the typography - * palette of its paired CV preset (same heading style, same body - * style, same spacing rhythm) so a writer's CV and cover letter - * read as one matched set.
- * - *All seven knobs ({@code id}, {@code displayName}, - * {@code header}, {@code layout}, {@code bodyStyle}, {@code spacing}, - * and at least implicit alignment via the layout / body style) must - * be configured before calling {@link #build()}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard) — the layered model
- * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument}
- * plus the {@code coverletter.v2} presets. Kept for backward
- * compatibility; scheduled for removal in a future major. See
- * {@code docs/templates/v2-layered/}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class CoverLetterBuilder {
-
- private String id;
- private String displayName;
- private Header header;
- private LetterFormat layout;
- private DocumentTextStyle bodyStyle;
- private Spacing spacing;
-
- private CoverLetterBuilder() {
- }
-
- /**
- * Returns a fresh builder.
- *
- * @return new builder instance
- */
- public static CoverLetterBuilder builder() {
- return new CoverLetterBuilder();
- }
-
- /**
- * Sets the stable identifier exposed via
- * {@link DocumentTemplate#id()}.
- *
- * @param value non-null identifier
- * @return this builder
- */
- public CoverLetterBuilder id(String value) {
- this.id = Objects.requireNonNull(value, "id");
- return this;
- }
-
- /**
- * Sets the human-readable display name.
- *
- * @param value non-null display name
- * @return this builder
- */
- public CoverLetterBuilder displayName(String value) {
- this.displayName = Objects.requireNonNull(value, "displayName");
- return this;
- }
-
- /**
- * Sets the header component used to render the document header.
- *
- * @param value non-null header component
- * @return this builder
- */
- public CoverLetterBuilder header(Header value) {
- this.header = Objects.requireNonNull(value, "header");
- return this;
- }
-
- /**
- * Sets the layout responsible for arranging header + letter
- * blocks.
- *
- * @param value non-null layout
- * @return this builder
- */
- public CoverLetterBuilder layout(LetterFormat value) {
- this.layout = Objects.requireNonNull(value, "layout");
- return this;
- }
-
- /**
- * Sets the text style applied to greeting, body paragraphs, and
- * closing.
- *
- * @param value non-null body text style
- * @return this builder
- */
- public CoverLetterBuilder bodyStyle(DocumentTextStyle value) {
- this.bodyStyle = Objects.requireNonNull(value, "bodyStyle");
- return this;
- }
-
- /**
- * Sets the active spacing tokens.
- *
- * @param value non-null spacing tokens
- * @return this builder
- */
- public CoverLetterBuilder spacing(Spacing value) {
- this.spacing = Objects.requireNonNull(value, "spacing");
- return this;
- }
-
- /**
- * Validates configuration and returns the assembled
- * {@link DocumentTemplate}.
- *
- * @return ready-to-use template instance
- * @throws NullPointerException if any required setter has not been
- * called
- */
- public DocumentTemplateThe single class of interest is - * {@link com.demcha.compose.document.templates.coverletter.builder.CoverLetterBuilder}. - * Preset classes wrap one builder call inside their - * {@code create(BusinessTheme)} factory; users wanting a custom - * cover-letter preset copy that factory body and tweak the chain.
- * - * @since 1.6.0 - */ -package com.demcha.compose.document.templates.coverletter.builder; diff --git a/src/main/java/com/demcha/compose/document/templates/coverletter/layouts/LetterFormat.java b/src/main/java/com/demcha/compose/document/templates/coverletter/layouts/LetterFormat.java deleted file mode 100644 index 4d5c5227a..000000000 --- a/src/main/java/com/demcha/compose/document/templates/coverletter/layouts/LetterFormat.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.demcha.compose.document.templates.coverletter.layouts; - -import com.demcha.compose.document.node.ContainerNode; -import com.demcha.compose.document.node.DocumentNode; -import com.demcha.compose.document.style.DocumentInsets; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * Templates v2 cover-letter layout. - * - *Single-column layout with generous paragraph spacing — header - * sits at the top, then greeting, body paragraphs, and closing - * stack vertically beneath it. There is no slot map: a cover - * letter is structurally simpler than a CV (one continuous reading - * flow), so the layout takes the rendered nodes in source order and - * emits one container.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard) — the layered model
- * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument}
- * plus the {@code coverletter.v2} presets. Kept for backward
- * compatibility; scheduled for removal in a future major. See
- * {@code docs/templates/v2-layered/}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class LetterFormat {
-
- private static final String LAYOUT_NAME = "layout.letterFormat";
-
- private double moduleGap = 0.0;
-
- private LetterFormat() {
- }
-
- /**
- * Returns a fresh layout with zero gap (caller drives spacing
- * through node margins).
- *
- * @return new layout instance
- */
- public static LetterFormat layout() {
- return new LetterFormat();
- }
-
- /**
- * Sets the vertical gap between top-level letter blocks (header,
- * greeting, body, closing).
- *
- * @param value non-negative finite gap in points
- * @return this layout (for chaining)
- * @throws IllegalArgumentException if {@code value} is negative,
- * {@code NaN}, or infinite
- */
- public LetterFormat moduleGap(double value) {
- if (Double.isNaN(value) || Double.isInfinite(value) || value < 0) {
- throw new IllegalArgumentException("moduleGap must be finite and non-negative: " + value);
- }
- this.moduleGap = value;
- return this;
- }
-
- /**
- * Composes the final {@link DocumentNode} from a header plus
- * ordered list of letter blocks (greeting, body, closing).
- *
- * @param header non-null header node
- * @param blocks non-null ordered list of letter blocks
- * @return container node holding the header and blocks in order
- * @throws NullPointerException if either argument is null
- */
- public DocumentNode compose(DocumentNode header, ListDeprecated surface. These are the older Gen-2 - * cover-letter layouts. They are not the current standard. The - * current standard is the layered surface - * {@code com.demcha.compose.document.templates.coverletter.v2} (data / theme / - * components / widgets / presets). This package is kept only for backward - * compatibility and is scheduled for removal in a future major.
- * - *Currently a single layout - * ({@link com.demcha.compose.document.templates.coverletter.layouts.LetterFormat}) - * covers all 14 cover-letter pair presets.
- * - *New code should target the layered {@code coverletter.v2} surface - * instead. See {@code docs/templates/v2-layered/}.
- * - * @since 1.6.0 - */ -@Deprecated(since = "1.7.0", forRemoval = true) -package com.demcha.compose.document.templates.coverletter.layouts; diff --git a/src/main/java/com/demcha/compose/document/templates/coverletter/package-info.java b/src/main/java/com/demcha/compose/document/templates/coverletter/package-info.java index 818e6a9bf..2bd13f3c7 100644 --- a/src/main/java/com/demcha/compose/document/templates/coverletter/package-info.java +++ b/src/main/java/com/demcha/compose/document/templates/coverletter/package-info.java @@ -1,42 +1,15 @@ /** - * Superseded Gen-2 cover-letter domain — layout, presets, builder, and spec - * data types. + * Cover-letter template family. * - *Deprecated surface. This package is the older Gen-2 - * cover-letter template stack. It is not the current standard. The - * current standard is the layered surface - * {@code com.demcha.compose.document.templates.coverletter.v2} (data / theme / - * components / widgets / presets). This package is kept only for backward - * compatibility and is scheduled for removal in a future major.
+ *The shipping cover-letter surface is the layered stack under + * {@code coverletter.v2} (data / theme / components / widgets / presets), + * built on the shared {@code templates.core} layer and a {@code BrandTheme}.
* - *Sub-packages partition the (deprecated) domain by concern:
- * - *New code should target the layered {@code coverletter.v2} surface - * instead. See {@code docs/templates/v2-layered/}.
- * - *Naming note: the user-facing concept is - * "cover-letter" with a hyphen, but Java packages cannot contain hyphens. - * The package name {@code coverletter} drops the hyphen for compatibility; - * file id strings and example file names retain the hyphenated form - * (e.g. {@code cover-letter-modern-professional.pdf}).
+ *Naming note: the user-facing concept is "cover-letter" + * with a hyphen, but Java packages cannot contain hyphens. The package name + * {@code coverletter} drops the hyphen; file id strings and example file names + * retain the hyphenated form (e.g. {@code cover-letter-modern-professional.pdf}).
* * @since 1.6.0 */ -@Deprecated(since = "1.7.0", forRemoval = true) package com.demcha.compose.document.templates.coverletter; diff --git a/src/main/java/com/demcha/compose/document/templates/coverletter/presets/BlueBannerLetter.java b/src/main/java/com/demcha/compose/document/templates/coverletter/presets/BlueBannerLetter.java deleted file mode 100644 index d733e0659..000000000 --- a/src/main/java/com/demcha/compose/document/templates/coverletter/presets/BlueBannerLetter.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.demcha.compose.document.templates.coverletter.presets; - -import com.demcha.compose.document.style.DocumentColor; -import com.demcha.compose.document.style.DocumentTextDecoration; -import com.demcha.compose.document.style.DocumentTextStyle; -import com.demcha.compose.document.templates.api.DocumentTemplate; -import com.demcha.compose.document.templates.components.Header; -import com.demcha.compose.document.templates.coverletter.builder.CoverLetterBuilder; -import com.demcha.compose.document.templates.coverletter.layouts.LetterFormat; -import com.demcha.compose.document.templates.coverletter.spec.CoverLetterSpec; -import com.demcha.compose.document.templates.themes.Spacing; -import com.demcha.compose.document.theme.BusinessTheme; -import com.demcha.compose.font.FontName; - -/** - * Templates v2 cover-letter pair for {@code BlueBanner} CV preset. - * - *PT Serif headline + Lato body, dark INK with the soft-blue - * BANNER_BG used by - * {@link com.demcha.compose.document.templates.cv.presets.BlueBanner} - * for accent runs.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.BlueBannerLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class BlueBannerLetter {
-
- /** Stable template identifier. */
- public static final String ID = "blue-banner-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Blue Banner Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(20, 25, 35);
- private static final DocumentColor SOFT = DocumentColor.rgb(85, 85, 85);
- private static final DocumentColor BANNER_BG = DocumentColor.rgb(112, 146, 190);
-
- private BlueBannerLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code BlueBanner} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Blue Banner Letter"
- */
- public static DocumentTemplatePT Serif throughout, dark grey ink — matches - * {@link com.demcha.compose.document.templates.cv.presets.BoxedSections}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.BoxedSectionsLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class BoxedSectionsLetter {
-
- /** Stable template identifier. */
- public static final String ID = "boxed-sections-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Boxed Sections Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(34, 34, 34);
- private static final DocumentColor MUTED = DocumentColor.rgb(120, 120, 120);
- private static final DocumentColor RULE = DocumentColor.rgb(170, 170, 170);
-
- private BoxedSectionsLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code BoxedSections} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Boxed Sections Letter"
- */
- public static DocumentTemplatePoppins headline + Lato body, dark grey ink — matches - * {@link com.demcha.compose.document.templates.cv.presets.CenteredHeadline}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.CenteredHeadlineLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class CenteredHeadlineLetter {
-
- /** Stable template identifier. */
- public static final String ID = "centered-headline-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Centered Headline Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(54, 54, 54);
- private static final DocumentColor HEADLINE = DocumentColor.rgb(70, 70, 70);
- private static final DocumentColor SOFT = DocumentColor.rgb(105, 105, 105);
- private static final DocumentColor RULE = DocumentColor.rgb(188, 188, 188);
-
- private CenteredHeadlineLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code CenteredHeadline} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Centered Headline Letter"
- */
- public static DocumentTemplatePT Serif throughout with the bronze accent and warm INK palette - * of {@link com.demcha.compose.document.templates.cv.presets.ClassicSerif}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.ClassicSerifLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class ClassicSerifLetter {
-
- /** Stable template identifier. */
- public static final String ID = "classic-serif-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Classic Serif Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(45, 43, 40);
- private static final DocumentColor MUTED = DocumentColor.rgb(105, 101, 94);
- private static final DocumentColor ACCENT = DocumentColor.rgb(126, 93, 52);
-
- private ClassicSerifLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code ClassicSerif} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Classic Serif Letter"
- */
- public static DocumentTemplateIBM Plex Mono headline + Lato body, dark INK ink with the - * teal-blue ACCENT used by - * {@link com.demcha.compose.document.templates.cv.presets.CompactMono}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.CompactMonoLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class CompactMonoLetter {
-
- /** Stable template identifier. */
- public static final String ID = "compact-mono-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Compact Mono Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(28, 34, 42);
- private static final DocumentColor MUTED = DocumentColor.rgb(84, 96, 112);
- private static final DocumentColor ACCENT = DocumentColor.rgb(0, 126, 151);
-
- private CompactMonoLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code CompactMono} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Compact Mono Letter"
- */
- public static DocumentTemplateHelvetica throughout, deep navy ink with bright editorial blue - * accent — matches - * {@link com.demcha.compose.document.templates.cv.presets.EditorialBlue}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.EditorialBlueLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class EditorialBlueLetter {
-
- /** Stable template identifier. */
- public static final String ID = "editorial-blue-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Editorial Blue Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(18, 31, 72);
- private static final DocumentColor BODY = DocumentColor.rgb(60, 72, 106);
- private static final DocumentColor ACCENT = DocumentColor.rgb(86, 136, 255);
-
- private EditorialBlueLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code EditorialBlue} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Editorial Blue Letter"
- */
- public static DocumentTemplateNavy primary, green accent, Barlow headline + Lato body — matches - * {@link com.demcha.compose.document.templates.cv.presets.EngineeringResume}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.EngineeringResumeLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class EngineeringResumeLetter {
-
- /** Stable template identifier. */
- public static final String ID = "engineering-resume-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Engineering Resume Letter";
-
- private static final DocumentColor NAVY = DocumentColor.rgb(13, 32, 47);
- private static final DocumentColor INK = DocumentColor.rgb(32, 42, 55);
- private static final DocumentColor MUTED = DocumentColor.rgb(91, 105, 119);
- private static final DocumentColor GREEN = DocumentColor.rgb(27, 145, 104);
-
- private EngineeringResumeLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code EngineeringResume} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Engineering Resume Letter"
- */
- public static DocumentTemplateSlate primary, bronze accent, Poppins headline + Lato body — - * matches {@link com.demcha.compose.document.templates.cv.presets.Executive}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.ExecutiveLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class ExecutiveLetter {
-
- /** Stable template identifier. */
- public static final String ID = "executive-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Executive Letter";
-
- private static final DocumentColor PRIMARY = DocumentColor.rgb(24, 35, 51);
- private static final DocumentColor BODY = DocumentColor.rgb(49, 58, 72);
- private static final DocumentColor ACCENT = DocumentColor.rgb(172, 112, 55);
-
- private ExecutiveLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code Executive} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Executive Letter"
- */
- public static DocumentTemplateVisual pair of - * {@link com.demcha.compose.document.templates.cv.presets.ModernProfessional}. - * Same right-aligned header (slate-blue name, royal-blue underlined - * contact links), same Helvetica body type. The cover letter itself - * is a single-column letter — header on top, greeting, body - * paragraphs separated by paragraph spacing, closing.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.ModernProfessionalLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class ModernProfessionalLetter {
-
- /** Stable template identifier. */
- public static final String ID = "modern-professional-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Modern Professional Letter";
-
- /** V1 {@code BrandTheme} primary slate-blue used by the display name. */
- private static final DocumentColor NAME_COLOR = DocumentColor.rgb(44, 62, 80);
-
- /** V1 link accent (royal blue) used by the contact link row. */
- private static final DocumentColor LINK_COLOR = DocumentColor.rgb(65, 105, 225);
-
- private ModernProfessionalLetter() {
- }
-
- /**
- * Builds a fresh cover-letter template paired with the
- * Modern Professional CV style.
- *
- * @param theme active business theme
- * @return ready-to-use template
- * @throws NullPointerException if {@code theme} is null
- */
- public static DocumentTemplateCrimson Text headline + Lato body, deep slate ink with muted gold - * accent — matches - * {@link com.demcha.compose.document.templates.cv.presets.MonogramSidebar}. - * The cover letter is a simple single-column letter — the CV's - * monogram sidebar is intentionally not replicated.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.MonogramSidebarLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class MonogramSidebarLetter {
-
- /** Stable template identifier. */
- public static final String ID = "monogram-sidebar-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Monogram Sidebar Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(37, 45, 58);
- private static final DocumentColor SOFT = DocumentColor.rgb(112, 119, 125);
- private static final DocumentColor ACCENT = DocumentColor.rgb(158, 146, 104);
-
- private MonogramSidebarLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code MonogramSidebar} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Monogram Sidebar Letter"
- */
- public static DocumentTemplateSame INK / ACCENT palette and Barlow + Lato typography as the - * {@link com.demcha.compose.document.templates.cv.presets.NordicClean} - * CV. Single-column letter format — header on top, greeting, body - * paragraphs, closing.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.NordicCleanLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class NordicCleanLetter {
-
- /** Stable template identifier. */
- public static final String ID = "nordic-clean-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Nordic Clean Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(18, 39, 52);
- private static final DocumentColor MUTED = DocumentColor.rgb(82, 104, 116);
- private static final DocumentColor ACCENT = DocumentColor.rgb(28, 128, 135);
-
- private NordicCleanLetter() {
- }
-
- /**
- * Builds a fresh cover-letter template paired with the
- * Nordic Clean CV style.
- *
- * @param theme active business theme
- * @return ready-to-use template
- * @throws NullPointerException if {@code theme} is null
- */
- public static DocumentTemplateDeep-slate primary, teal accent, Poppins headline + Lato body — - * matches {@link com.demcha.compose.document.templates.cv.presets.Panel}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.PanelLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class PanelLetter {
-
- /** Stable template identifier. */
- public static final String ID = "panel-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Panel Letter";
-
- private static final DocumentColor HEADER_TEXT = DocumentColor.rgb(20, 44, 66);
- private static final DocumentColor BODY = DocumentColor.rgb(54, 68, 84);
- private static final DocumentColor ACCENT = DocumentColor.rgb(0, 128, 128);
-
- private PanelLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code Panel} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Panel Letter"
- */
- public static DocumentTemplateCrimson Text serif headline + Lato body in the restrained grey - * palette of - * {@link com.demcha.compose.document.templates.cv.presets.SidebarPortrait}. - * The cover letter is a simple single-column letter — the CV's - * portrait sidebar is intentionally not replicated.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.SidebarPortraitLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class SidebarPortraitLetter {
-
- /** Stable template identifier. */
- public static final String ID = "sidebar-portrait-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Sidebar Portrait Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(34, 34, 34);
- private static final DocumentColor SOFT = DocumentColor.rgb(85, 85, 85);
- private static final DocumentColor ACCENT = DocumentColor.rgb(106, 106, 106);
-
- private SidebarPortraitLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code SidebarPortrait} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Sidebar Portrait Letter"
- */
- public static DocumentTemplateAll-grey palette with Barlow Condensed for the headline and Lato - * body — matches - * {@link com.demcha.compose.document.templates.cv.presets.TimelineMinimal}.
- * - * @deprecated Superseded by the layered…v2… surface (the current
- * standard). Kept for backward compatibility; scheduled for removal
- * in a future major. See {@code docs/templates/v2-layered/} and
- * {@link com.demcha.compose.document.templates.coverletter.v2.presets.TimelineMinimalLetter}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public final class TimelineMinimalLetter {
-
- /** Stable template identifier. */
- public static final String ID = "timeline-minimal-letter";
-
- /** Human-readable display name. */
- public static final String DISPLAY_NAME = "Timeline Minimal Letter";
-
- private static final DocumentColor INK = DocumentColor.rgb(74, 74, 74);
- private static final DocumentColor SOFT = DocumentColor.rgb(122, 122, 122);
-
- private TimelineMinimalLetter() {
- }
-
- /**
- * Builds the cover-letter template paired with the {@code TimelineMinimal} CV preset.
- *
- * @param theme the active theme supplying palette, typography, and spacing
- * @return a {@code DocumentTemplate} for the "Timeline Minimal Letter"
- */
- public static DocumentTemplateDeprecated surface. These are the older Gen-2 - * cover-letter presets. They are not the current standard. The - * current standard is the layered surface - * {@code com.demcha.compose.document.templates.coverletter.v2.presets}. This - * package is kept only for backward compatibility and is scheduled for - * removal in a future major.
- * - *Each preset shares the typography palette and spacing rhythm - * of its paired CV preset, so a writer's CV and cover letter ship - * as a matched set with consistent visual identity.
- * - *New code should target the layered {@code coverletter.v2} presets - * instead. See {@code docs/templates/v2-layered/}.
- * - * @since 1.6.0 - */ -@Deprecated(since = "1.7.0", forRemoval = true) -package com.demcha.compose.document.templates.coverletter.presets; diff --git a/src/main/java/com/demcha/compose/document/templates/coverletter/spec/CoverLetterHeader.java b/src/main/java/com/demcha/compose/document/templates/coverletter/spec/CoverLetterHeader.java deleted file mode 100644 index ceff37f2c..000000000 --- a/src/main/java/com/demcha/compose/document/templates/coverletter/spec/CoverLetterHeader.java +++ /dev/null @@ -1,180 +0,0 @@ -package com.demcha.compose.document.templates.coverletter.spec; - -import java.util.List; -import java.util.Objects; - -/** - * Top-of-document identity block for a cover letter in Templates v2. - * - *Mirrors {@code CvHeader} structurally because the user-facing - * concept (name, address, phone, email, links) is identical. The - * domain types are kept separate so the cover-letter and CV - * surfaces can evolve independently if needed.
- * - * @param name document subject's name (required, non-blank) - * @param address optional address line; empty string when absent - * @param phone optional phone number; empty string when absent - * @param email optional email address; empty string when absent - * @param links ordered list of {@link Link} entries (typically - * LinkedIn, GitHub); never null after construction - * @deprecated Superseded by the layered…v2… surface (the current
- * standard) — the layered model
- * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument}
- * plus the {@code coverletter.v2} presets. Kept for backward
- * compatibility; scheduled for removal in a future major. See
- * {@code docs/templates/v2-layered/}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public record CoverLetterHeader(
- String name,
- String address,
- String phone,
- String email,
- List links) {
-
- /**
- * Compact constructor that normalises null strings to empty and
- * defensively copies the link list.
- *
- * @throws NullPointerException if {@code name} is null
- * @throws IllegalArgumentException if {@code name} is blank
- */
- public CoverLetterHeader {
- Objects.requireNonNull(name, "name");
- if (name.isBlank()) {
- throw new IllegalArgumentException("name must not be blank");
- }
- address = address == null ? "" : address;
- phone = phone == null ? "" : phone;
- email = email == null ? "" : email;
- links = links == null ? List.of() : List.copyOf(links);
- }
-
- /**
- * Returns the contact items (address, phone) in source order,
- * skipping blank fields.
- *
- * @return ordered list of non-blank contact strings
- */
- public ListA cover letter is structurally simpler than a CV: a header - * identifying the writer, a greeting line ({@code "Dear Hiring - * Manager,"}), a sequence of body paragraphs, and a closing line - * ({@code "Sincerely, Artem"}). Each segment is rendered by the - * paired preset using the same typography palette as its CV - * counterpart so a writer can ship a CV and matching cover letter - * with consistent visual identity.
- * - * @param header identity block (required) - * @param greeting first body line (required, may be blank to - * suppress); typically {@code "Dear Hiring - * Manager,"} - * @param bodyParagraphs ordered list of body paragraphs; blank - * paragraphs are kept to preserve the - * writer's intended whitespace; may carry - * markdown markers ({@code **bold**}, - * {@code *italic*}) - * @param closing last body line (required, may be blank); - * typically {@code "Sincerely, Alex"} - * @deprecated Superseded by the layered…v2… surface (the current
- * standard) — the layered model
- * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument}
- * plus the {@code coverletter.v2} presets. Kept for backward
- * compatibility; scheduled for removal in a future major. See
- * {@code docs/templates/v2-layered/}.
- */
-@Deprecated(since = "1.7.0", forRemoval = true)
-public record CoverLetterSpec(
- CoverLetterHeader header,
- String greeting,
- ListDeprecated surface. These are the older Gen-2 - * cover-letter spec records. They are not the current standard. The - * current standard is the layered model - * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument} - * in the {@code com.demcha.compose.document.templates.coverletter.v2} - * surface. This package is kept only for backward compatibility and is - * scheduled for removal in a future major.
- * - *This package holds the immutable records a user fills with their - * cover-letter content before passing the spec to a Gen-2 preset for - * rendering:
- * - *New code should target the layered {@code coverletter.v2} data model - * instead. See {@code docs/templates/v2-layered/}.
- * - * @since 1.6.0 - */ -@Deprecated(since = "1.7.0", forRemoval = true) -package com.demcha.compose.document.templates.coverletter.spec; diff --git a/src/main/java/com/demcha/compose/document/templates/data/coverletter/CoverLetterDocumentSpec.java b/src/main/java/com/demcha/compose/document/templates/data/coverletter/CoverLetterDocumentSpec.java deleted file mode 100644 index f4dc69a42..000000000 --- a/src/main/java/com/demcha/compose/document/templates/data/coverletter/CoverLetterDocumentSpec.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.demcha.compose.document.templates.data.coverletter; - -import com.demcha.compose.document.templates.data.common.Header; - -import java.util.Objects; -import java.util.function.Consumer; - -/** - * Public compose-first cover-letter input. - * - *Authoring role: keeps cover-letter composition document-shaped: - * one header, one body, and optional job context for personalization.
- * - * @param header contact/profile header rendered before the letter body - * @param body cover-letter body text - * @param jobDetails target role metadata used by templates - * @author Artem Demchyshyn - * @deprecated Test-only dead code; the live CV/cover-letter model is - * {@code cv.v2} / {@code coverletter.v2}. Kept for backward - * compatibility; scheduled for removal in a future major. See - * {@code docs/templates/v2-layered/} and - * {@link com.demcha.compose.document.templates.coverletter.v2.data.CoverLetterDocument}. - */ -@Deprecated(since = "1.7.0", forRemoval = true) -public record CoverLetterDocumentSpec( - Header header, - String body, - JobDetails jobDetails -) { - - /** - * Creates a normalized cover-letter spec. - */ - public CoverLetterDocumentSpec { - body = Objects.requireNonNullElse(body, ""); - } - - /** - * Creates a document spec from the common cover-letter inputs. - * - * @param header contact/profile header - * @param body cover-letter body text - * @param jobDetails target role metadata - * @return document spec - */ - public static CoverLetterDocumentSpec of(Header header, String body, JobDetails jobDetails) { - return new CoverLetterDocumentSpec(header, body, jobDetails); - } - - /** - * Starts a fluent cover-letter spec builder. - * - * @return document builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Builder for cover-letter document specs. - */ - public static final class Builder { - private Header header; - private String body; - private JobDetails jobDetails; - - private Builder() { - } - - /** - * Sets the contact/profile header. - * - * @param header header block - * @return this builder - */ - public Builder header(Header header) { - this.header = header; - return this; - } - - /** - * Configures the contact/profile header. - * - * @param spec header builder callback - * @return this builder - */ - public Builder header(ConsumerPipeline role: captures the target role, company, and optional job - * posting metadata so template composers can resolve letter copy and chrome.
- * - *Mutability: immutable record. Thread-safety: thread-safe - * when referenced through immutable component values.
- * - * @param url optional job posting URL - * @param title target role title - * @param company target company name - * @param location target job location - * @param description optional job description text - * @param seniorityLevel optional seniority label - * @param employmentType optional employment type label - */ -@JsonInclude(JsonInclude.Include.NON_NULL) -public record JobDetails( - String url, - String title, - String company, - String location, - String description, - String seniorityLevel, - String employmentType) { - - /** - * Starts a fluent job details builder. - * - * @return job details builder - */ - public static Builder builder() { - return new Builder(); - } - - /** - * Fluent builder for job details. - */ - public static final class Builder { - private String url; - private String title; - private String company; - private String location; - private String description; - private String seniorityLevel; - private String employmentType; - - private Builder() { - } - - /** - * Sets the job posting URL. - * - * @param url job posting URL - * @return this builder - */ - public Builder url(String url) { - this.url = url; - return this; - } - - /** - * Sets the target role title. - * - * @param title role title - * @return this builder - */ - public Builder title(String title) { - this.title = title; - return this; - } - - /** - * Sets the company name. - * - * @param company company name - * @return this builder - */ - public Builder company(String company) { - this.company = company; - return this; - } - - /** - * Sets the job location. - * - * @param location job location - * @return this builder - */ - public Builder location(String location) { - this.location = location; - return this; - } - - /** - * Sets the job description text. - * - * @param description job description text - * @return this builder - */ - public Builder description(String description) { - this.description = description; - return this; - } - - /** - * Sets the seniority label. - * - * @param seniorityLevel seniority label - * @return this builder - */ - public Builder seniorityLevel(String seniorityLevel) { - this.seniorityLevel = seniorityLevel; - return this; - } - - /** - * Sets the employment type label. - * - * @param employmentType employment type label - * @return this builder - */ - public Builder employmentType(String employmentType) { - this.employmentType = employmentType; - return this; - } - - /** - * Builds immutable job details. - * - * @return job details - */ - public JobDetails build() { - return new JobDetails( - url, - title, - company, - location, - description, - seniorityLevel, - employmentType); - } - } -} diff --git a/src/main/java/com/demcha/compose/document/templates/data/coverletter/package-info.java b/src/main/java/com/demcha/compose/document/templates/data/coverletter/package-info.java deleted file mode 100644 index 30e6a8b6a..000000000 --- a/src/main/java/com/demcha/compose/document/templates/data/coverletter/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Cover-letter document specs and supporting data records for canonical templates. - */ -package com.demcha.compose.document.templates.data.coverletter; diff --git a/src/main/java/com/demcha/compose/document/templates/data/package-info.java b/src/main/java/com/demcha/compose/document/templates/data/package-info.java index 03f4e01c3..84f1013d7 100644 --- a/src/main/java/com/demcha/compose/document/templates/data/package-info.java +++ b/src/main/java/com/demcha/compose/document/templates/data/package-info.java @@ -3,8 +3,8 @@ * {@code document.templates} layer. * *Concrete public data types live in child packages such as - * {@code data.cv}, {@code data.invoice}, {@code data.proposal}, - * {@code data.coverletter}, and {@code data.schedule}. Shared contact/link - * types live in {@code data.common}.
+ * {@code data.cv}, {@code data.invoice}, {@code data.proposal}, and + * {@code data.schedule}. Shared contact/link types live in + * {@code data.common}. */ package com.demcha.compose.document.templates.data; diff --git a/src/test/java/com/demcha/compose/document/templates/TemplateTestSupport.java b/src/test/java/com/demcha/compose/document/templates/TemplateTestSupport.java index f0af670fd..e27454083 100644 --- a/src/test/java/com/demcha/compose/document/templates/TemplateTestSupport.java +++ b/src/test/java/com/demcha/compose/document/templates/TemplateTestSupport.java @@ -3,16 +3,13 @@ import com.demcha.compose.GraphCompose; import com.demcha.compose.document.api.DocumentPageSize; import com.demcha.compose.document.api.DocumentSession; -import com.demcha.compose.document.templates.data.coverletter.CoverLetterDocumentSpec; import com.demcha.compose.document.templates.data.invoice.InvoiceData; import com.demcha.compose.document.templates.data.invoice.InvoiceDocumentSpec; -import com.demcha.compose.document.templates.data.coverletter.JobDetails; import com.demcha.compose.document.templates.data.proposal.ProposalData; import com.demcha.compose.document.templates.data.proposal.ProposalDocumentSpec; import com.demcha.compose.document.templates.data.schedule.WeeklyScheduleData; import com.demcha.compose.document.templates.data.schedule.WeeklyScheduleDocumentSpec; import com.demcha.compose.font.FontName; -import com.demcha.mock.CoverLetterMock; import com.demcha.mock.InvoiceDataFixtures; import com.demcha.mock.ProposalDataFixtures; import com.demcha.mock.WeeklyScheduleDataFixtures; @@ -50,21 +47,6 @@ public final class TemplateTestSupport { private TemplateTestSupport() { } - public static String coverLetter(String companyName) { - return CoverLetterMock.letter.replace("${companyName}", companyName); - } - - public static JobDetails jobDetails(String companyName) { - return new JobDetails( - "https://linkedin.com/jobs/view/visual-test", - "Software Engineer", - companyName, - "Remote", - "Visual verification test", - "Mid", - "Full-time"); - } - public static InvoiceData canonicalInvoiceData() { return InvoiceDataFixtures.standardInvoice(); } diff --git a/src/test/java/com/demcha/compose/document/templates/coverletter/presets/CoverLetterGalleryTest.java b/src/test/java/com/demcha/compose/document/templates/coverletter/presets/CoverLetterGalleryTest.java deleted file mode 100644 index b088146b0..000000000 --- a/src/test/java/com/demcha/compose/document/templates/coverletter/presets/CoverLetterGalleryTest.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.demcha.compose.document.templates.coverletter.presets; - -import com.demcha.compose.GraphCompose; -import com.demcha.compose.document.api.DocumentPageSize; -import com.demcha.compose.document.api.DocumentSession; -import com.demcha.compose.document.templates.api.DocumentTemplate; -import com.demcha.compose.document.templates.coverletter.spec.CoverLetterHeader; -import com.demcha.compose.document.templates.coverletter.spec.CoverLetterSpec; -import com.demcha.compose.document.theme.BusinessTheme; -import org.junit.jupiter.api.Test; - -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.function.Function; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Renders every Templates v2 cover-letter preset against the same - * shared sample spec and writes the results to - * {@code target/visual-tests/clean/templates/coverletter/v2/} for - * human glance comparison. The 14 letter renders should read as a - * matched set with the sibling 14 CV preset renders in - * {@code .../templates/cv/v2/}. - */ -class CoverLetterGalleryTest { - - private static final BusinessTheme THEME = BusinessTheme.modern(); - - private static final Path OUTPUT_DIR = Path.of( - "target", "visual-tests", "clean", "templates", "coverletter", "v2"); - - private static CoverLetterSpec sampleSpec() { - return CoverLetterSpec.builder() - .header(CoverLetterHeader.builder() - .name("Artem Demchyshyn") - .address("London, UK") - .phone("+44 20 5555 1000") - .email("artem@demo.dev") - .link("LinkedIn", "https://linkedin.com/in/graphcompose") - .link("GitHub", "https://github.com/DemchaAV") - .build()) - .greeting("Dear Hiring Team at **Northwind Systems**,") - .paragraph("I am excited to share my interest in the Senior " - + "Platform Engineer role. My recent work has focused " - + "on building **reusable document-generation systems** " - + "that balance public API design, render quality, and " - + "maintainability.") - .paragraph("I enjoy translating fuzzy workflow requirements into " - + "clear template abstractions, reliable test coverage, " - + "and examples that make adoption easier for the rest " - + "of the team.") - .paragraph("I would welcome the opportunity to bring that same " - + "mix of engineering rigor and product thinking to your " - + "platform group.") - .closing("Sincerely, *Artem Demchyshyn*") - .build(); - } - - private void render(String slug, FunctionA snapshot mismatch means the preset's rendered tree drifted — - * either intentionally (re-run with - * {@code -Dgraphcompose.updateSnapshots=true}) or as a regression.
- */ -class PresetLayoutSnapshotTest { - - private static final BusinessTheme THEME = BusinessTheme.modern(); - - private static CoverLetterSpec canonicalLetterSpec() { - return CoverLetterSpec.builder() - .header(CoverLetterHeader.builder() - .name("Artem Demchyshyn") - .address("London, UK") - .phone("+44 20 5555 1000") - .email("artem@demo.dev") - .link("LinkedIn", "https://linkedin.com/in/graphcompose") - .link("GitHub", "https://github.com/DemchaAV") - .build()) - .greeting("Dear Hiring Team at **Northwind Systems**,") - .paragraph("I am excited to share my interest in the Senior " - + "Platform Engineer role. My recent work has focused " - + "on building **reusable document-generation systems**.") - .paragraph("I enjoy translating fuzzy workflow requirements into " - + "clear template abstractions and reliable test coverage.") - .paragraph("I would welcome the opportunity to bring that mix " - + "of engineering rigor and product thinking to your team.") - .closing("Sincerely, *Artem Demchyshyn*") - .build(); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("presets") - void shouldMatchLayoutSnapshot(String slug, - FunctionEach preset renders a fixed canonical {@link CoverLetterSpec} on - * full A4 with the gallery-standard 48pt margin, the resulting PDF is - * rasterized via PDFBox, and the per-pixel diff against a checked-in - * baseline PNG is asserted to stay within the {@code PIXEL_DIFF_BUDGET} - * mismatched-pixel budget at {@code PER_PIXEL_TOLERANCE} per-channel - * colour tolerance. Re-run with - * {@code -Dgraphcompose.visual.approve=true} to refresh the baselines - * after a deliberate visual change.
- * - *Baselines live under - * {@code src/test/resources/visual-baselines/coverletter-v2/}.
- */ -class PresetVisualParityTest { - - private static final BusinessTheme THEME = BusinessTheme.modern(); - - private static final Path BASELINE_ROOT = Path.of( - "src", "test", "resources", "visual-baselines", "coverletter-v2"); - - // Calibrated for cross-platform PDFBox font + colour rendering - // drift between Windows-recorded baselines and Linux CI. Budget - // sized to cover the v1.6.0 CI observation (worst preset - // modern_professional / editorial_blue at ~7k mismatched - // pixels = 1.4% of 595x841) with a comfortable margin. - private static final long PIXEL_DIFF_BUDGET = 20000L; - private static final int PER_PIXEL_TOLERANCE = 8; - private static final float MARGIN = 48f; - - @ParameterizedTest(name = "{0}") - @MethodSource("presets") - void rendersWithinPixelDiffBudget(String slug, - Function