From 5fb4976ec9cadb54105801c13ccaed83f6c3250c Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Mon, 1 Jun 2026 15:30:49 +0200 Subject: [PATCH] feat: add hide link underline option to resume settings, resolves #3134 --- .../-sidebar/right/sections/page.tsx | 24 + .../src/reactive-resume-v4-json.broad.test.ts | 4 + .../import/src/reactive-resume-v4-json.tsx | 1 + .../src/templates/shared/primitives.test.ts | 7 + .../pdf/src/templates/shared/primitives.tsx | 14 +- .../templates/shared/rich-text-stylesheet.ts | 4 +- .../src/templates/shared/rich-text.test.ts | 9 + .../pdf/src/templates/shared/rich-text.tsx | 3 +- .../pdf/src/templates/shared/styles.test.ts | 18 + packages/pdf/src/templates/shared/styles.ts | 14 +- packages/schema/schema.json | 2865 ++++++++++++++++- packages/schema/src/resume/data.test.ts | 7 + packages/schema/src/resume/data.ts | 1 + packages/schema/src/resume/default.test.ts | 4 + packages/schema/src/resume/default.ts | 1 + packages/schema/src/resume/sample.ts | 1 + 16 files changed, 2820 insertions(+), 157 deletions(-) create mode 100644 packages/pdf/src/templates/shared/styles.test.ts diff --git a/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/page.tsx b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/page.tsx index ceba07e5b..89640640c 100644 --- a/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/page.tsx +++ b/apps/web/src/routes/builder/$resumeId/-sidebar/right/sections/page.tsx @@ -258,6 +258,30 @@ function PageSectionForm() { )} + + {(field) => ( + 0} + > + { + field.handleChange(checked); + handleAutoSave("hideLinkUnderline", checked); + }} + /> + } + /> + + Hide Link Underline + + + )} + + {(field) => ( { expect(result.picture.borderWidth).toBeGreaterThan(0); }); + it("maps v4 underline link preference to the v5 hide underline page setting", () => { + expect(result.metadata.page.hideLinkUnderline).toBe(true); + }); + it("maps summary content", () => { expect(result.summary.content).toBe("

About me

"); expect(result.summary.hidden).toBe(false); diff --git a/packages/import/src/reactive-resume-v4-json.tsx b/packages/import/src/reactive-resume-v4-json.tsx index 714b044f2..69a60b5aa 100644 --- a/packages/import/src/reactive-resume-v4-json.tsx +++ b/packages/import/src/reactive-resume-v4-json.tsx @@ -744,6 +744,7 @@ export class ReactiveResumeV4JSONImporter { marginY: nonNegative(v4Data.metadata.page?.margin ?? 14), format: v4Data.metadata.page?.format ?? "a4", locale: "en-US", + hideLinkUnderline: v4Data.metadata.typography?.underlineLinks === false, hideIcons: v4Data.metadata.typography?.hideIcons ?? false, hideSectionIcons: true, }, diff --git a/packages/pdf/src/templates/shared/primitives.test.ts b/packages/pdf/src/templates/shared/primitives.test.ts index ed275580e..e3aca6acc 100644 --- a/packages/pdf/src/templates/shared/primitives.test.ts +++ b/packages/pdf/src/templates/shared/primitives.test.ts @@ -4,6 +4,13 @@ import { describe, expect, it } from "vitest"; const source = readFileSync(fileURLToPath(new URL("./primitives.tsx", import.meta.url)), "utf8"); +describe("Link", () => { + it("passes the resume page underline preference to shared link styles", () => { + expect(source).toContain("metadata.page.hideLinkUnderline"); + expect(source).toContain("hideUnderline: metadata.page.hideLinkUnderline"); + }); +}); + describe("SectionHeadingIcon", () => { it("passes the resolved heading icon size through the icon size prop", () => { expect(source).toContain("size: sizeProp"); diff --git a/packages/pdf/src/templates/shared/primitives.tsx b/packages/pdf/src/templates/shared/primitives.tsx index a0a63fb59..a4b1cabbf 100644 --- a/packages/pdf/src/templates/shared/primitives.tsx +++ b/packages/pdf/src/templates/shared/primitives.tsx @@ -34,10 +34,22 @@ export const Heading = ({ style, ...props }: ComponentProps) => }; export const Link = ({ style, ...props }: ComponentProps) => { + const { metadata } = useRender(); const linkStyle = useTemplateStyle("link"); const linkRuleStyle = useSectionStyleRule("link"); - return ; + return ( + + ); }; export const Small = ({ style, ...props }: ComponentProps) => { diff --git a/packages/pdf/src/templates/shared/rich-text-stylesheet.ts b/packages/pdf/src/templates/shared/rich-text-stylesheet.ts index 6b02da64f..44df906dd 100644 --- a/packages/pdf/src/templates/shared/rich-text-stylesheet.ts +++ b/packages/pdf/src/templates/shared/rich-text-stylesheet.ts @@ -11,6 +11,7 @@ type RichTextProseSpacing = { type CreateRichTextStylesheetOptions = { boldStyle?: StyleInput; + hideLinkUnderline?: boolean; linkStyle?: StyleInput; richParagraphStyle?: StyleInput; richParagraphRuleStyle?: StyleInput; @@ -32,6 +33,7 @@ const emptyProseSpacing: RichTextProseSpacing = { export const createRichTextStylesheet = ({ boldStyle, + hideLinkUnderline, linkStyle, richParagraphStyle, richParagraphRuleStyle, @@ -48,5 +50,5 @@ export const createRichTextStylesheet = ({ li: mergeStyles(proseSpacing.listItem), [`.${richTextMarkClassName}`]: mergeStyles(richMarkStyle, richMarkRuleStyle, safeTextStyle), p: mergeStyles(richParagraphStyle, richParagraphRuleStyle, safeTextStyle, proseSpacing.paragraph), - a: mergeLinkStyles(linkStyle, richLinkRuleStyle, safeTextStyle), + a: mergeLinkStyles({ hideUnderline: hideLinkUnderline === true }, linkStyle, richLinkRuleStyle, safeTextStyle), }); diff --git a/packages/pdf/src/templates/shared/rich-text.test.ts b/packages/pdf/src/templates/shared/rich-text.test.ts index 692fc9e62..2c5aa2050 100644 --- a/packages/pdf/src/templates/shared/rich-text.test.ts +++ b/packages/pdf/src/templates/shared/rich-text.test.ts @@ -66,4 +66,13 @@ describe("createRichTextStylesheet", () => { expect(stylesheet.ol).toEqual({ rowGap: 8 }); expect(stylesheet.li).toEqual({ marginTop: 2, marginBottom: 2 }); }); + + it("can force rich text links to render without underlines", () => { + const stylesheet = createRichTextStylesheet({ + hideLinkUnderline: true, + richLinkRuleStyle: { textDecoration: "underline", textDecorationStyle: "dotted" }, + }); + + expect(stylesheet.a).toMatchObject({ textDecoration: "none", textDecorationStyle: "dotted" }); + }); }); diff --git a/packages/pdf/src/templates/shared/rich-text.tsx b/packages/pdf/src/templates/shared/rich-text.tsx index 06f5b002d..29514e243 100644 --- a/packages/pdf/src/templates/shared/rich-text.tsx +++ b/packages/pdf/src/templates/shared/rich-text.tsx @@ -58,7 +58,7 @@ const applyRtlDirectionRecursively = (node: ReactNode): ReactNode => { }; export const RichText = ({ children }: RichTextProps) => { - const { rtl } = useRender(); + const { metadata, rtl } = useRender(); const rtlTextWrapStyle: Style | undefined = rtl ? { direction: "rtl", textAlign: "right" } : undefined; const boldStyle = useTemplateStyle("bold"); const linkStyle = useTemplateStyle("link"); @@ -169,6 +169,7 @@ export const RichText = ({ children }: RichTextProps) => { }} stylesheet={createRichTextStylesheet({ boldStyle, + hideLinkUnderline: metadata.page.hideLinkUnderline, linkStyle, richParagraphStyle, richParagraphRuleStyle, diff --git a/packages/pdf/src/templates/shared/styles.test.ts b/packages/pdf/src/templates/shared/styles.test.ts new file mode 100644 index 000000000..024d7a2d5 --- /dev/null +++ b/packages/pdf/src/templates/shared/styles.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it } from "vitest"; +import { composeLinkStyles, mergeLinkStyles } from "./styles"; + +describe("link styles", () => { + it("underlines links by default", () => { + expect(composeLinkStyles({}, { color: "#111" }).at(-1)).toEqual({ textDecoration: "underline" }); + expect(mergeLinkStyles({}, { color: "#111" })).toMatchObject({ color: "#111", textDecoration: "underline" }); + }); + + it("forces links to render without underlines when requested", () => { + expect(composeLinkStyles({ hideUnderline: true }, { textDecoration: "underline" }).at(-1)).toEqual({ + textDecoration: "none", + }); + expect(mergeLinkStyles({ hideUnderline: true }, { textDecoration: "underline" })).toMatchObject({ + textDecoration: "none", + }); + }); +}); diff --git a/packages/pdf/src/templates/shared/styles.ts b/packages/pdf/src/templates/shared/styles.ts index 1e68b42ec..41d673746 100644 --- a/packages/pdf/src/templates/shared/styles.ts +++ b/packages/pdf/src/templates/shared/styles.ts @@ -4,6 +4,10 @@ export type TemplatePlacement = "main" | "sidebar"; export type StyleInput = Style | Style[] | null | undefined; +type LinkStyleOptions = { + hideUnderline?: boolean; +}; + export const composeStyles = (...styles: StyleInput[]): Style[] => { return styles.flatMap((style) => { if (!style) return []; @@ -14,12 +18,18 @@ export const composeStyles = (...styles: StyleInput[]): Style[] => { }; const linkUnderlineStyle = { textDecoration: "underline" } satisfies Style; +const linkNoUnderlineStyle = { textDecoration: "none" } satisfies Style; -export const composeLinkStyles = (...styles: StyleInput[]): Style[] => composeStyles(...styles, linkUnderlineStyle); +const resolveLinkDecorationStyle = ({ hideUnderline = false }: LinkStyleOptions = {}) => + hideUnderline ? linkNoUnderlineStyle : linkUnderlineStyle; + +export const composeLinkStyles = (options: LinkStyleOptions = {}, ...styles: StyleInput[]): Style[] => + composeStyles(...styles, resolveLinkDecorationStyle(options)); export const mergeStyles = (...styles: StyleInput[]): Style => Object.assign({}, ...composeStyles(...styles)); -export const mergeLinkStyles = (...styles: StyleInput[]): Style => mergeStyles(...styles, linkUnderlineStyle); +export const mergeLinkStyles = (options: LinkStyleOptions = {}, ...styles: StyleInput[]): Style => + mergeStyles(...styles, resolveLinkDecorationStyle(options)); // Increased from 1.2 to 1.3 to prevent descenders (g, p, y, etc.) from being // clipped by the overflow:hidden applied in safeTextStyle on all Heading elements. diff --git a/packages/schema/schema.json b/packages/schema/schema.json index fee293484..5ca48bdb7 100644 --- a/packages/schema/schema.json +++ b/packages/schema/schema.json @@ -5,7 +5,10 @@ "picture": { "type": "object", "properties": { - "hidden": { "type": "boolean", "description": "Whether to hide the picture from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the picture from the resume." + }, "url": { "type": "string", "description": "The URL to the picture to display on the resume. Prefer local app-served paths (for example /uploads/...) populated via upload." @@ -71,11 +74,26 @@ "basics": { "type": "object", "properties": { - "name": { "type": "string", "description": "The full name of the author of the resume." }, - "headline": { "type": "string", "description": "The headline of the author of the resume." }, - "email": { "type": "string", "description": "The email address of the author of the resume." }, - "phone": { "type": "string", "description": "The phone number of the author of the resume." }, - "location": { "type": "string", "description": "The location of the author of the resume." }, + "name": { + "type": "string", + "description": "The full name of the author of the resume." + }, + "headline": { + "type": "string", + "description": "The headline of the author of the resume." + }, + "email": { + "type": "string", + "description": "The email address of the author of the resume." + }, + "phone": { + "type": "string", + "description": "The phone number of the author of the resume." + }, + "location": { + "type": "string", + "description": "The location of the author of the resume." + }, "website": { "type": "object", "properties": { @@ -105,7 +123,10 @@ "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." }, - "text": { "type": "string", "description": "The text to display for the custom field." }, + "text": { + "type": "string", + "description": "The text to display for the custom field." + }, "link": { "default": "", "type": "string", @@ -125,7 +146,15 @@ "summary": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the summary of the resume." }, + "title": { + "type": "string", + "description": "The title of the summary of the resume." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the summary section title in the PDF output. Empty string uses the default summary icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the summary should span across.", @@ -133,13 +162,16 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the summary from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the summary from the resume." + }, "content": { "type": "string", "description": "The content of the summary of the resume. This should be a HTML-formatted string." } }, - "required": ["title", "columns", "hidden", "content"], + "required": ["title", "icon", "columns", "hidden", "content"], "additionalProperties": false, "description": "Summary section of the resume, useful for a short bio or introduction" }, @@ -149,7 +181,15 @@ "profiles": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -157,7 +197,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -167,7 +210,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -187,7 +233,11 @@ "description": "The username of the author on the network or platform." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the profile of the author on the network or platform, if any.", "type": "object", "properties": { @@ -215,14 +265,22 @@ "description": "The items to display in the profiles section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the profiles of the author." }, "experience": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -230,7 +288,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -240,7 +301,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "company": { "type": "string", "minLength": 1, @@ -250,13 +314,20 @@ "type": "string", "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." }, - "location": { "type": "string", "description": "The location of the company or organization." }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, "period": { "type": "string", "description": "The overall period of time at the company. When multiple roles are used, this should reflect the total tenure." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the company or organization, if any.", "type": "object", "properties": { @@ -292,8 +363,14 @@ "type": "string", "description": "The unique identifier for the role. Usually generated as a UUID." }, - "position": { "type": "string", "description": "The position or job title for this role." }, - "period": { "type": "string", "description": "The period of time this role was held." }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held." + }, "description": { "type": "string", "description": "The description of this specific role. This should be a HTML-formatted string." @@ -320,14 +397,22 @@ "description": "The items to display in the experience section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the experience of the author." }, "education": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -335,7 +420,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -345,19 +433,41 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "school": { "type": "string", "minLength": 1, "description": "The name of the school or institution." }, - "degree": { "type": "string", "description": "The degree or qualification obtained." }, - "area": { "type": "string", "description": "The area of study or specialization." }, - "grade": { "type": "string", "description": "The grade or score achieved." }, - "location": { "type": "string", "description": "The location of the school or institution." }, - "period": { "type": "string", "description": "The period of time the education was obtained over." }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, + "period": { + "type": "string", + "description": "The period of time the education was obtained over." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the school or institution, if any.", "type": "object", "properties": { @@ -400,14 +510,22 @@ "description": "The items to display in the education section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the education of the author." }, "projects": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -415,7 +533,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -425,11 +546,25 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, - "period": { "type": "string", "description": "The period of time the project was worked on." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the project." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the project, if any.", "type": "object", "properties": { @@ -461,14 +596,22 @@ "description": "The items to display in the projects section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the projects of the author." }, "skills": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -476,7 +619,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -486,7 +632,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -496,7 +645,11 @@ "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.", "type": "string" }, - "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the skill." + }, "proficiency": { "type": "string", "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." @@ -512,7 +665,9 @@ "default": [], "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } } }, "required": ["id", "hidden", "icon", "iconColor", "name", "proficiency", "level", "keywords"], @@ -521,14 +676,22 @@ "description": "The items to display in the skills section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the skills of the author." }, "languages": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -536,7 +699,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -546,7 +712,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "language": { "type": "string", "minLength": 1, @@ -570,14 +739,22 @@ "description": "The items to display in the languages section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the languages of the author." }, "interests": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -585,7 +762,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -595,7 +775,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -605,12 +788,18 @@ "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.", "type": "string" }, - "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the interest/hobby." + }, "keywords": { "default": [], "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } } }, "required": ["id", "hidden", "icon", "iconColor", "name", "keywords"], @@ -619,14 +808,22 @@ "description": "The items to display in the interests section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the interests of the author." }, "awards": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -634,7 +831,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -644,12 +844,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, - "awarder": { "type": "string", "description": "The awarder of the award." }, - "date": { "type": "string", "description": "The date when the award was received." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the award." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the award, if any.", "type": "object", "properties": { @@ -681,14 +898,22 @@ "description": "The items to display in the awards section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the awards of the author." }, "certifications": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -696,7 +921,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -706,12 +934,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, - "issuer": { "type": "string", "description": "The issuer of the certification." }, - "date": { "type": "string", "description": "The date when the certification was received." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the certification." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the certification, if any.", "type": "object", "properties": { @@ -743,14 +988,22 @@ "description": "The items to display in the certifications section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the certifications of the author." }, "publications": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -758,7 +1011,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -768,12 +1024,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, - "publisher": { "type": "string", "description": "The publisher of the publication." }, - "date": { "type": "string", "description": "The date when the publication was published." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the publication." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the publication, if any.", "type": "object", "properties": { @@ -805,14 +1078,22 @@ "description": "The items to display in the publications section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the publications of the author." }, "volunteer": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -820,7 +1101,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -830,19 +1114,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "organization": { "type": "string", "minLength": 1, "description": "The name of the organization or company." }, - "location": { "type": "string", "description": "The location of the organization or company." }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, "period": { "type": "string", "description": "The period of time the author was volunteered at the organization or company." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the organization or company, if any.", "type": "object", "properties": { @@ -874,14 +1168,22 @@ "description": "The items to display in the volunteer section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the volunteer experience of the author." }, "references": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -889,7 +1191,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "items": { "type": "array", "items": { @@ -899,15 +1204,25 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "name": { "type": "string", "minLength": 1, "description": "The name of the reference, or a note such as 'Available upon request'." }, - "position": { "type": "string", "description": "The position or job title of the reference." }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website or LinkedIn profile of the reference, if any.", "type": "object", "properties": { @@ -928,7 +1243,10 @@ "required": ["url", "label", "inlineLink"], "additionalProperties": false }, - "phone": { "type": "string", "description": "The phone number of the reference." }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, "description": { "type": "string", "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." @@ -940,7 +1258,7 @@ "description": "The items to display in the references section." } }, - "required": ["title", "columns", "hidden", "items"], + "required": ["title", "icon", "columns", "hidden", "items"], "additionalProperties": false, "description": "The section to display the references of the author." } @@ -967,7 +1285,15 @@ "items": { "type": "object", "properties": { - "title": { "type": "string", "description": "The title of the section." }, + "title": { + "type": "string", + "description": "The title of the section." + }, + "icon": { + "default": "", + "description": "Phosphor icon name to display before the section title in the PDF output. Empty string uses the default section icon; 'none' hides the icon.", + "type": "string" + }, "columns": { "default": 1, "description": "The number of columns the section should span across.", @@ -975,7 +1301,10 @@ "minimum": 1, "maximum": 6 }, - "hidden": { "type": "boolean", "description": "Whether to hide the section from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the section from the resume." + }, "id": { "type": "string", "description": "The unique identifier for the custom section. Usually generated as a UUID." @@ -1011,7 +1340,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "recipient": { "type": "string", "description": "The recipient's address block as HTML (name, title, company, address, email)." @@ -1031,7 +1363,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "content": { "type": "string", "description": "The rich text content of the summary item. This should be a HTML-formatted string." @@ -1047,7 +1382,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -1067,7 +1405,11 @@ "description": "The username of the author on the network or platform." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the profile of the author on the network or platform, if any.", "type": "object", "properties": { @@ -1099,7 +1441,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "company": { "type": "string", "minLength": 1, @@ -1109,13 +1454,20 @@ "type": "string", "description": "The position held at the company or organization. Used when there is only a single role. If multiple roles are provided in the 'roles' field, this serves as a summary title or can be left blank." }, - "location": { "type": "string", "description": "The location of the company or organization." }, + "location": { + "type": "string", + "description": "The location of the company or organization." + }, "period": { "type": "string", "description": "The overall period of time at the company. When multiple roles are used, this should reflect the total tenure." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the company or organization, if any.", "type": "object", "properties": { @@ -1151,8 +1503,14 @@ "type": "string", "description": "The unique identifier for the role. Usually generated as a UUID." }, - "position": { "type": "string", "description": "The position or job title for this role." }, - "period": { "type": "string", "description": "The period of time this role was held." }, + "position": { + "type": "string", + "description": "The position or job title for this role." + }, + "period": { + "type": "string", + "description": "The period of time this role was held." + }, "description": { "type": "string", "description": "The description of this specific role. This should be a HTML-formatted string." @@ -1183,22 +1541,41 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "school": { "type": "string", "minLength": 1, "description": "The name of the school or institution." }, - "degree": { "type": "string", "description": "The degree or qualification obtained." }, - "area": { "type": "string", "description": "The area of study or specialization." }, - "grade": { "type": "string", "description": "The grade or score achieved." }, - "location": { "type": "string", "description": "The location of the school or institution." }, + "degree": { + "type": "string", + "description": "The degree or qualification obtained." + }, + "area": { + "type": "string", + "description": "The area of study or specialization." + }, + "grade": { + "type": "string", + "description": "The grade or score achieved." + }, + "location": { + "type": "string", + "description": "The location of the school or institution." + }, "period": { "type": "string", "description": "The period of time the education was obtained over." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the school or institution, if any.", "type": "object", "properties": { @@ -1245,11 +1622,25 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "name": { "type": "string", "minLength": 1, "description": "The name of the project." }, - "period": { "type": "string", "description": "The period of time the project was worked on." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the project." + }, + "period": { + "type": "string", + "description": "The period of time the project was worked on." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the project, if any.", "type": "object", "properties": { @@ -1285,7 +1676,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -1295,7 +1689,11 @@ "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.", "type": "string" }, - "name": { "type": "string", "minLength": 1, "description": "The name of the skill." }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the skill." + }, "proficiency": { "type": "string", "description": "The proficiency level of the skill. Can be any text, such as 'Beginner', 'Intermediate', 'Advanced', etc." @@ -1311,7 +1709,9 @@ "default": [], "description": "The keywords associated with the skill, if any. These are displayed as tags below the name.", "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } } }, "required": ["id", "hidden", "icon", "iconColor", "name", "proficiency", "level", "keywords"], @@ -1324,7 +1724,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "language": { "type": "string", "minLength": 1, @@ -1352,7 +1755,10 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "icon": { "type": "string", "description": "The icon to display for the custom field. Must be a valid icon name from @phosphor-icons/web icon set, or an empty string to hide. Default to '' (empty string) when unsure which icons are available." @@ -1362,12 +1768,18 @@ "description": "Custom color for the icon, defined as rgba(r, g, b, a). Leave blank to use the template default icon color.", "type": "string" }, - "name": { "type": "string", "minLength": 1, "description": "The name of the interest/hobby." }, + "name": { + "type": "string", + "minLength": 1, + "description": "The name of the interest/hobby." + }, "keywords": { "default": [], "description": "The keywords associated with the interest/hobby, if any. These are displayed as tags below the name.", "type": "array", - "items": { "type": "string" } + "items": { + "type": "string" + } } }, "required": ["id", "hidden", "icon", "iconColor", "name", "keywords"], @@ -1380,12 +1792,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the award." }, - "awarder": { "type": "string", "description": "The awarder of the award." }, - "date": { "type": "string", "description": "The date when the award was received." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the award." + }, + "awarder": { + "type": "string", + "description": "The awarder of the award." + }, + "date": { + "type": "string", + "description": "The date when the award was received." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the award, if any.", "type": "object", "properties": { @@ -1421,12 +1850,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the certification." }, - "issuer": { "type": "string", "description": "The issuer of the certification." }, - "date": { "type": "string", "description": "The date when the certification was received." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the certification." + }, + "issuer": { + "type": "string", + "description": "The issuer of the certification." + }, + "date": { + "type": "string", + "description": "The date when the certification was received." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website of the certification, if any.", "type": "object", "properties": { @@ -1462,12 +1908,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, - "title": { "type": "string", "minLength": 1, "description": "The title of the publication." }, - "publisher": { "type": "string", "description": "The publisher of the publication." }, - "date": { "type": "string", "description": "The date when the publication was published." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, + "title": { + "type": "string", + "minLength": 1, + "description": "The title of the publication." + }, + "publisher": { + "type": "string", + "description": "The publisher of the publication." + }, + "date": { + "type": "string", + "description": "The date when the publication was published." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the publication, if any.", "type": "object", "properties": { @@ -1503,19 +1966,29 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "organization": { "type": "string", "minLength": 1, "description": "The name of the organization or company." }, - "location": { "type": "string", "description": "The location of the organization or company." }, + "location": { + "type": "string", + "description": "The location of the organization or company." + }, "period": { "type": "string", "description": "The period of time the author was volunteered at the organization or company." }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The link to the organization or company, if any.", "type": "object", "properties": { @@ -1551,15 +2024,25 @@ "type": "string", "description": "The unique identifier for the item. Usually generated as a UUID." }, - "hidden": { "type": "boolean", "description": "Whether to hide the item from the resume." }, + "hidden": { + "type": "boolean", + "description": "Whether to hide the item from the resume." + }, "name": { "type": "string", "minLength": 1, "description": "The name of the reference, or a note such as 'Available upon request'." }, - "position": { "type": "string", "description": "The position or job title of the reference." }, + "position": { + "type": "string", + "description": "The position or job title of the reference." + }, "website": { - "default": { "url": "", "label": "", "inlineLink": false }, + "default": { + "url": "", + "label": "", + "inlineLink": false + }, "description": "The website or LinkedIn profile of the reference, if any.", "type": "object", "properties": { @@ -1580,7 +2063,10 @@ "required": ["url", "label", "inlineLink"], "additionalProperties": false }, - "phone": { "type": "string", "description": "The phone number of the reference." }, + "phone": { + "type": "string", + "description": "The phone number of the reference." + }, "description": { "type": "string", "description": "The description of the reference. Can be used to display a quote, a testimonial, etc. This should be a HTML-formatted string." @@ -1594,7 +2080,7 @@ "description": "The items to display in the custom section. Items follow the schema of the section type." } }, - "required": ["title", "columns", "hidden", "id", "type", "items"], + "required": ["title", "icon", "columns", "hidden", "id", "type", "items"], "additionalProperties": false }, "description": "Custom sections of the resume, such as a custom section for notes, etc." @@ -1645,12 +2131,16 @@ }, "main": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "The items to display in the main column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." }, "sidebar": { "type": "array", - "items": { "type": "string" }, + "items": { + "type": "string" + }, "description": "The items to display in the sidebar column of the page. A string array of section IDs (experience, education, projects, skills, languages, interests, awards, certifications, publications, volunteer, references, profiles, summary or UUIDs for custom sections)." } }, @@ -1698,13 +2188,33 @@ "type": "string", "description": "The locale of the page. Used for displaying pre-translated section headings, if not overridden." }, + "hideLinkUnderline": { + "default": false, + "type": "boolean", + "description": "Whether to hide the underlines of the links." + }, "hideIcons": { "default": false, "type": "boolean", - "description": "Whether to hide the icons of the sections." + "description": "Whether to hide the item-level icons (skills, profiles, interests)." + }, + "hideSectionIcons": { + "default": true, + "type": "boolean", + "description": "Whether to hide the section heading icons displayed before section titles." } }, - "required": ["gapX", "gapY", "marginX", "marginY", "format", "locale", "hideIcons"], + "required": [ + "gapX", + "gapY", + "marginX", + "marginY", + "format", + "locale", + "hideLinkUnderline", + "hideIcons", + "hideSectionIcons" + ], "additionalProperties": false, "description": "The page settings of the resume. Determines the margins, format, and locale of the resume." }, @@ -1765,7 +2275,10 @@ "default": ["400"], "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", "type": "array", - "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } + "items": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + } }, "fontSize": { "default": 11, @@ -1797,7 +2310,10 @@ "default": ["400"], "description": "The weight of the font, defined as a number between 100 and 900. Default to 400 when unsure if the weight is available in the font.", "type": "array", - "items": { "type": "string", "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] } + "items": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + } }, "fontSize": { "default": 11, @@ -1826,13 +2342,2058 @@ "notes": { "type": "string", "description": "Personal notes for the resume. Can be used to add any additional information or instructions for the resume. These notes are not displayed on the resume, they are only visible to the author of the resume when editing the resume. This should be a HTML-formatted string." + }, + "styleRules": { + "default": [], + "description": "Structured style rules that target semantic resume sections and slots for React PDF rendering.", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "description": "Unique identifier for this style rule." + }, + "label": { + "default": "", + "description": "Human-readable label for this style rule.", + "type": "string" + }, + "enabled": { + "default": true, + "description": "Whether this style rule should affect PDF rendering.", + "type": "boolean" + }, + "target": { + "oneOf": [ + { + "type": "object", + "properties": { + "scope": { + "type": "string", + "const": "global" + } + }, + "required": ["scope"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "scope": { + "type": "string", + "const": "sectionType" + }, + "sectionType": { + "type": "string", + "enum": [ + "summary", + "profiles", + "experience", + "education", + "projects", + "skills", + "languages", + "interests", + "awards", + "certifications", + "publications", + "volunteer", + "references", + "cover-letter" + ] + } + }, + "required": ["scope", "sectionType"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "scope": { + "type": "string", + "const": "sectionId" + }, + "sectionId": { + "type": "string", + "minLength": 1 + } + }, + "required": ["scope", "sectionId"], + "additionalProperties": false + } + ], + "description": "The resume content this style rule applies to." + }, + "slots": { + "type": "object", + "properties": { + "section": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "heading": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "item": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "text": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "secondaryText": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "link": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "icon": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "level": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richParagraph": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richList": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richListItemRow": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richListItemContent": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richLink": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richBold": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + }, + "richMark": { + "type": "object", + "properties": { + "color": { + "type": "string" + }, + "backgroundColor": { + "type": "string" + }, + "borderColor": { + "type": "string" + }, + "textDecorationColor": { + "type": "string" + }, + "opacity": { + "type": "number", + "minimum": 0, + "maximum": 1 + }, + "fontSize": { + "type": "number", + "minimum": 6, + "maximum": 48 + }, + "fontWeight": { + "type": "string", + "enum": ["100", "200", "300", "400", "500", "600", "700", "800", "900"] + }, + "fontStyle": { + "type": "string", + "enum": ["normal", "italic"] + }, + "lineHeight": { + "type": "number", + "minimum": 0.5, + "maximum": 4 + }, + "letterSpacing": { + "type": "number", + "minimum": -16, + "maximum": 16 + }, + "textDecoration": { + "type": "string", + "enum": ["none", "underline", "line-through"] + }, + "textDecorationStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "textAlign": { + "type": "string", + "enum": ["left", "center", "right", "justify"] + }, + "textTransform": { + "type": "string", + "enum": ["none", "uppercase", "lowercase", "capitalize"] + }, + "padding": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "paddingLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginTop": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginRight": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginBottom": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "marginLeft": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "rowGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "columnGap": { + "type": "number", + "minimum": -72, + "maximum": 72 + }, + "borderStyle": { + "type": "string", + "enum": ["solid", "dashed", "dotted"] + }, + "borderWidth": { + "type": "number", + "minimum": 0 + }, + "borderRadius": { + "type": "number", + "minimum": 0 + } + }, + "additionalProperties": false, + "description": "Constrained visual style intent that can be safely translated to React PDF styles." + } + }, + "additionalProperties": false, + "description": "The semantic style slots configured by this rule." + } + }, + "required": ["id", "label", "enabled", "target", "slots"], + "additionalProperties": false + } } }, - "required": ["template", "layout", "page", "design", "typography", "notes"], + "required": ["template", "layout", "page", "design", "typography", "notes", "styleRules"], "additionalProperties": false, "description": "Metadata for the resume, such as template, layout, typography, etc. This section describes the overall design and appearance of the resume." } }, "required": ["picture", "basics", "summary", "sections", "customSections", "metadata"], - "additionalProperties": false + "additionalProperties": {} } diff --git a/packages/schema/src/resume/data.test.ts b/packages/schema/src/resume/data.test.ts index a4feef2e5..7329a9c15 100644 --- a/packages/schema/src/resume/data.test.ts +++ b/packages/schema/src/resume/data.test.ts @@ -280,6 +280,13 @@ describe("pageSchema", () => { expect(result.success).toBe(true); if (result.success) expect(result.data.hideSectionIcons).toBe(true); }); + + it("defaults hideLinkUnderline to false when missing", () => { + const { hideLinkUnderline: _, ...pageWithout } = defaultResumeData.metadata.page; + const result = pageSchema.safeParse(pageWithout); + expect(result.success).toBe(true); + if (result.success) expect(result.data.hideLinkUnderline).toBe(false); + }); }); describe("baseSectionSchema", () => { diff --git a/packages/schema/src/resume/data.ts b/packages/schema/src/resume/data.ts index 0feb9f8fd..e05ac667e 100644 --- a/packages/schema/src/resume/data.ts +++ b/packages/schema/src/resume/data.ts @@ -469,6 +469,7 @@ export const pageSchema = z.object({ .string() .describe("The locale of the page. Used for displaying pre-translated section headings, if not overridden.") .catch("en-US"), + hideLinkUnderline: z.boolean().describe("Whether to hide the underlines of the links.").catch(false), hideIcons: z.boolean().describe("Whether to hide the item-level icons (skills, profiles, interests).").catch(false), hideSectionIcons: z .boolean() diff --git a/packages/schema/src/resume/default.test.ts b/packages/schema/src/resume/default.test.ts index d78e0d406..f3fd4f03a 100644 --- a/packages/schema/src/resume/default.test.ts +++ b/packages/schema/src/resume/default.test.ts @@ -20,6 +20,10 @@ describe("defaultResumeData", () => { expect(defaultResumeData.metadata.page.format).toBe("a4"); }); + it("shows link underlines by default", () => { + expect(defaultResumeData.metadata.page.hideLinkUnderline).toBe(false); + }); + it("starts with no resume content", () => { expect(defaultResumeData.basics.name).toBe(""); expect(defaultResumeData.summary.content).toBe(""); diff --git a/packages/schema/src/resume/default.ts b/packages/schema/src/resume/default.ts index 7c447bfe4..11ffba1a8 100644 --- a/packages/schema/src/resume/default.ts +++ b/packages/schema/src/resume/default.ts @@ -135,6 +135,7 @@ export const defaultResumeData: ResumeData = { marginY: 12, format: "a4", locale: "en-US", + hideLinkUnderline: false, hideIcons: false, hideSectionIcons: true, }, diff --git a/packages/schema/src/resume/sample.ts b/packages/schema/src/resume/sample.ts index 3b7a818b6..44800ccae 100644 --- a/packages/schema/src/resume/sample.ts +++ b/packages/schema/src/resume/sample.ts @@ -567,6 +567,7 @@ export const sampleResumeData: ResumeData = { marginY: 16, format: "a4", locale: "en-US", + hideLinkUnderline: false, hideIcons: false, hideSectionIcons: false, },