mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-10 21:15:04 +10:00
refactor(docx): getBaseRun helper, hoist mainConfig, derive sections, remove dead OL code
- Add getBaseRun() next to getHtmlStyle() and replace 8 inline baseRun spreads (~27 lines). - Hoist one mainConfig object in buildDocument; sidebar call spreads only the two differing color keys (~17 lines). - Remove BUILT_IN_SECTIONS Set; derive membership via `sectionId in data.sections` (~14 lines). - Delete unreachable ordered-list numbering machinery in html-to-docx.ts: numberingRef is always undefined through all call paths, so isOrdered && numberingRef is always false (~15 lines). Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
This commit is contained in:
@@ -122,27 +122,13 @@ function blendWithWhite(hex: string, opacity: number): string {
|
||||
|
||||
// --- Section rendering dispatch ---
|
||||
|
||||
const BUILT_IN_SECTIONS = new Set<string>([
|
||||
"profiles",
|
||||
"experience",
|
||||
"education",
|
||||
"projects",
|
||||
"skills",
|
||||
"languages",
|
||||
"interests",
|
||||
"awards",
|
||||
"certifications",
|
||||
"publications",
|
||||
"volunteer",
|
||||
"references",
|
||||
]);
|
||||
|
||||
function renderSection(sectionId: string, data: ResumeData, colorHex: string): Paragraph[] {
|
||||
if (sectionId === "summary") {
|
||||
return renderSummary(data.summary, colorHex);
|
||||
}
|
||||
|
||||
if (BUILT_IN_SECTIONS.has(sectionId)) {
|
||||
// ponytail: data.sections keys are the source of truth; no need to maintain a parallel Set
|
||||
if (sectionId in data.sections) {
|
||||
const section = data.sections[sectionId as SectionType];
|
||||
if (section) {
|
||||
return renderBuiltInSection(sectionId as SectionType, section, colorHex);
|
||||
@@ -405,17 +391,19 @@ export function buildDocument(data: ResumeData): Document {
|
||||
|
||||
const documentChildren: (Paragraph | Table)[] = [];
|
||||
|
||||
// ponytail: hoisted once; sidebar call spreads only the two differing color keys
|
||||
const mainConfig = {
|
||||
headingFont,
|
||||
headingSizeHalfPt: headingSize,
|
||||
bodyFont,
|
||||
bodySizeHalfPt: bodySize,
|
||||
textColorHex,
|
||||
primaryColorHex: colorHex,
|
||||
};
|
||||
|
||||
// Header placement depends on template
|
||||
if (templateConfig.headerPosition === "full-width") {
|
||||
// Configure colors for main content
|
||||
setRenderConfig({
|
||||
headingFont,
|
||||
headingSizeHalfPt: headingSize,
|
||||
bodyFont,
|
||||
bodySizeHalfPt: bodySize,
|
||||
textColorHex,
|
||||
primaryColorHex: colorHex,
|
||||
});
|
||||
setRenderConfig(mainConfig);
|
||||
documentChildren.push(...buildHeader(data, colorHex, textColorHex));
|
||||
}
|
||||
|
||||
@@ -424,27 +412,13 @@ export function buildDocument(data: ResumeData): Document {
|
||||
const isFullWidth = layoutPage.fullWidth || layoutPage.sidebar.length === 0;
|
||||
|
||||
if (isFullWidth) {
|
||||
setRenderConfig({
|
||||
headingFont,
|
||||
headingSizeHalfPt: headingSize,
|
||||
bodyFont,
|
||||
bodySizeHalfPt: bodySize,
|
||||
textColorHex,
|
||||
primaryColorHex: colorHex,
|
||||
});
|
||||
setRenderConfig(mainConfig);
|
||||
for (const sectionId of [...layoutPage.main, ...layoutPage.sidebar]) {
|
||||
documentChildren.push(...renderSection(sectionId, data, colorHex));
|
||||
}
|
||||
} else {
|
||||
// Render main sections with normal colors
|
||||
setRenderConfig({
|
||||
headingFont,
|
||||
headingSizeHalfPt: headingSize,
|
||||
bodyFont,
|
||||
bodySizeHalfPt: bodySize,
|
||||
textColorHex,
|
||||
primaryColorHex: colorHex,
|
||||
});
|
||||
setRenderConfig(mainConfig);
|
||||
|
||||
const mainParagraphs: Paragraph[] = [];
|
||||
if (templateConfig.headerPosition === "main-only") {
|
||||
@@ -455,14 +429,7 @@ export function buildDocument(data: ResumeData): Document {
|
||||
}
|
||||
|
||||
// Render sidebar sections with potentially inverted colors
|
||||
setRenderConfig({
|
||||
headingFont,
|
||||
headingSizeHalfPt: headingSize,
|
||||
bodyFont,
|
||||
bodySizeHalfPt: bodySize,
|
||||
textColorHex: sidebarTextColorHex,
|
||||
primaryColorHex: sidebarHeadingColorHex,
|
||||
});
|
||||
setRenderConfig({ ...mainConfig, textColorHex: sidebarTextColorHex, primaryColorHex: sidebarHeadingColorHex });
|
||||
|
||||
const sidebarParagraphs: Paragraph[] = [];
|
||||
if (templateConfig.headerPosition === "sidebar-only") {
|
||||
|
||||
@@ -22,7 +22,6 @@ interface InlineStyle {
|
||||
}
|
||||
|
||||
type InlineChild = TextRun | ExternalHyperlink;
|
||||
type NumberingOptions = { reference: string; level: number; instance?: number };
|
||||
|
||||
/** Module-level link color, set per htmlToParagraphs invocation. */
|
||||
let currentLinkColor = "0563C1";
|
||||
@@ -123,18 +122,7 @@ function collectInlineChildren(node: Node, style: InlineStyle): InlineChild[] {
|
||||
return children;
|
||||
}
|
||||
|
||||
function getNumberingOptions(reference: string, level: number, instance?: number): NumberingOptions {
|
||||
return instance === undefined ? { reference, level } : { reference, level, instance };
|
||||
}
|
||||
|
||||
function processBlockElement(
|
||||
el: HTMLElement,
|
||||
style: InlineStyle,
|
||||
paragraphs: Paragraph[],
|
||||
listLevel?: number,
|
||||
numberingRef?: string,
|
||||
listIndex?: number,
|
||||
): void {
|
||||
function processBlockElement(el: HTMLElement, style: InlineStyle, paragraphs: Paragraph[], listLevel?: number): void {
|
||||
const tag = el.tagName;
|
||||
const mergedStyle = mergeStyle(style, tag, el);
|
||||
|
||||
@@ -149,20 +137,13 @@ function processBlockElement(
|
||||
if (tag === "P" || tag === "DIV") {
|
||||
const inlineChildren = collectInlineChildren(el, mergedStyle);
|
||||
if (inlineChildren.length > 0) {
|
||||
paragraphs.push(
|
||||
new Paragraph({
|
||||
children: inlineChildren,
|
||||
...(listLevel != null && numberingRef
|
||||
? { numbering: getNumberingOptions(numberingRef, listLevel, listIndex) }
|
||||
: {}),
|
||||
}),
|
||||
);
|
||||
paragraphs.push(new Paragraph({ children: inlineChildren }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (tag === "UL" || tag === "OL") {
|
||||
const isOrdered = tag === "OL";
|
||||
// ponytail: ordered-list numbering (numberingRef path) was never reachable; always uses bullet
|
||||
const level = listLevel != null ? listLevel + 1 : 0;
|
||||
|
||||
for (const li of el.children) {
|
||||
@@ -177,30 +158,16 @@ function processBlockElement(
|
||||
if (liChild.nodeType === Node.TEXT_NODE) {
|
||||
const text = (liChild.textContent ?? "").trim();
|
||||
if (text) {
|
||||
paragraphs.push(
|
||||
new Paragraph({
|
||||
children: [new TextRun({ text, ...mergedStyle })],
|
||||
...(isOrdered && numberingRef
|
||||
? { numbering: getNumberingOptions(numberingRef, level, listIndex) }
|
||||
: { bullet: { level } }),
|
||||
}),
|
||||
);
|
||||
paragraphs.push(new Paragraph({ children: [new TextRun({ text, ...mergedStyle })], bullet: { level } }));
|
||||
}
|
||||
} else if (liChild.nodeType === Node.ELEMENT_NODE) {
|
||||
processBlockElement(liChild as HTMLElement, mergedStyle, paragraphs, level, numberingRef, listIndex);
|
||||
processBlockElement(liChild as HTMLElement, mergedStyle, paragraphs, level);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const inlineChildren = collectInlineChildren(li, mergedStyle);
|
||||
if (inlineChildren.length > 0) {
|
||||
paragraphs.push(
|
||||
new Paragraph({
|
||||
children: inlineChildren,
|
||||
...(isOrdered && numberingRef
|
||||
? { numbering: getNumberingOptions(numberingRef, level, listIndex) }
|
||||
: { bullet: { level } }),
|
||||
}),
|
||||
);
|
||||
paragraphs.push(new Paragraph({ children: inlineChildren, bullet: { level } }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,15 @@ function getHtmlStyle(): HtmlStyleConfig {
|
||||
};
|
||||
}
|
||||
|
||||
/** Base TextRun properties (font, size, color) shared across all section renderers. */
|
||||
function getBaseRun() {
|
||||
return {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a section heading paragraph with primary color and bottom border.
|
||||
* Uses the heading typography set via `setRenderConfig`.
|
||||
@@ -70,11 +79,7 @@ function sectionHeading(title: string, colorHex: string): Paragraph {
|
||||
}
|
||||
|
||||
function titleAndSubtitle(primary: string, secondary: string, rightText?: string): Paragraph {
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
const children: (TextRun | ExternalHyperlink)[] = [new TextRun({ text: primary, bold: true, ...baseRun })];
|
||||
|
||||
if (secondary) {
|
||||
@@ -151,12 +156,7 @@ function renderExperience(section: Sections["experience"], colorHex: string): Pa
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
if (item.roles && item.roles.length > 0) {
|
||||
@@ -202,11 +202,7 @@ function renderEducation(section: Sections["education"], colorHex: string): Para
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const degreeArea = [item.degree, item.area].filter(Boolean).join(", ");
|
||||
@@ -259,11 +255,7 @@ function renderSkills(section: Sections["skills"], colorHex: string): Paragraph[
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const children: TextRun[] = [new TextRun({ text: item.name, bold: true, ...baseRun })];
|
||||
@@ -287,11 +279,7 @@ function renderLanguages(section: Sections["languages"], colorHex: string): Para
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const children: TextRun[] = [new TextRun({ text: item.language, bold: true, ...baseRun })];
|
||||
@@ -311,11 +299,7 @@ function renderInterests(section: Sections["interests"], colorHex: string): Para
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const children: TextRun[] = [new TextRun({ text: item.name, bold: true, ...baseRun })];
|
||||
@@ -418,11 +402,7 @@ function renderReferences(section: Sections["references"], colorHex: string): Pa
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const children: TextRun[] = [new TextRun({ text: item.name, bold: true, ...baseRun })];
|
||||
@@ -457,11 +437,7 @@ function renderProfiles(section: Sections["profiles"], colorHex: string): Paragr
|
||||
if (section.hidden || items.length === 0) return [];
|
||||
|
||||
const paragraphs: Paragraph[] = [sectionHeading(section.title, colorHex)];
|
||||
const baseRun = {
|
||||
...(bodyFont ? { font: bodyFont } : {}),
|
||||
...(bodySize ? { size: bodySize } : {}),
|
||||
...(textColor ? { color: textColor } : {}),
|
||||
};
|
||||
const baseRun = getBaseRun();
|
||||
|
||||
for (const item of items) {
|
||||
const children: (TextRun | ExternalHyperlink)[] = [new TextRun({ text: item.network, bold: true, ...baseRun })];
|
||||
|
||||
Reference in New Issue
Block a user