v5.2.0: undo/redo, version history, embedded AI assistant, mobile builder & more (#3205)

This commit is contained in:
Amruth Pillai
2026-07-04 14:57:25 +02:00
committed by GitHub
parent 09bc6ec521
commit 57e9c8c487
181 changed files with 46794 additions and 11348 deletions
+8
View File
@@ -109,6 +109,8 @@ describe("getResumeSectionIcon", () => {
icon: "lightning",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
@@ -127,6 +129,8 @@ describe("getResumeSectionIcon", () => {
icon: "",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
@@ -145,6 +149,8 @@ describe("getResumeSectionIcon", () => {
icon: "",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
@@ -163,6 +169,8 @@ describe("getResumeSectionIcon", () => {
icon: "none",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
+4
View File
@@ -112,6 +112,8 @@ describe("getResumeSectionTitle", () => {
icon: "",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
@@ -130,6 +132,8 @@ describe("getResumeSectionTitle", () => {
icon: "",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [],
},
],
@@ -16,7 +16,7 @@ describe("ExperienceSection", () => {
describe("SectionShell", () => {
it("keeps section and heading style rules when section heading icons are hidden", () => {
expect(source).toContain("<View style={composeStyles(sectionStyle, sectionRuleStyle)}>");
expect(source).toContain("<View style={composeStyles(sectionStyle, sectionRuleStyle)} {...breakProps}>");
expect(source).toContain("<Heading style={composeStyles(sectionHeadingStyle, sectionHeadingRuleStyle)}>");
});
+33 -2
View File
@@ -11,6 +11,8 @@ import type {
ProjectItem,
PublicationItem,
ReferenceItem,
ResumeData,
SectionType,
SkillItem,
SummaryItem,
VolunteerItem,
@@ -179,6 +181,26 @@ const getVisibleItems = <T extends { hidden: boolean }>(section: ItemSection<T>,
return filterItems(section.items, sectionType);
};
// Resolve the per-section page-break constraints the same way section title/icon are resolved:
// standard sections live under data.sections[id], the summary under data.summary, and custom
// sections are matched by id. Missing flags default to false (older stored resumes lack them).
const getSectionBreaks = (data: ResumeData, sectionId: string): { keepTogether: boolean; startOnNewPage: boolean } => {
if (sectionId === "summary") {
return { keepTogether: data.summary.keepTogether, startOnNewPage: data.summary.startOnNewPage };
}
if (sectionId in data.sections) {
const section = data.sections[sectionId as SectionType];
return { keepTogether: section.keepTogether, startOnNewPage: section.startOnNewPage };
}
const customSection = data.customSections.find((section) => section.id === sectionId);
return {
keepTogether: customSection?.keepTogether ?? false,
startOnNewPage: customSection?.startOnNewPage ?? false,
};
};
const SectionShell = ({ sectionId, title, showHeading = true, children }: SectionShellProps) => {
const data = useRender();
const sectionStyle = useTemplateStyle("section");
@@ -189,11 +211,20 @@ const SectionShell = ({ sectionId, title, showHeading = true, children }: Sectio
const sectionTitle = getResumeSectionTitle(data, sectionId, title);
const sectionIcon = getResumeSectionIcon(data, sectionId);
const showIcon = Boolean(sectionIcon) && !data.metadata.page.hideSectionIcons;
const { keepTogether, startOnNewPage } = getSectionBreaks(data, sectionId);
// wrap={false} keeps the whole section on one page; break forces it onto a fresh page.
// Only set the props when enabled so we never pass undefined (exactOptionalPropertyTypes).
const breakProps: { wrap?: false; break?: true } = {};
// ponytail: react-pdf ceiling — wrap={false} keeps a section together only if it fits on
// one page; a section taller than a full page is clipped, not split. No upgrade path in
// react-pdf, so the layout UI warns users this only works for sections that fit one page.
if (keepTogether) breakProps.wrap = false;
if (startOnNewPage) breakProps.break = true;
if (!showIcon) {
// No icon: render heading exactly as before (no structural change)
return (
<View style={composeStyles(sectionStyle, sectionRuleStyle)}>
<View style={composeStyles(sectionStyle, sectionRuleStyle)} {...breakProps}>
{showHeading && (
<Heading style={composeStyles(sectionHeadingStyle, sectionHeadingRuleStyle)}>{sectionTitle}</Heading>
)}
@@ -204,7 +235,7 @@ const SectionShell = ({ sectionId, title, showHeading = true, children }: Sectio
// With icon: wrap in a flex row container that inherits the heading's border/decoration
return (
<View style={composeStyles(sectionStyle, sectionRuleStyle)}>
<View style={composeStyles(sectionStyle, sectionRuleStyle)} {...breakProps}>
{showHeading && (
<View
style={composeStyles(