mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2025-11-10 12:32:28 +10:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5dc15dc08 | |||
| eab996f7e7 | |||
| 43c5a33773 | |||
| 7fb0226ddc | |||
| db6e7a7480 | |||
| 6335ad1571 |
2
.github/workflows/publish-docker-image.yml
vendored
2
.github/workflows/publish-docker-image.yml
vendored
@ -153,7 +153,7 @@ jobs:
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Deploy the latest image on rxresu.me
|
||||
run: curl -Xk POST ${{ secrets.SERVICE_WEBHOOK }}
|
||||
run: curl -kX POST ${{ secrets.SERVICE_WEBHOOK }}
|
||||
|
||||
- name: Inform about the release on Discord
|
||||
uses: sarisia/actions-status-discord@v1.14.3
|
||||
|
||||
@ -40,12 +40,5 @@
|
||||
|
||||
<!-- Phosphor Icons -->
|
||||
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
||||
|
||||
<!-- Simple Icons -->
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/simple-icons-font@v14/font/simple-icons.min.css"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
|
||||
type BrandIconProps = {
|
||||
slug: string;
|
||||
};
|
||||
@ -8,12 +6,12 @@ export const BrandIcon = ({ slug }: BrandIconProps) => {
|
||||
if (slug === "linkedin") {
|
||||
return (
|
||||
<img
|
||||
alt="LinkedIn"
|
||||
alt="linkedin"
|
||||
className="size-4"
|
||||
src={`${window.location.origin}/support-logos/linkedin.svg`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <i className={cn("si si--color text-[1rem]", `si-${slug}`)} />;
|
||||
return <img alt={slug} className="size-4" src={`https://cdn.simpleicons.org/${slug}`} />;
|
||||
};
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { StrictMode } from "react";
|
||||
import * as ReactDOM from "react-dom/client";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { RouterProvider } from "react-router";
|
||||
|
||||
import { router } from "./router";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
import webfontloader from "webfontloader";
|
||||
|
||||
import { useArtboardStore } from "../store/artboard";
|
||||
@ -55,5 +55,11 @@ export const ArtboardPage = () => {
|
||||
}
|
||||
}, [metadata]);
|
||||
|
||||
return <Outlet />;
|
||||
return (
|
||||
<>
|
||||
{metadata.css.visible && <style lang="css">{`[data-page] { ${metadata.css.value} }`}</style>}
|
||||
|
||||
<Outlet />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useEffect } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { useArtboardStore } from "../store/artboard";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createBrowserRouter, createRoutesFromChildren, Route } from "react-router-dom";
|
||||
import { createBrowserRouter, createRoutesFromChildren, Route } from "react-router";
|
||||
|
||||
import { ArtboardPage } from "../pages/artboard";
|
||||
import { BuilderLayout } from "../pages/builder";
|
||||
@ -6,7 +6,7 @@ import { PreviewLayout } from "../pages/preview";
|
||||
import { Providers } from "../providers";
|
||||
|
||||
export const routes = createRoutesFromChildren(
|
||||
<Route element={<Providers />}>
|
||||
<Route element={<Providers />} hydrateFallbackElement={<div>Loading...</div>}>
|
||||
<Route path="artboard" element={<ArtboardPage />}>
|
||||
<Route path="builder" element={<BuilderLayout />} />
|
||||
<Route path="preview" element={<PreviewLayout />} />
|
||||
|
||||
@ -43,12 +43,5 @@
|
||||
|
||||
<!-- Phosphor Icons -->
|
||||
<script src="https://unpkg.com/@phosphor-icons/web"></script>
|
||||
|
||||
<!-- Simple Icons -->
|
||||
<link
|
||||
type="text/css"
|
||||
rel="stylesheet"
|
||||
href="https://unpkg.com/simple-icons-font@v14/font/simple-icons.min.css"
|
||||
/>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
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 (forced-colors: 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": {
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -314,7 +314,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -314,7 +314,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -315,7 +315,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -289,7 +289,7 @@
|
||||
[[], []]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -288,7 +288,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -287,7 +287,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -289,7 +289,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -306,7 +306,7 @@
|
||||
[["projects", "certifications", "skills", "languages", "references"], []]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -287,7 +287,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -315,7 +315,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -288,7 +288,7 @@
|
||||
]
|
||||
],
|
||||
"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
|
||||
},
|
||||
"page": {
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
KeyboardShortcut,
|
||||
} from "@reactive-resume/ui";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { useLogout } from "../services/auth";
|
||||
|
||||
@ -26,7 +26,7 @@ export const UserOptions = ({ children }: Props) => {
|
||||
<DropdownMenuContent side="top" align="start" className="w-48">
|
||||
<DropdownMenuItem
|
||||
onClick={() => {
|
||||
navigate("/dashboard/settings");
|
||||
void navigate("/dashboard/settings");
|
||||
}}
|
||||
>
|
||||
{t`Settings`}
|
||||
|
||||
@ -2,7 +2,7 @@ import { t } from "@lingui/macro";
|
||||
import { deepSearchAndParseDates, ErrorMessage } from "@reactive-resume/utils";
|
||||
import _axios from "axios";
|
||||
import createAuthRefreshInterceptor from "axios-auth-refresh";
|
||||
import { redirect } from "react-router-dom";
|
||||
import { redirect } from "react-router";
|
||||
|
||||
import { refreshToken } from "@/client/services/auth";
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { StrictMode } from "react";
|
||||
import * as ReactDOM from "react-dom/client";
|
||||
import { RouterProvider } from "react-router-dom";
|
||||
import { RouterProvider } from "react-router";
|
||||
|
||||
import { router } from "./router";
|
||||
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import { useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useBackupOtp } from "@/client/services/auth";
|
||||
@ -39,7 +39,7 @@ export const BackupOtpPage = () => {
|
||||
try {
|
||||
await backupOtp(data);
|
||||
|
||||
navigate("/dashboard");
|
||||
void navigate("/dashboard");
|
||||
} catch {
|
||||
form.reset();
|
||||
}
|
||||
@ -92,7 +92,7 @@ export const BackupOtpPage = () => {
|
||||
variant="link"
|
||||
className="px-5"
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
void navigate(-1);
|
||||
}}
|
||||
>
|
||||
<ArrowLeft size={14} className="mr-2" />
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
import { useState } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useForgotPassword } from "@/client/services/auth";
|
||||
@ -93,7 +93,7 @@ export const ForgotPasswordPage = () => {
|
||||
variant="link"
|
||||
className="px-5"
|
||||
onClick={() => {
|
||||
navigate(-1);
|
||||
void navigate(-1);
|
||||
}}
|
||||
>
|
||||
<ArrowLeft size={14} className="mr-2" />
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { useMemo } from "react";
|
||||
import { Link, matchRoutes, Outlet, useLocation } from "react-router-dom";
|
||||
import { Link, matchRoutes, Outlet, useLocation } from "react-router";
|
||||
|
||||
import { LocaleSwitch } from "@/client/components/locale-switch";
|
||||
import { Logo } from "@/client/components/logo";
|
||||
|
||||
@ -20,7 +20,7 @@ import { cn } from "@reactive-resume/utils";
|
||||
import { useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useLogin } from "@/client/services/auth";
|
||||
|
||||
@ -20,7 +20,7 @@ import { cn } from "@reactive-resume/utils";
|
||||
import { useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useRegister } from "@/client/services/auth";
|
||||
@ -51,7 +51,7 @@ export const RegisterPage = () => {
|
||||
try {
|
||||
await register(data);
|
||||
|
||||
navigate("/auth/verify-email");
|
||||
void navigate("/auth/verify-email");
|
||||
} catch {
|
||||
form.reset();
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useResetPassword } from "@/client/services/auth";
|
||||
@ -42,7 +42,7 @@ export const ResetPasswordPage = () => {
|
||||
try {
|
||||
await resetPassword(data);
|
||||
|
||||
navigate("/auth/login");
|
||||
void navigate("/auth/login");
|
||||
} catch {
|
||||
form.reset();
|
||||
}
|
||||
@ -50,7 +50,7 @@ export const ResetPasswordPage = () => {
|
||||
|
||||
// Redirect the user to the forgot password page if the token is not present.
|
||||
useEffect(() => {
|
||||
if (!token) navigate("/auth/forgot-password");
|
||||
if (!token) void navigate("/auth/forgot-password");
|
||||
}, [token, navigate]);
|
||||
|
||||
return (
|
||||
|
||||
@ -3,7 +3,7 @@ import { ArrowRight, Info, SealCheck } from "@phosphor-icons/react";
|
||||
import { Alert, AlertDescription, AlertTitle, Button } from "@reactive-resume/ui";
|
||||
import { useEffect } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router-dom";
|
||||
import { Link, useNavigate, useSearchParams } from "react-router";
|
||||
|
||||
import { useToast } from "@/client/hooks/use-toast";
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
@ -28,7 +28,7 @@ export const VerifyEmailPage = () => {
|
||||
title: t`Your email address has been verified successfully.`,
|
||||
});
|
||||
|
||||
navigate("/dashboard/resumes", { replace: true });
|
||||
void navigate("/dashboard/resumes", { replace: true });
|
||||
};
|
||||
|
||||
if (!token) return;
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
import { useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import { Link, useNavigate } from "react-router";
|
||||
import { z } from "zod";
|
||||
|
||||
import { useVerifyOtp } from "@/client/services/auth";
|
||||
@ -39,7 +39,7 @@ export const VerifyOtpPage = () => {
|
||||
try {
|
||||
await verifyOtp(data);
|
||||
|
||||
navigate("/dashboard");
|
||||
void navigate("/dashboard");
|
||||
} catch {
|
||||
form.reset();
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { t } from "@lingui/macro";
|
||||
import { HouseSimple, Lock, SidebarSimple } from "@phosphor-icons/react";
|
||||
import { Button, Tooltip } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { useBuilderStore } from "@/client/stores/builder";
|
||||
import { useResumeStore } from "@/client/stores/resume";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { useBreakpoint } from "@reactive-resume/hooks";
|
||||
import { Panel, PanelGroup, PanelResizeHandle, Sheet, SheetContent } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { useBuilderStore } from "@/client/stores/builder";
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { t } from "@lingui/macro";
|
||||
import { ResumeDto } from "@reactive-resume/dto";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { LoaderFunction, redirect } from "react-router-dom";
|
||||
import { LoaderFunction, redirect } from "react-router";
|
||||
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
import { findResumeById } from "@/client/services/resume";
|
||||
|
||||
@ -17,7 +17,7 @@ import {
|
||||
} from "@reactive-resume/schema";
|
||||
import { Button, ScrollArea, Separator } from "@reactive-resume/ui";
|
||||
import { Fragment, useRef } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { Icon } from "@/client/components/icon";
|
||||
import { UserAvatar } from "@/client/components/user-avatar";
|
||||
|
||||
@ -5,6 +5,7 @@ import { useRef } from "react";
|
||||
import { Copyright } from "@/client/components/copyright";
|
||||
import { ThemeSwitch } from "@/client/components/theme-switch";
|
||||
|
||||
import { CssSection } from "./sections/css";
|
||||
import { ExportSection } from "./sections/export";
|
||||
import { InformationSection } from "./sections/information";
|
||||
import { LayoutSection } from "./sections/layout";
|
||||
@ -37,6 +38,8 @@ export const RightSidebar = () => {
|
||||
<Separator />
|
||||
<ThemeSection />
|
||||
<Separator />
|
||||
<CssSection />
|
||||
<Separator />
|
||||
<PageSection />
|
||||
<Separator />
|
||||
<SharingSection />
|
||||
@ -85,6 +88,13 @@ export const RightSidebar = () => {
|
||||
scrollIntoView("#theme");
|
||||
}}
|
||||
/>
|
||||
<SectionIcon
|
||||
id="css"
|
||||
name={t`Custom CSS`}
|
||||
onClick={() => {
|
||||
scrollIntoView("#css");
|
||||
}}
|
||||
/>
|
||||
<SectionIcon
|
||||
id="page"
|
||||
name={t`Page`}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
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 {
|
||||
Code,
|
||||
DiamondsFour,
|
||||
DownloadSimple,
|
||||
IconProps,
|
||||
@ -19,6 +20,7 @@ export type MetadataKey =
|
||||
| "layout"
|
||||
| "typography"
|
||||
| "theme"
|
||||
| "css"
|
||||
| "page"
|
||||
| "locale"
|
||||
| "sharing"
|
||||
@ -45,6 +47,9 @@ export const getSectionIcon = (id: MetadataKey, props: IconProps = {}) => {
|
||||
case "theme": {
|
||||
return <Palette size={18} {...props} />;
|
||||
}
|
||||
case "css": {
|
||||
return <Code size={18} {...props} />;
|
||||
}
|
||||
case "page": {
|
||||
return <ReadCvLogo size={18} {...props} />;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { FadersHorizontal, ReadCvLogo } from "@phosphor-icons/react";
|
||||
import { Button, KeyboardShortcut, Separator } from "@reactive-resume/ui";
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import { motion } from "framer-motion";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import { Link, useLocation, useNavigate } from "react-router";
|
||||
import useKeyboardShortcut from "use-keyboard-shortcut";
|
||||
|
||||
import { Copyright } from "@/client/components/copyright";
|
||||
@ -71,12 +71,12 @@ export const Sidebar = ({ setOpen }: SidebarProps) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
useKeyboardShortcut(["shift", "r"], () => {
|
||||
navigate("/dashboard/resumes");
|
||||
void navigate("/dashboard/resumes");
|
||||
setOpen?.(false);
|
||||
});
|
||||
|
||||
useKeyboardShortcut(["shift", "s"], () => {
|
||||
navigate("/dashboard/settings");
|
||||
void navigate("/dashboard/settings");
|
||||
setOpen?.(false);
|
||||
});
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { SidebarSimple } from "@phosphor-icons/react";
|
||||
import { Button, Sheet, SheetClose, SheetContent, SheetTrigger } from "@reactive-resume/ui";
|
||||
import { motion } from "framer-motion";
|
||||
import { useState } from "react";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { Sidebar } from "./_components/sidebar";
|
||||
|
||||
|
||||
@ -33,7 +33,8 @@ import {
|
||||
Input,
|
||||
Tooltip,
|
||||
} from "@reactive-resume/ui";
|
||||
import { cn, generateRandomName, kebabCase } from "@reactive-resume/utils";
|
||||
import { cn, generateRandomName } from "@reactive-resume/utils";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
@ -42,7 +43,7 @@ import { useCreateResume, useDeleteResume, useUpdateResume } from "@/client/serv
|
||||
import { useImportResume } from "@/client/services/resume/import";
|
||||
import { useDialog } from "@/client/stores/dialog";
|
||||
|
||||
const formSchema = createResumeSchema.extend({ id: idSchema.optional() });
|
||||
const formSchema = createResumeSchema.extend({ id: idSchema.optional(), slug: z.string() });
|
||||
|
||||
type FormValues = z.infer<typeof formSchema>;
|
||||
|
||||
@ -71,7 +72,7 @@ export const ResumeDialog = () => {
|
||||
}, [isOpen, payload]);
|
||||
|
||||
useEffect(() => {
|
||||
const slug = kebabCase(form.watch("title"));
|
||||
const slug = slugify(form.watch("title"));
|
||||
form.setValue("slug", slug);
|
||||
}, [form.watch("title")]);
|
||||
|
||||
@ -122,7 +123,7 @@ export const ResumeDialog = () => {
|
||||
const onGenerateRandomName = () => {
|
||||
const name = generateRandomName();
|
||||
form.setValue("title", name);
|
||||
form.setValue("slug", kebabCase(name));
|
||||
form.setValue("slug", slugify(name));
|
||||
};
|
||||
|
||||
const onCreateSample = async () => {
|
||||
@ -130,7 +131,7 @@ export const ResumeDialog = () => {
|
||||
|
||||
await duplicateResume({
|
||||
title: randomName,
|
||||
slug: kebabCase(randomName),
|
||||
slug: slugify(randomName),
|
||||
data: sampleResume,
|
||||
});
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ import {
|
||||
import { cn } from "@reactive-resume/utils";
|
||||
import dayjs from "dayjs";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { useDialog } from "@/client/stores/dialog";
|
||||
|
||||
@ -37,7 +37,7 @@ export const ResumeCard = ({ resume }: Props) => {
|
||||
const lastUpdated = dayjs().to(resume.updatedAt);
|
||||
|
||||
const onOpen = () => {
|
||||
navigate(`/builder/${resume.id}`);
|
||||
void navigate(`/builder/${resume.id}`);
|
||||
};
|
||||
|
||||
const onUpdate = () => {
|
||||
|
||||
@ -22,7 +22,7 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "@reactive-resume/ui";
|
||||
import dayjs from "dayjs";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { useDialog } from "@/client/stores/dialog";
|
||||
|
||||
@ -40,7 +40,7 @@ export const ResumeListItem = ({ resume }: Props) => {
|
||||
const lastUpdated = dayjs().to(resume.updatedAt);
|
||||
|
||||
const onOpen = () => {
|
||||
navigate(`/builder/${resume.id}`);
|
||||
void navigate(`/builder/${resume.id}`);
|
||||
};
|
||||
|
||||
const onUpdate = () => {
|
||||
|
||||
@ -11,7 +11,7 @@ import {
|
||||
Input,
|
||||
} from "@reactive-resume/ui";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
import { useCounter } from "usehooks-ts";
|
||||
import { z } from "zod";
|
||||
|
||||
@ -52,7 +52,7 @@ export const DangerZoneSettings = () => {
|
||||
title: t`Your account and all your data has been deleted successfully. Goodbye!`,
|
||||
});
|
||||
|
||||
navigate("/");
|
||||
void navigate("/");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Separator } from "@reactive-resume/ui";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { Copyright } from "@/client/components/copyright";
|
||||
import { LocaleSwitch } from "@/client/components/locale-switch";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { motion } from "framer-motion";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { Logo } from "@/client/components/logo";
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { ScrollArea } from "@reactive-resume/ui";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { Footer } from "./components/footer";
|
||||
import { Header } from "./components/header";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { t } from "@lingui/macro";
|
||||
import { Book, SignOut } from "@phosphor-icons/react";
|
||||
import { Button } from "@reactive-resume/ui";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link } from "react-router";
|
||||
|
||||
import { useLogout } from "@/client/services/auth";
|
||||
import { useAuthStore } from "@/client/stores/auth";
|
||||
|
||||
@ -5,7 +5,7 @@ import { Button } from "@reactive-resume/ui";
|
||||
import { pageSizeMap } from "@reactive-resume/utils";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { Link, LoaderFunction, redirect, useLoaderData } from "react-router-dom";
|
||||
import { Link, LoaderFunction, redirect, useLoaderData } from "react-router";
|
||||
|
||||
import { Icon } from "@/client/components/icon";
|
||||
import { ThemeSwitch } from "@/client/components/theme-switch";
|
||||
@ -22,8 +22,8 @@ export const PublicResumePage = () => {
|
||||
|
||||
const { printResume, loading } = usePrintResume();
|
||||
|
||||
const { id, title, data: resume } = useLoaderData() as ResumeDto;
|
||||
const format = resume.metadata.page.format;
|
||||
const { id, title, data: resume } = useLoaderData();
|
||||
const format = resume.metadata.page.format as keyof typeof pageSizeMap;
|
||||
|
||||
const updateResumeInFrame = useCallback(() => {
|
||||
if (!frameRef.current?.contentWindow) return;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { TooltipProvider } from "@reactive-resume/ui";
|
||||
import { QueryClientProvider } from "@tanstack/react-query";
|
||||
import { HelmetProvider } from "react-helmet-async";
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Outlet } from "react-router";
|
||||
|
||||
import { helmetContext } from "../constants/helmet";
|
||||
import { queryClient } from "../libs/query-client";
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Navigate, Outlet, useLocation } from "react-router-dom";
|
||||
import { Navigate, Outlet, useLocation } from "react-router";
|
||||
|
||||
import { useUser } from "@/client/services/user";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Navigate, Outlet, useSearchParams } from "react-router-dom";
|
||||
import { Navigate, Outlet, useSearchParams } from "react-router";
|
||||
|
||||
import { useAuthStore } from "@/client/stores/auth";
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { createBrowserRouter, createRoutesFromElements, Navigate, Route } from "react-router-dom";
|
||||
import { createBrowserRouter, createRoutesFromElements, Navigate, Route } from "react-router";
|
||||
|
||||
import { BackupOtpPage } from "../pages/auth/backup-otp/page";
|
||||
import { ForgotPasswordPage } from "../pages/auth/forgot-password/page";
|
||||
@ -23,7 +23,8 @@ import { GuestGuard } from "./guards/guest";
|
||||
import { authLoader } from "./loaders/auth";
|
||||
|
||||
export const routes = createRoutesFromElements(
|
||||
<Route element={<Providers />}>
|
||||
// eslint-disable-next-line lingui/no-unlocalized-strings
|
||||
<Route element={<Providers />} hydrateFallbackElement={<div>Loading...</div>}>
|
||||
<Route element={<HomeLayout />}>
|
||||
<Route path="/" element={<HomePage />} />
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { authResponseSchema, UserDto } from "@reactive-resume/dto";
|
||||
import { LoaderFunction, redirect } from "react-router-dom";
|
||||
import { LoaderFunction, redirect } from "react-router";
|
||||
|
||||
import { USER_KEY } from "@/client/constants/query-keys";
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { AuthResponseDto, LoginDto } from "@reactive-resume/dto";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { AxiosResponse } from "axios";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useNavigate } from "react-router";
|
||||
|
||||
import { axios } from "@/client/libs/axios";
|
||||
import { queryClient } from "@/client/libs/query-client";
|
||||
@ -28,7 +28,7 @@ export const useLogin = () => {
|
||||
mutationFn: login,
|
||||
onSuccess: (data) => {
|
||||
if (data.status === "2fa_required") {
|
||||
navigate("/auth/verify-otp");
|
||||
void navigate("/auth/verify-otp");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -150,6 +150,17 @@ export class PrinterService {
|
||||
return temporaryHtml_;
|
||||
}, 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 buffer = Buffer.from(uint8array);
|
||||
pagesBuffer.push(buffer);
|
||||
@ -209,6 +220,8 @@ export class PrinterService {
|
||||
|
||||
return resumeUrl;
|
||||
} catch (error) {
|
||||
this.logger.error(error);
|
||||
|
||||
throw new InternalServerErrorException(
|
||||
ErrorMessage.ResumePrinterError,
|
||||
(error as Error).message,
|
||||
|
||||
@ -8,7 +8,8 @@ import { Prisma } from "@prisma/client";
|
||||
import { CreateResumeDto, ImportResumeDto, ResumeDto, UpdateResumeDto } from "@reactive-resume/dto";
|
||||
import { defaultResumeData, ResumeData } from "@reactive-resume/schema";
|
||||
import type { DeepPartial } from "@reactive-resume/utils";
|
||||
import { ErrorMessage, generateRandomName, kebabCase } from "@reactive-resume/utils";
|
||||
import { ErrorMessage, generateRandomName } from "@reactive-resume/utils";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import deepmerge from "deepmerge";
|
||||
import { PrismaService } from "nestjs-prisma";
|
||||
|
||||
@ -40,7 +41,7 @@ export class ResumeService {
|
||||
userId,
|
||||
title: createResumeDto.title,
|
||||
visibility: createResumeDto.visibility,
|
||||
slug: createResumeDto.slug ?? kebabCase(createResumeDto.title),
|
||||
slug: createResumeDto.slug ?? slugify(createResumeDto.title),
|
||||
},
|
||||
});
|
||||
}
|
||||
@ -54,7 +55,7 @@ export class ResumeService {
|
||||
visibility: "private",
|
||||
data: importResumeDto.data,
|
||||
title: importResumeDto.title ?? randomTitle,
|
||||
slug: importResumeDto.slug ?? kebabCase(randomTitle),
|
||||
slug: importResumeDto.slug ?? slugify(randomTitle),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Injectable, InternalServerErrorException, Logger, OnModuleInit } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { MinioClient, MinioService } from "nestjs-minio-client";
|
||||
import sharp from "sharp";
|
||||
|
||||
@ -116,14 +117,19 @@ export class StorageService implements OnModuleInit {
|
||||
) {
|
||||
const extension = type === "resumes" ? "pdf" : "jpg";
|
||||
const storageUrl = this.configService.getOrThrow<string>("STORAGE_URL");
|
||||
const filepath = `${userId}/${type}/${filename}.${extension}`;
|
||||
|
||||
let normalizedFilename = slugify(filename);
|
||||
if (!normalizedFilename) normalizedFilename = createId();
|
||||
|
||||
const filepath = `${userId}/${type}/${normalizedFilename}.${extension}`;
|
||||
const url = `${storageUrl}/${filepath}`;
|
||||
|
||||
const metadata =
|
||||
extension === "jpg"
|
||||
? { "Content-Type": "image/jpeg" }
|
||||
: {
|
||||
"Content-Type": "application/pdf",
|
||||
"Content-Disposition": `attachment; filename=${filename}.${extension}`,
|
||||
"Content-Disposition": `attachment; filename=${normalizedFilename}.${extension}`,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
@ -11,8 +11,10 @@
|
||||
"dependencies": {
|
||||
"@reactive-resume/utils": "*",
|
||||
"@reactive-resume/schema": "*",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"nestjs-zod": "^3.0.0",
|
||||
"@swc/helpers": "~0.5.11",
|
||||
"zod": "^3.24.1"
|
||||
"zod": "^3.24.1",
|
||||
"@paralleldrive/cuid2": "^2.2.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,19 @@
|
||||
import { kebabCase } from "@reactive-resume/utils";
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { createZodDto } from "nestjs-zod/dto";
|
||||
import { z } from "zod";
|
||||
|
||||
export const createResumeSchema = z.object({
|
||||
title: z.string().min(1),
|
||||
slug: z.string().min(1).transform(kebabCase).optional(),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((value) => {
|
||||
const slug = slugify(value);
|
||||
if (!slug) return createId();
|
||||
return slug;
|
||||
})
|
||||
.optional(),
|
||||
visibility: z.enum(["public", "private"]).default("private"),
|
||||
});
|
||||
|
||||
|
||||
@ -1,11 +1,20 @@
|
||||
import { createId } from "@paralleldrive/cuid2";
|
||||
import { resumeDataSchema } from "@reactive-resume/schema";
|
||||
import { kebabCase } from "@reactive-resume/utils";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { createZodDto } from "nestjs-zod/dto";
|
||||
import { z } from "zod";
|
||||
|
||||
export const importResumeSchema = z.object({
|
||||
title: z.string().optional(),
|
||||
slug: z.string().min(1).transform(kebabCase).optional(),
|
||||
slug: z
|
||||
.string()
|
||||
.min(1)
|
||||
.transform((value) => {
|
||||
const slug = slugify(value);
|
||||
if (slug === "") return createId();
|
||||
return slug;
|
||||
})
|
||||
.optional(),
|
||||
visibility: z.enum(["public", "private"]).default("private").optional(),
|
||||
data: resumeDataSchema,
|
||||
});
|
||||
|
||||
@ -12,7 +12,7 @@ export const metadataSchema = z.object({
|
||||
template: z.string().default("rhyhorn"),
|
||||
layout: z.array(z.array(z.array(z.string()))).default(defaultLayout), // pages -> columns -> sections
|
||||
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),
|
||||
}),
|
||||
page: z.object({
|
||||
@ -50,7 +50,7 @@ export const defaultMetadata: Metadata = {
|
||||
template: "rhyhorn",
|
||||
layout: defaultLayout,
|
||||
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,
|
||||
},
|
||||
page: {
|
||||
|
||||
@ -308,7 +308,7 @@ export const sampleResume: ResumeData = {
|
||||
],
|
||||
],
|
||||
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,
|
||||
},
|
||||
page: {
|
||||
|
||||
@ -7,4 +7,4 @@ export const pageSizeMap = {
|
||||
width: 216,
|
||||
height: 279,
|
||||
},
|
||||
};
|
||||
} as const;
|
||||
|
||||
@ -30,17 +30,6 @@ export const extractUrl = (string: string) => {
|
||||
return result ? result[0] : null;
|
||||
};
|
||||
|
||||
export const kebabCase = (string?: string | null) => {
|
||||
if (!string) return "";
|
||||
|
||||
return (
|
||||
string
|
||||
.match(/[A-Z]{2,}(?=[A-Z][a-z]+\d*|\b)|[A-Z]?[a-z]+\d*|[A-Z]|\d+/gu)
|
||||
?.join("-")
|
||||
.toLowerCase() ?? ""
|
||||
);
|
||||
};
|
||||
|
||||
export const generateRandomName = () => {
|
||||
return uniqueNamesGenerator({
|
||||
dictionaries: [adjectives, adjectives, animals],
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
getInitials,
|
||||
isEmptyString,
|
||||
isUrl,
|
||||
kebabCase,
|
||||
processUsername,
|
||||
} from "../string";
|
||||
|
||||
@ -40,16 +39,6 @@ describe("extractUrl", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("kebabCase", () => {
|
||||
it("converts a string to kebab-case", () => {
|
||||
expect(kebabCase("fooBar")).toBe("foo-bar");
|
||||
expect(kebabCase("Foo Bar")).toBe("foo-bar");
|
||||
expect(kebabCase("foo_bar")).toBe("foo-bar");
|
||||
expect(kebabCase("")).toBe("");
|
||||
expect(kebabCase(null)).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
describe("generateRandomName", () => {
|
||||
it("generates a random name", () => {
|
||||
const name = generateRandomName();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@reactive-resume/source",
|
||||
"description": "A free and open-source resume builder that simplifies the process of creating, updating, and sharing your resume.",
|
||||
"version": "4.3.3",
|
||||
"version": "4.3.5",
|
||||
"license": "MIT",
|
||||
"private": true,
|
||||
"author": {
|
||||
@ -75,6 +75,7 @@
|
||||
"@types/passport-github2": "^1.2.9",
|
||||
"@types/passport-google-oauth20": "^2.0.16",
|
||||
"@types/passport-local": "^1.0.38",
|
||||
"@types/prismjs": "^1.26.5",
|
||||
"@types/react": "^18.3.18",
|
||||
"@types/react-dom": "^18.3.5",
|
||||
"@types/react-is": "^18.3.1",
|
||||
@ -168,6 +169,7 @@
|
||||
"@radix-ui/react-toggle-group": "^1.1.1",
|
||||
"@radix-ui/react-tooltip": "^1.1.6",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.1",
|
||||
"@sindresorhus/slugify": "^2.2.1",
|
||||
"@swc/helpers": "^0.5.15",
|
||||
"@tanstack/react-query": "^5.64.0",
|
||||
"@tiptap/extension-highlight": "^2.11.2",
|
||||
@ -214,6 +216,7 @@
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"prisma": "^5.22.0",
|
||||
"prismjs": "^1.29.0",
|
||||
"puppeteer": "^23.11.1",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^18.3.1",
|
||||
@ -223,7 +226,8 @@
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-parallax-tilt": "^1.7.272",
|
||||
"react-resizable-panels": "^2.1.7",
|
||||
"react-router-dom": "^6.28.1",
|
||||
"react-router": "^7.1.1",
|
||||
"react-simple-code-editor": "^0.14.1",
|
||||
"react-zoom-pan-pinch": "^3.6.1",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
|
||||
113
pnpm-lock.yaml
generated
113
pnpm-lock.yaml
generated
@ -155,6 +155,9 @@ importers:
|
||||
'@radix-ui/react-visually-hidden':
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@sindresorhus/slugify':
|
||||
specifier: ^2.2.1
|
||||
version: 2.2.1
|
||||
'@swc/helpers':
|
||||
specifier: ^0.5.15
|
||||
version: 0.5.15
|
||||
@ -293,6 +296,9 @@ importers:
|
||||
prisma:
|
||||
specifier: ^5.22.0
|
||||
version: 5.22.0
|
||||
prismjs:
|
||||
specifier: ^1.29.0
|
||||
version: 1.29.0
|
||||
puppeteer:
|
||||
specifier: ^23.11.1
|
||||
version: 23.11.1(typescript@5.7.3)
|
||||
@ -320,9 +326,12 @@ importers:
|
||||
react-resizable-panels:
|
||||
specifier: ^2.1.7
|
||||
version: 2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-router-dom:
|
||||
specifier: ^6.28.1
|
||||
version: 6.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react-router:
|
||||
specifier: ^7.1.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:
|
||||
specifier: ^3.6.1
|
||||
version: 3.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@ -504,6 +513,9 @@ importers:
|
||||
'@types/passport-local':
|
||||
specifier: ^1.0.38
|
||||
version: 1.0.38
|
||||
'@types/prismjs':
|
||||
specifier: ^1.26.5
|
||||
version: 1.26.5
|
||||
'@types/react':
|
||||
specifier: ^18.3.18
|
||||
version: 18.3.18
|
||||
@ -3576,10 +3588,6 @@ packages:
|
||||
'@remirror/core-constants@3.0.0':
|
||||
resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
|
||||
|
||||
'@remix-run/router@1.21.0':
|
||||
resolution: {integrity: sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
'@rollup/pluginutils@5.1.4':
|
||||
resolution: {integrity: sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -3749,6 +3757,14 @@ packages:
|
||||
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
'@sindresorhus/slugify@2.2.1':
|
||||
resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@sindresorhus/transliterate@1.6.0':
|
||||
resolution: {integrity: sha512-doH1gimEu3A46VX6aVxpHTeHrytJAG6HgdxntYnCFiIFHEM/ZGpG8KiZGBChchjQmG0XFIBL552kBTjVcMZXwQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@sinonjs/commons@3.0.1':
|
||||
resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==}
|
||||
|
||||
@ -4224,6 +4240,9 @@ packages:
|
||||
peerDependencies:
|
||||
'@types/express': '*'
|
||||
|
||||
'@types/cookie@0.6.0':
|
||||
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
|
||||
|
||||
'@types/cookies@0.9.0':
|
||||
resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==}
|
||||
|
||||
@ -4395,6 +4414,9 @@ packages:
|
||||
'@types/pg@8.6.1':
|
||||
resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
|
||||
|
||||
'@types/prismjs@1.26.5':
|
||||
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
|
||||
|
||||
'@types/prop-types@15.7.11':
|
||||
resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
|
||||
|
||||
@ -5590,6 +5612,10 @@ packages:
|
||||
resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
cookie@1.0.2:
|
||||
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cookies@0.9.1:
|
||||
resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
@ -9384,6 +9410,10 @@ packages:
|
||||
engines: {node: '>=16.13'}
|
||||
hasBin: true
|
||||
|
||||
prismjs@1.29.0:
|
||||
resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
proc-log@3.0.0:
|
||||
resolution: {integrity: sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
@ -9669,18 +9699,21 @@ packages:
|
||||
react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
|
||||
|
||||
react-router-dom@6.28.1:
|
||||
resolution: {integrity: sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
react-router@7.1.1:
|
||||
resolution: {integrity: sha512-39sXJkftkKWRZ2oJtHhCxmoCrBCULr/HAH4IT5DHlgu/Q0FCPV0S4Lx+abjDTx/74xoZzNYDYbOZWlJjruyuDQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
react: '>=18'
|
||||
react-dom: '>=18'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
react-router@6.28.1:
|
||||
resolution: {integrity: sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
react-simple-code-editor@0.14.1:
|
||||
resolution: {integrity: sha512-BR5DtNRy+AswWJECyA17qhUDvrrCZ6zXOCfkQY5zSmb96BVUbpVAv03WpcjcwtCwiLbIANx3gebHOcXYn1EHow==}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
react-style-singleton@2.2.1:
|
||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||
@ -10062,6 +10095,9 @@ packages:
|
||||
resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
set-cookie-parser@2.7.1:
|
||||
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -10757,6 +10793,9 @@ packages:
|
||||
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||
engines: {node: '>=0.6.x'}
|
||||
|
||||
turbo-stream@2.4.0:
|
||||
resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==}
|
||||
|
||||
type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
@ -15212,8 +15251,6 @@ snapshots:
|
||||
|
||||
'@remirror/core-constants@3.0.0': {}
|
||||
|
||||
'@remix-run/router@1.21.0': {}
|
||||
|
||||
'@rollup/pluginutils@5.1.4(rollup@4.30.1)':
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
@ -15388,6 +15425,15 @@ snapshots:
|
||||
|
||||
'@sindresorhus/is@4.6.0': {}
|
||||
|
||||
'@sindresorhus/slugify@2.2.1':
|
||||
dependencies:
|
||||
'@sindresorhus/transliterate': 1.6.0
|
||||
escape-string-regexp: 5.0.0
|
||||
|
||||
'@sindresorhus/transliterate@1.6.0':
|
||||
dependencies:
|
||||
escape-string-regexp: 5.0.0
|
||||
|
||||
'@sinonjs/commons@3.0.1':
|
||||
dependencies:
|
||||
type-detect: 4.0.8
|
||||
@ -15907,6 +15953,8 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/express': 4.17.21
|
||||
|
||||
'@types/cookie@0.6.0': {}
|
||||
|
||||
'@types/cookies@0.9.0':
|
||||
dependencies:
|
||||
'@types/connect': 3.4.38
|
||||
@ -16135,6 +16183,8 @@ snapshots:
|
||||
pg-protocol: 1.7.0
|
||||
pg-types: 2.2.0
|
||||
|
||||
'@types/prismjs@1.26.5': {}
|
||||
|
||||
'@types/prop-types@15.7.11': {}
|
||||
|
||||
'@types/pug@2.0.10':
|
||||
@ -17577,6 +17627,8 @@ snapshots:
|
||||
|
||||
cookie@0.7.2: {}
|
||||
|
||||
cookie@1.0.2: {}
|
||||
|
||||
cookies@0.9.1:
|
||||
dependencies:
|
||||
depd: 2.0.0
|
||||
@ -22285,6 +22337,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
prismjs@1.29.0: {}
|
||||
|
||||
proc-log@3.0.0: {}
|
||||
|
||||
process-nextick-args@2.0.1: {}
|
||||
@ -22674,17 +22728,20 @@ snapshots:
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
react-router-dom@6.28.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
react-router@7.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@types/cookie': 0.6.0
|
||||
cookie: 1.0.2
|
||||
react: 18.3.1
|
||||
set-cookie-parser: 2.7.1
|
||||
turbo-stream: 2.4.0
|
||||
optionalDependencies:
|
||||
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:
|
||||
'@remix-run/router': 1.21.0
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
react-router: 6.28.1(react@18.3.1)
|
||||
|
||||
react-router@6.28.1(react@18.3.1):
|
||||
dependencies:
|
||||
'@remix-run/router': 1.21.0
|
||||
react: 18.3.1
|
||||
|
||||
react-style-singleton@2.2.1(@types/react@18.3.18)(react@18.3.1):
|
||||
dependencies:
|
||||
@ -23133,6 +23190,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
set-cookie-parser@2.7.1: {}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
@ -23953,6 +24012,8 @@ snapshots:
|
||||
|
||||
tsscmp@1.0.6: {}
|
||||
|
||||
turbo-stream@2.4.0: {}
|
||||
|
||||
type-check@0.4.0:
|
||||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
Reference in New Issue
Block a user