mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-12 15:52:56 +10:00
Implement Custom CSS Feature
This commit is contained in:
@ -55,5 +55,11 @@ export const ArtboardPage = () => {
|
|||||||
}
|
}
|
||||||
}, [metadata]);
|
}, [metadata]);
|
||||||
|
|
||||||
return <Outlet />;
|
return (
|
||||||
|
<>
|
||||||
|
{metadata.css.visible && <style lang="css">{`[data-page] { ${metadata.css.value} }`}</style>}
|
||||||
|
|
||||||
|
<Outlet />
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
155
apps/client/public/styles/prism-dark.css
Normal file
155
apps/client/public/styles/prism-dark.css
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: #f8f8f2;
|
||||||
|
background: none;
|
||||||
|
font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
word-wrap: normal;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: #2b2b2b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: 0.1em;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: #d4d0ab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation {
|
||||||
|
color: #fefefe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.property,
|
||||||
|
.token.tag,
|
||||||
|
.token.constant,
|
||||||
|
.token.symbol,
|
||||||
|
.token.deleted {
|
||||||
|
color: #ffa07a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.boolean,
|
||||||
|
.token.number {
|
||||||
|
color: #00e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.token.attr-name,
|
||||||
|
.token.string,
|
||||||
|
.token.char,
|
||||||
|
.token.builtin,
|
||||||
|
.token.inserted {
|
||||||
|
color: #abe338;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.operator,
|
||||||
|
.token.entity,
|
||||||
|
.token.url,
|
||||||
|
.language-css .token.string,
|
||||||
|
.style .token.string,
|
||||||
|
.token.variable {
|
||||||
|
color: #00e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.function {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.keyword {
|
||||||
|
color: #00e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.regex,
|
||||||
|
.token.important {
|
||||||
|
color: #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.entity {
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (-ms-high-contrast: active) {
|
||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: windowText;
|
||||||
|
background: window;
|
||||||
|
}
|
||||||
|
|
||||||
|
:not(pre) > code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
background: window;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important {
|
||||||
|
background: highlight;
|
||||||
|
color: window;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.attr-value,
|
||||||
|
.token.function,
|
||||||
|
.token.keyword,
|
||||||
|
.token.operator,
|
||||||
|
.token.selector {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-value,
|
||||||
|
.token.comment,
|
||||||
|
.token.doctype,
|
||||||
|
.token.function,
|
||||||
|
.token.keyword,
|
||||||
|
.token.operator,
|
||||||
|
.token.property,
|
||||||
|
.token.string {
|
||||||
|
color: highlight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-value,
|
||||||
|
.token.url {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
167
apps/client/public/styles/prism-light.css
Normal file
167
apps/client/public/styles/prism-light.css
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
code[class*="language-"],
|
||||||
|
pre[class*="language-"] {
|
||||||
|
color: #393a34;
|
||||||
|
font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
|
||||||
|
direction: ltr;
|
||||||
|
text-align: left;
|
||||||
|
white-space: pre;
|
||||||
|
word-spacing: normal;
|
||||||
|
word-break: normal;
|
||||||
|
font-size: 0.9em;
|
||||||
|
line-height: 1.2em;
|
||||||
|
|
||||||
|
-moz-tab-size: 4;
|
||||||
|
-o-tab-size: 4;
|
||||||
|
tab-size: 4;
|
||||||
|
|
||||||
|
-webkit-hyphens: none;
|
||||||
|
-moz-hyphens: none;
|
||||||
|
-ms-hyphens: none;
|
||||||
|
hyphens: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre > code[class*="language-"] {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::-moz-selection,
|
||||||
|
pre[class*="language-"] ::-moz-selection,
|
||||||
|
code[class*="language-"]::-moz-selection,
|
||||||
|
code[class*="language-"] ::-moz-selection {
|
||||||
|
background: #c1def1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre[class*="language-"]::selection,
|
||||||
|
pre[class*="language-"] ::selection,
|
||||||
|
code[class*="language-"]::selection,
|
||||||
|
code[class*="language-"] ::selection {
|
||||||
|
background: #c1def1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code blocks */
|
||||||
|
pre[class*="language-"] {
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0.5em 0;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline code */
|
||||||
|
:not(pre) > code[class*="language-"] {
|
||||||
|
padding: 0.2em;
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
background: #f8f8f8;
|
||||||
|
border: 1px solid #dddddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.comment,
|
||||||
|
.token.prolog,
|
||||||
|
.token.doctype,
|
||||||
|
.token.cdata {
|
||||||
|
color: #008000;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.namespace {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.string {
|
||||||
|
color: #a31515;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.punctuation,
|
||||||
|
.token.operator {
|
||||||
|
color: #393a34; /* no highlight */
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.url,
|
||||||
|
.token.symbol,
|
||||||
|
.token.number,
|
||||||
|
.token.boolean,
|
||||||
|
.token.variable,
|
||||||
|
.token.constant,
|
||||||
|
.token.inserted {
|
||||||
|
color: #36acaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.atrule,
|
||||||
|
.token.keyword,
|
||||||
|
.token.attr-value,
|
||||||
|
.language-autohotkey .token.selector,
|
||||||
|
.language-json .token.boolean,
|
||||||
|
.language-json .token.number,
|
||||||
|
code[class*="language-css"] {
|
||||||
|
color: #0000ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.function {
|
||||||
|
color: #393a34;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.deleted,
|
||||||
|
.language-autohotkey .token.tag {
|
||||||
|
color: #9a050f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.selector,
|
||||||
|
.language-autohotkey .token.keyword {
|
||||||
|
color: #00009f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important {
|
||||||
|
color: #e90;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.important,
|
||||||
|
.token.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.class-name,
|
||||||
|
.language-json .token.property {
|
||||||
|
color: #2b91af;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.tag,
|
||||||
|
.token.selector {
|
||||||
|
color: #800000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.attr-name,
|
||||||
|
.token.property,
|
||||||
|
.token.regex,
|
||||||
|
.token.entity {
|
||||||
|
color: #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.token.directive.tag .tag {
|
||||||
|
background: #ffff00;
|
||||||
|
color: #393a34;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Numbers plugin
|
||||||
|
* http://prismjs.com/plugins/line-numbers/
|
||||||
|
*/
|
||||||
|
.line-numbers.line-numbers .line-numbers-rows {
|
||||||
|
border-right-color: #a5a5a5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.line-numbers .line-numbers-rows > span:before {
|
||||||
|
color: #2b91af;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* overrides color-values for the Line Highlight plugin
|
||||||
|
* http://prismjs.com/plugins/line-highlight/
|
||||||
|
*/
|
||||||
|
.line-highlight.line-highlight {
|
||||||
|
background: rgba(193, 222, 241, 0.2);
|
||||||
|
background: -webkit-linear-gradient(left, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0));
|
||||||
|
background: linear-gradient(to right, rgba(193, 222, 241, 0.2) 70%, rgba(221, 222, 241, 0));
|
||||||
|
}
|
||||||
@ -289,7 +289,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -314,7 +314,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -314,7 +314,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -315,7 +315,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -289,7 +289,7 @@
|
|||||||
[[], []]
|
[[], []]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -288,7 +288,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -287,7 +287,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -289,7 +289,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -306,7 +306,7 @@
|
|||||||
[["projects", "certifications", "skills", "languages", "references"], []]
|
[["projects", "certifications", "skills", "languages", "references"], []]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -287,7 +287,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -315,7 +315,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -288,7 +288,7 @@
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
"css": {
|
"css": {
|
||||||
"value": ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
"value": "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
"visible": false
|
"visible": false
|
||||||
},
|
},
|
||||||
"page": {
|
"page": {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { useRef } from "react";
|
|||||||
import { Copyright } from "@/client/components/copyright";
|
import { Copyright } from "@/client/components/copyright";
|
||||||
import { ThemeSwitch } from "@/client/components/theme-switch";
|
import { ThemeSwitch } from "@/client/components/theme-switch";
|
||||||
|
|
||||||
|
import { CssSection } from "./sections/css";
|
||||||
import { ExportSection } from "./sections/export";
|
import { ExportSection } from "./sections/export";
|
||||||
import { InformationSection } from "./sections/information";
|
import { InformationSection } from "./sections/information";
|
||||||
import { LayoutSection } from "./sections/layout";
|
import { LayoutSection } from "./sections/layout";
|
||||||
@ -37,6 +38,8 @@ export const RightSidebar = () => {
|
|||||||
<Separator />
|
<Separator />
|
||||||
<ThemeSection />
|
<ThemeSection />
|
||||||
<Separator />
|
<Separator />
|
||||||
|
<CssSection />
|
||||||
|
<Separator />
|
||||||
<PageSection />
|
<PageSection />
|
||||||
<Separator />
|
<Separator />
|
||||||
<SharingSection />
|
<SharingSection />
|
||||||
@ -85,6 +88,13 @@ export const RightSidebar = () => {
|
|||||||
scrollIntoView("#theme");
|
scrollIntoView("#theme");
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<SectionIcon
|
||||||
|
id="css"
|
||||||
|
name={t`Custom CSS`}
|
||||||
|
onClick={() => {
|
||||||
|
scrollIntoView("#css");
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<SectionIcon
|
<SectionIcon
|
||||||
id="page"
|
id="page"
|
||||||
name={t`Page`}
|
name={t`Page`}
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
// import "prismjs/themes/prism.min.css";
|
||||||
|
|
||||||
|
import { t } from "@lingui/macro";
|
||||||
|
import { useTheme } from "@reactive-resume/hooks";
|
||||||
|
import { Label, Switch } from "@reactive-resume/ui";
|
||||||
|
import Prism from "prismjs";
|
||||||
|
import { Helmet } from "react-helmet-async";
|
||||||
|
import CodeEditor from "react-simple-code-editor";
|
||||||
|
|
||||||
|
import { useResumeStore } from "@/client/stores/resume";
|
||||||
|
|
||||||
|
import { getSectionIcon } from "../shared/section-icon";
|
||||||
|
|
||||||
|
export const CssSection = () => {
|
||||||
|
const { isDarkMode } = useTheme();
|
||||||
|
|
||||||
|
const setValue = useResumeStore((state) => state.setValue);
|
||||||
|
const css = useResumeStore((state) => state.resume.data.metadata.css);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section id="css" className="grid gap-y-6">
|
||||||
|
<Helmet>
|
||||||
|
{isDarkMode && <link rel="stylesheet" href="/styles/prism-dark.css" />}
|
||||||
|
{!isDarkMode && <link rel="stylesheet" href="/styles/prism-light.css" />}
|
||||||
|
</Helmet>
|
||||||
|
|
||||||
|
<header className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-x-4">
|
||||||
|
{getSectionIcon("css")}
|
||||||
|
<h2 className="line-clamp-1 text-2xl font-bold lg:text-3xl">{t`Custom CSS`}</h2>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main className="space-y-4">
|
||||||
|
<div className="flex items-center gap-x-4">
|
||||||
|
<Switch
|
||||||
|
id="metadata.css.visible"
|
||||||
|
checked={css.visible}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
setValue("metadata.css.visible", checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Label htmlFor="metadata.css.visible">{t`Apply Custom CSS`}</Label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="rounded border p-4">
|
||||||
|
<CodeEditor
|
||||||
|
tabSize={4}
|
||||||
|
value={css.value}
|
||||||
|
className="language-css font-mono"
|
||||||
|
highlight={(code) => Prism.highlight(code, Prism.languages.css, "css")}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
setValue("metadata.css.value", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
Code,
|
||||||
DiamondsFour,
|
DiamondsFour,
|
||||||
DownloadSimple,
|
DownloadSimple,
|
||||||
IconProps,
|
IconProps,
|
||||||
@ -19,6 +20,7 @@ export type MetadataKey =
|
|||||||
| "layout"
|
| "layout"
|
||||||
| "typography"
|
| "typography"
|
||||||
| "theme"
|
| "theme"
|
||||||
|
| "css"
|
||||||
| "page"
|
| "page"
|
||||||
| "locale"
|
| "locale"
|
||||||
| "sharing"
|
| "sharing"
|
||||||
@ -45,6 +47,9 @@ export const getSectionIcon = (id: MetadataKey, props: IconProps = {}) => {
|
|||||||
case "theme": {
|
case "theme": {
|
||||||
return <Palette size={18} {...props} />;
|
return <Palette size={18} {...props} />;
|
||||||
}
|
}
|
||||||
|
case "css": {
|
||||||
|
return <Code size={18} {...props} />;
|
||||||
|
}
|
||||||
case "page": {
|
case "page": {
|
||||||
return <ReadCvLogo size={18} {...props} />;
|
return <ReadCvLogo size={18} {...props} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -150,6 +150,17 @@ export class PrinterService {
|
|||||||
return temporaryHtml_;
|
return temporaryHtml_;
|
||||||
}, pageElement);
|
}, pageElement);
|
||||||
|
|
||||||
|
// Apply custom CSS if enabled
|
||||||
|
const css = resume.data.metadata.css;
|
||||||
|
|
||||||
|
if (css.visible) {
|
||||||
|
await page.evaluate((cssValue: string) => {
|
||||||
|
const styleTag = document.createElement("style");
|
||||||
|
styleTag.textContent = cssValue;
|
||||||
|
document.head.append(styleTag);
|
||||||
|
}, css.value);
|
||||||
|
}
|
||||||
|
|
||||||
const uint8array = await page.pdf({ width, height, printBackground: true });
|
const uint8array = await page.pdf({ width, height, printBackground: true });
|
||||||
const buffer = Buffer.from(uint8array);
|
const buffer = Buffer.from(uint8array);
|
||||||
pagesBuffer.push(buffer);
|
pagesBuffer.push(buffer);
|
||||||
|
|||||||
@ -12,7 +12,7 @@ export const metadataSchema = z.object({
|
|||||||
template: z.string().default("rhyhorn"),
|
template: z.string().default("rhyhorn"),
|
||||||
layout: z.array(z.array(z.array(z.string()))).default(defaultLayout), // pages -> columns -> sections
|
layout: z.array(z.array(z.array(z.string()))).default(defaultLayout), // pages -> columns -> sections
|
||||||
css: z.object({
|
css: z.object({
|
||||||
value: z.string().default(".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}"),
|
value: z.string().default("* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}"),
|
||||||
visible: z.boolean().default(false),
|
visible: z.boolean().default(false),
|
||||||
}),
|
}),
|
||||||
page: z.object({
|
page: z.object({
|
||||||
@ -50,7 +50,7 @@ export const defaultMetadata: Metadata = {
|
|||||||
template: "rhyhorn",
|
template: "rhyhorn",
|
||||||
layout: defaultLayout,
|
layout: defaultLayout,
|
||||||
css: {
|
css: {
|
||||||
value: ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
value: "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
|
|||||||
@ -308,7 +308,7 @@ export const sampleResume: ResumeData = {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
css: {
|
css: {
|
||||||
value: ".section {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
value: "* {\n\toutline: 1px solid #000;\n\toutline-offset: 4px;\n}",
|
||||||
visible: false,
|
visible: false,
|
||||||
},
|
},
|
||||||
page: {
|
page: {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@reactive-resume/source",
|
"name": "@reactive-resume/source",
|
||||||
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||||
"version": "4.3.4",
|
"version": "4.3.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": {
|
"author": {
|
||||||
@ -75,6 +75,7 @@
|
|||||||
"@types/passport-github2": "^1.2.9",
|
"@types/passport-github2": "^1.2.9",
|
||||||
"@types/passport-google-oauth20": "^2.0.16",
|
"@types/passport-google-oauth20": "^2.0.16",
|
||||||
"@types/passport-local": "^1.0.38",
|
"@types/passport-local": "^1.0.38",
|
||||||
|
"@types/prismjs": "^1.26.5",
|
||||||
"@types/react": "^18.3.18",
|
"@types/react": "^18.3.18",
|
||||||
"@types/react-dom": "^18.3.5",
|
"@types/react-dom": "^18.3.5",
|
||||||
"@types/react-is": "^18.3.1",
|
"@types/react-is": "^18.3.1",
|
||||||
@ -215,6 +216,7 @@
|
|||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"prisma": "^5.22.0",
|
"prisma": "^5.22.0",
|
||||||
|
"prismjs": "^1.29.0",
|
||||||
"puppeteer": "^23.11.1",
|
"puppeteer": "^23.11.1",
|
||||||
"qrcode.react": "^4.2.0",
|
"qrcode.react": "^4.2.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
@ -225,6 +227,7 @@
|
|||||||
"react-parallax-tilt": "^1.7.272",
|
"react-parallax-tilt": "^1.7.272",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
"react-router": "^7.1.1",
|
"react-router": "^7.1.1",
|
||||||
|
"react-simple-code-editor": "^0.14.1",
|
||||||
"react-zoom-pan-pinch": "^3.6.1",
|
"react-zoom-pan-pinch": "^3.6.1",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
|||||||
31
pnpm-lock.yaml
generated
31
pnpm-lock.yaml
generated
@ -296,6 +296,9 @@ importers:
|
|||||||
prisma:
|
prisma:
|
||||||
specifier: ^5.22.0
|
specifier: ^5.22.0
|
||||||
version: 5.22.0
|
version: 5.22.0
|
||||||
|
prismjs:
|
||||||
|
specifier: ^1.29.0
|
||||||
|
version: 1.29.0
|
||||||
puppeteer:
|
puppeteer:
|
||||||
specifier: ^23.11.1
|
specifier: ^23.11.1
|
||||||
version: 23.11.1(typescript@5.7.3)
|
version: 23.11.1(typescript@5.7.3)
|
||||||
@ -326,6 +329,9 @@ importers:
|
|||||||
react-router:
|
react-router:
|
||||||
specifier: ^7.1.1
|
specifier: ^7.1.1
|
||||||
version: 7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
react-simple-code-editor:
|
||||||
|
specifier: ^0.14.1
|
||||||
|
version: 0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
react-zoom-pan-pinch:
|
react-zoom-pan-pinch:
|
||||||
specifier: ^3.6.1
|
specifier: ^3.6.1
|
||||||
version: 3.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 3.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -507,6 +513,9 @@ importers:
|
|||||||
'@types/passport-local':
|
'@types/passport-local':
|
||||||
specifier: ^1.0.38
|
specifier: ^1.0.38
|
||||||
version: 1.0.38
|
version: 1.0.38
|
||||||
|
'@types/prismjs':
|
||||||
|
specifier: ^1.26.5
|
||||||
|
version: 1.26.5
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.3.18
|
specifier: ^18.3.18
|
||||||
version: 18.3.18
|
version: 18.3.18
|
||||||
@ -4405,6 +4414,9 @@ packages:
|
|||||||
'@types/pg@8.6.1':
|
'@types/pg@8.6.1':
|
||||||
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
|
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
|
||||||
|
|
||||||
|
'@types/prismjs@1.26.5':
|
||||||
|
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
|
||||||
|
|
||||||
'@types/prop-types@15.7.11':
|
'@types/prop-types@15.7.11':
|
||||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||||
|
|
||||||
@ -9398,6 +9410,10 @@ packages:
|
|||||||
engines: {node: '>=16.13'}
|
engines: {node: '>=16.13'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
prismjs@1.29.0:
|
||||||
|
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
proc-log@3.0.0:
|
proc-log@3.0.0:
|
||||||
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
|
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
|
||||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||||
@ -9693,6 +9709,12 @@ packages:
|
|||||||
react-dom:
|
react-dom:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
react-simple-code-editor@0.14.1:
|
||||||
|
resolution: {integrity: sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==}
|
||||||
|
peerDependencies:
|
||||||
|
react: '>=16.8.0'
|
||||||
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
react-style-singleton@2.2.1:
|
react-style-singleton@2.2.1:
|
||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -16161,6 +16183,8 @@ snapshots:
|
|||||||
pg-protocol: 1.7.0
|
pg-protocol: 1.7.0
|
||||||
pg-types: 2.2.0
|
pg-types: 2.2.0
|
||||||
|
|
||||||
|
'@types/prismjs@1.26.5': {}
|
||||||
|
|
||||||
'@types/prop-types@15.7.11': {}
|
'@types/prop-types@15.7.11': {}
|
||||||
|
|
||||||
'@types/pug@2.0.10':
|
'@types/pug@2.0.10':
|
||||||
@ -22313,6 +22337,8 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
prismjs@1.29.0: {}
|
||||||
|
|
||||||
proc-log@3.0.0: {}
|
proc-log@3.0.0: {}
|
||||||
|
|
||||||
process-nextick-args@2.0.1: {}
|
process-nextick-args@2.0.1: {}
|
||||||
@ -22712,6 +22738,11 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
|
react-simple-code-editor@0.14.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
|
dependencies:
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
react-style-singleton@2.2.1(@types/react@18.3.18)(react@18.3.1):
|
react-style-singleton@2.2.1(@types/react@18.3.18)(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
get-nonce: 1.0.1
|
get-nonce: 1.0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user