feat(cv): harden multi-page builder
CI and Deploy / test (pull_request) Failing after 3m0s
CI and Deploy / deploy (pull_request) Has been skipped

Wrap pathological content, paginate oversized entries, measure A4 and Letter previews correctly, unify section ordering, and gate stored-output actions on saved state.
This commit is contained in:
cesnimda
2026-08-15 14:09:51 +02:00
parent d424633f95
commit 5203ddea72
19 changed files with 508 additions and 206 deletions
+32 -21
View File
@@ -93,11 +93,34 @@ public static class CvVariantResolver
built[key] = new CvRenderSection { Key = key, Title = Trim(other.Title) ?? "Additional", Kind = "bullets", Bullets = Clean(other.Items) };
}
// Determine order + visibility from settings, falling back to the default order then any extras.
var settingByKey = settings.Sections.ToDictionary(s => s.Key, s => s, StringComparer.OrdinalIgnoreCase);
var ordered = settings.Sections.Count > 0
? settings.Sections.Select(s => s.Key).ToList()
: DefaultOrder.ToList();
// Custom sections use the same order list as profile-backed sections. Older variants that do
// not yet contain custom:<key> rows still append them in their stored custom-section order.
var customHiddenByKey = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
foreach (var custom in settings.CustomSections)
{
if (string.IsNullOrWhiteSpace(custom.Key)) continue;
var key = $"custom:{custom.Key}";
customHiddenByKey[key] = custom.Hidden;
built[key] = new CvRenderSection
{
Key = key,
Title = Trim(custom.Title) ?? "Additional",
Kind = "bullets",
Bullets = Clean(custom.Items),
};
}
// Determine order + visibility from settings, falling back to the default order then any
// extras. Tolerate malformed/legacy duplicate keys instead of failing the whole render.
var settingByKey = new Dictionary<string, CvSectionSetting>(StringComparer.OrdinalIgnoreCase);
var ordered = new List<string>();
foreach (var section in settings.Sections)
{
if (string.IsNullOrWhiteSpace(section.Key)) continue;
settingByKey[section.Key] = section;
if (!ordered.Contains(section.Key, StringComparer.OrdinalIgnoreCase)) ordered.Add(section.Key);
}
if (ordered.Count == 0) ordered.AddRange(DefaultOrder);
foreach (var key in built.Keys)
{
if (!ordered.Contains(key, StringComparer.OrdinalIgnoreCase)) ordered.Add(key);
@@ -105,7 +128,6 @@ public static class CvVariantResolver
foreach (var key in ordered)
{
if (key.StartsWith("custom:", StringComparison.OrdinalIgnoreCase)) continue; // handled below
if (!built.TryGetValue(key, out var section)) continue;
if (settingByKey.TryGetValue(key, out var cfg))
{
@@ -116,22 +138,11 @@ public static class CvVariantResolver
section.Entries = ReorderByKey(section.Entries, cfg.ItemOrder);
}
}
if (!section.IsEmpty) model.Sections.Add(section);
}
// Variant-only custom sections, placed by their position in the order list if present.
foreach (var custom in settings.CustomSections)
{
if (custom.Hidden) continue;
var items = Clean(custom.Items);
if (items.Count == 0) continue;
model.Sections.Add(new CvRenderSection
else if (customHiddenByKey.TryGetValue(key, out var customHidden) && customHidden)
{
Key = $"custom:{custom.Key}",
Title = Trim(custom.Title) ?? "Additional",
Kind = "bullets",
Bullets = items,
});
continue;
}
if (!section.IsEmpty) model.Sections.Add(section);
}
return model;