feat(career): theme polish, rich-text bullets, entry ordering, outline API
Phase 4.5 backend enablers. - Themes (priority 3): AtsFriendly flag on single-column themes (surfaced in GET /api/cv/themes), print-quality page-break rules (entries never split across a page; headings stay with content; widow/orphan control), darkened the creative sidebar for AA contrast. - Rich text (priority 1): bullets/summary support **bold**, *italic*, __underline__, [text](url) via a safe inline pass — everything is HTML-escaped first, so no user tag can survive; only the whitelist emits markup. - Entry ordering (priority 1): CvSectionSetting.ItemOrder reorders entries within a section by ItemKey, never touching the master profile. - Outline API: GET /api/cv/outline returns the master profile as sections+entries with ItemKeys, so the Content tab can render editable per-item rows. - 3 new tests (22 total in the builder suite). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+10
-6
@@ -47,6 +47,10 @@ public sealed class CvTheme
|
||||
|
||||
public bool DefaultIcons { get; init; }
|
||||
|
||||
// ATS-friendly = single-column, no sidebar tables/graphics that trip naive resume parsers. Set on
|
||||
// the single-column themes; surfaced in GET /api/cv/themes so the picker can badge it.
|
||||
public bool AtsFriendly { get; init; }
|
||||
|
||||
// Which section keys render in the sidebar for two-column layouts (ignored for single/header-band).
|
||||
public List<string> SidebarSections { get; init; } = new() { "contact", "skills", "languages" };
|
||||
}
|
||||
@@ -62,7 +66,7 @@ public static class CvThemeCatalog
|
||||
Description = "Clean SaaS-style layout with a confident accent header.",
|
||||
Layout = "header-band", HeaderStyle = "band", HeadingStyle = "caps-rule",
|
||||
Accent = "#2563eb", HeadingFont = "'Segoe UI', Roboto, Arial, sans-serif", BodyFont = "'Segoe UI', Roboto, Arial, sans-serif",
|
||||
PhotoShape = "rounded", DefaultIcons = true,
|
||||
PhotoShape = "rounded", DefaultIcons = true, AtsFriendly = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
@@ -71,7 +75,7 @@ public static class CvThemeCatalog
|
||||
Layout = "single", HeaderStyle = "plain", HeadingStyle = "plain",
|
||||
Accent = "#111827", HeadingColor = "#111827", Muted = "#6b7280",
|
||||
HeadingFont = "'Helvetica Neue', Arial, sans-serif", BodyFont = "'Helvetica Neue', Arial, sans-serif",
|
||||
SectionGapMm = 7, EntryGapMm = 5, PhotoShape = "none",
|
||||
SectionGapMm = 7, EntryGapMm = 5, PhotoShape = "none", AtsFriendly = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
@@ -80,7 +84,7 @@ public static class CvThemeCatalog
|
||||
Layout = "single", HeaderStyle = "centered", HeadingStyle = "underline",
|
||||
Accent = "#7c2d12", Ink = "#1c1917", Muted = "#57534e", Line = "#1c1917",
|
||||
HeadingFont = "Georgia, 'Times New Roman', serif", BodyFont = "Georgia, 'Times New Roman', serif",
|
||||
NameSizePt = 27, PhotoShape = "square",
|
||||
NameSizePt = 27, PhotoShape = "square", AtsFriendly = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
@@ -99,7 +103,7 @@ public static class CvThemeCatalog
|
||||
Layout = "single", HeaderStyle = "plain", HeadingStyle = "caps-rule",
|
||||
Accent = "#334155", Ink = "#111827", Muted = "#374151",
|
||||
HeadingFont = "Arial, Helvetica, sans-serif", BodyFont = "Arial, Helvetica, sans-serif",
|
||||
PhotoShape = "none",
|
||||
PhotoShape = "none", AtsFriendly = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
@@ -117,14 +121,14 @@ public static class CvThemeCatalog
|
||||
Layout = "single", HeaderStyle = "kicker", HeadingStyle = "underline",
|
||||
Accent = "#7c3aed", Ink = "#1f2937", Muted = "#4b5563",
|
||||
HeadingFont = "Georgia, 'Times New Roman', serif", BodyFont = "'Segoe UI', Arial, sans-serif",
|
||||
NameSizePt = 26, SectionGapMm = 7, PhotoShape = "circle",
|
||||
NameSizePt = 26, SectionGapMm = 7, PhotoShape = "circle", AtsFriendly = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Id = "creative", Name = "Creative", Category = "Creative",
|
||||
Description = "Bold accent sidebar and photo-forward header for design and product roles.",
|
||||
Layout = "sidebar-left", HeaderStyle = "band", HeadingStyle = "bar",
|
||||
Accent = "#db2777", Ink = "#18181b", Muted = "#52525b", SidebarBg = "#db2777", SidebarInk = "#ffffff",
|
||||
Accent = "#db2777", Ink = "#18181b", Muted = "#52525b", SidebarBg = "#be185d", SidebarInk = "#ffffff",
|
||||
HeadingFont = "'Poppins', 'Segoe UI', Arial, sans-serif", BodyFont = "'Segoe UI', Arial, sans-serif",
|
||||
PhotoShape = "circle", DefaultIcons = true,
|
||||
SidebarSections = new() { "contact", "skills", "languages", "interests" },
|
||||
|
||||
@@ -39,6 +39,10 @@ public sealed class CvSectionSetting
|
||||
public string Key { get; set; } = string.Empty; // summary|skills|experience|education|projects|certifications|languages|interests|links|custom:<k>
|
||||
public bool Hidden { get; set; }
|
||||
public string? Title { get; set; } // renamed heading
|
||||
// Explicit entry order for this section, as a list of item ItemKeys. Entries not listed keep their
|
||||
// master order and come last. Null => master profile order. Lets a variant reorder entries without
|
||||
// touching the master profile.
|
||||
public List<string>? ItemOrder { get; set; }
|
||||
}
|
||||
|
||||
public sealed class CvItemOverride
|
||||
|
||||
Reference in New Issue
Block a user