1
1
mirror of https://github.com/theoludwig/theoludwig.git synced 2025-05-29 22:37:44 +02:00

Compare commits

...

34 Commits

Author SHA1 Message Date
9f79b88202 chore(release): 3.2.4 [skip ci] 2024-04-13 17:17:11 +00:00
23d9caf578 style: fix eslint 2024-04-13 19:13:48 +02:00
7febe6d1f9 fix(blog): typos in posts 2024-04-13 19:03:18 +02:00
c4650c34d9 build(deps): update latest 2024-04-13 18:54:36 +02:00
0eb780485c fix(footer): show 0.0.0-development version in Footer in development 2024-04-06 20:40:25 +02:00
cd5e92b64a fix: hydratation error with age calculation 2024-04-06 20:32:09 +02:00
982b148329 Revert "fix(portfolio): update link to Carolo (carolo.org)"
This reverts commit c2c9b59c7a.
2024-04-06 20:27:04 +02:00
0febee5b51 refactor: rename to primary color 2024-04-06 20:25:02 +02:00
3502f51735 chore(release): 3.2.3 [skip ci] 2024-02-15 08:41:01 +00:00
493df4e2f2 style: fix prettier 2024-02-15 09:35:58 +01:00
c2c9b59c7a fix(portfolio): update link to Carolo (carolo.org) 2024-02-15 09:34:02 +01:00
f6e3008ab9 fix(blog): add command to commit in the past in Git Ultimate Guide 2024-02-15 09:30:34 +01:00
15e94cec64 fix: update dependencies to latest to address security issues Node.js v20.11.1
Ref: https://nodejs.org/en/blog/vulnerability/february-2024-security-releases
2024-02-15 09:27:03 +01:00
5185c6758b chore(release): 3.2.2 [skip ci] 2024-02-02 16:31:35 +00:00
b633eef833 fix: remove npm vulnerability by updating html-w3c-validator 2024-02-02 17:30:25 +01:00
d2e627ff13 chore: cleaner configs 2024-01-29 21:26:59 +01:00
1e0567b538 chore(release): 3.2.1 [skip ci] 2024-01-28 15:15:48 +00:00
c8d32c6acc test: correct selector for Main Title of page 2024-01-28 16:13:02 +01:00
05503cda26 chore: only report errors for html validation 2024-01-28 16:04:00 +01:00
303b6f3011 fix: correct responsive for Header Title 2024-01-28 15:45:45 +01:00
0272cf7080 docs(license): add email address 2024-01-28 15:42:23 +01:00
e8ea42a260 fix: wrong font weight on hover link 2024-01-28 15:42:01 +01:00
f337e14260 chore(release): 3.2.0 [skip ci] 2024-01-28 12:16:11 +00:00
f5020cad19 chore: usage of eslint-plugin-tailwindcss 2024-01-28 03:21:11 +01:00
b8ceefb2f6 feat: new logo v1 2024-01-28 01:56:47 +01:00
1523c8cac0 fix: wording typos 2024-01-25 14:46:03 +01:00
548ddc8425 style: format JSONC files with Prettier correctly 2024-01-24 21:49:30 +01:00
bac65ad61a fix: improve wording 2024-01-23 23:59:10 +01:00
b91f3165b7 fix(blog): add depreciation notice on Thream post 2024-01-23 22:29:21 +01:00
5478e202a7 fix(open source): replace nrwl/nx by DefinitelyTyped 2024-01-23 22:13:39 +01:00
a89b5932c2 fix: update dependencies to latest 2024-01-23 22:01:50 +01:00
339e42acfa chore(release): 3.1.2 [skip ci] 2023-12-28 05:24:14 +00:00
c123815a86 fix(portfolio): remove Thream project as it is now deprecated 2023-12-28 06:21:32 +01:00
dd26a277a2 fix: update dependencies to latest 2023-12-28 06:21:24 +01:00
80 changed files with 7021 additions and 5970 deletions

View File

@ -1,4 +1,21 @@
build **/.turbo
.next **/.next
coverage **/out
node_modules **/build
**/coverage
**/node_modules
# envs
.env
.env.production
.env.development
secrets
# misc
.DS_Store
*.pem
Dockerfile
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@ -1,4 +1,4 @@
# For more information see: https://editorconfig.org/ # https://editorconfig.org/
root = true root = true

View File

@ -1,11 +1,34 @@
{ {
"extends": ["conventions", "next/core-web-vitals", "prettier"], "root": true,
"extends": [
"conventions",
"next/core-web-vitals",
"plugin:tailwindcss/recommended",
"prettier"
],
"plugins": ["prettier"], "plugins": ["prettier"],
"parserOptions": { "parserOptions": {
"project": "./tsconfig.json" "project": "./tsconfig.json"
}, },
"settings": {
"tailwindcss": {
"callees": ["classNames"]
},
"react": {
"version": "detect"
}
},
"rules": { "rules": {
"prettier/prettier": "error" "prettier/prettier": "error",
"react/self-closing-comp": [
"error",
{
"component": true,
"html": true
}
],
"react/void-dom-elements-no-children": "error",
"react/jsx-boolean-value": "error"
}, },
"overrides": [ "overrides": [
{ {

View File

@ -13,7 +13,7 @@ jobs:
- uses: "actions/checkout@v4.1.1" - uses: "actions/checkout@v4.1.1"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.0.0" uses: "actions/setup-node@v4.0.2"
with: with:
node-version: "20.x" node-version: "20.x"
cache: "npm" cache: "npm"

View File

@ -13,7 +13,7 @@ jobs:
- uses: "actions/checkout@v4.1.1" - uses: "actions/checkout@v4.1.1"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.0.0" uses: "actions/setup-node@v4.0.2"
with: with:
node-version: "20.x" node-version: "20.x"
cache: "npm" cache: "npm"

View File

@ -21,7 +21,7 @@ jobs:
git_commit_gpgsign: true git_commit_gpgsign: true
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.0.0" uses: "actions/setup-node@v4.0.2"
with: with:
node-version: "20.x" node-version: "20.x"
cache: "npm" cache: "npm"

View File

@ -13,7 +13,7 @@ jobs:
- uses: "actions/checkout@v4.1.1" - uses: "actions/checkout@v4.1.1"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.0.0" uses: "actions/setup-node@v4.0.2"
with: with:
node-version: "20.x" node-version: "20.x"
cache: "npm" cache: "npm"
@ -30,7 +30,7 @@ jobs:
- uses: "actions/checkout@v4.1.1" - uses: "actions/checkout@v4.1.1"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.0.0" uses: "actions/setup-node@v4.0.2"
with: with:
node-version: "20.x" node-version: "20.x"
cache: "npm" cache: "npm"

View File

@ -8,13 +8,3 @@ tasks:
ports: ports:
- port: 3000 - port: 3000
onOpen: "open-preview" onOpen: "open-preview"
github:
prebuilds:
master: true
branches: true
pullRequests: true
pullRequestsFromForks: true
addComment: true
addBadge: true
addLabel: true

View File

@ -1,4 +1,6 @@
{ {
"$schema": "https://raw.githubusercontent.com/theoludwig/html-w3c-validator/master/schema/html-w3c-validatorrc-schema.json",
"urls": ["http://127.0.0.1:3000/", "http://127.0.0.1:3000/blog"], "urls": ["http://127.0.0.1:3000/", "http://127.0.0.1:3000/blog"],
"files": ["./public/curriculum-vitae/index.html"] "files": ["./public/curriculum-vitae/index.html"],
"severities": ["error"]
} }

View File

@ -1,4 +1,3 @@
#!/bin/sh #!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"
npm run lint:commit -- --edit npm run lint:commit -- --edit

View File

@ -1,4 +1,3 @@
#!/bin/sh #!/usr/bin/env sh
. "$(dirname "$0")/_/husky.sh"
npm run lint:staged npm run lint:staged

View File

@ -1,6 +1,7 @@
{ {
"*": ["editorconfig-checker"], "**/*": ["editorconfig-checker", "prettier --write --ignore-unknown"],
"*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --fix"], "**/*.md": ["markdownlint-cli2 --fix --no-globs"],
"*.{css,scss,sass,json,jsonc,yml,yaml}": ["prettier --write"], "**/*.{js,jsx,ts,tsx}": [
"*.{md,mdx}": ["prettier --write", "markdownlint-cli2 --fix"] "eslint --fix --max-warnings 0 --report-unused-disable-directives"
]
} }

View File

@ -1,12 +1,12 @@
{ {
"config": { "config": {
"extends": "markdownlint/style/prettier",
"default": true, "default": true,
"relative-links": true, "relative-links": true,
"extends": "markdownlint/style/prettier", "no-duplicate-heading": false,
"MD024": false, "no-inline-html": false,
"MD033": false
}, },
"globs": ["**/*.{md,mdx}"], "globs": ["**/*.md"],
"ignores": ["**/node_modules"], "ignores": ["**/node_modules"],
"customRules": ["markdownlint-rule-relative-links"] "customRules": ["markdownlint-rule-relative-links"],
} }

View File

@ -1,3 +1,4 @@
{ {
"semi": false "semi": false,
"plugins": ["prettier-plugin-tailwindcss"]
} }

View File

@ -5,7 +5,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode", "editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll": true "source.fixAll": "explicit"
}, },
"eslint.options": { "eslint.options": {
"ignorePath": ".gitignore" "ignorePath": ".gitignore"

View File

@ -1,23 +1,28 @@
FROM node:20.9.0 AS builder-dependencies FROM node:20.12.2 AS builder-dependencies
WORKDIR /usr/src/application WORKDIR /usr/src/application
COPY ./package*.json ./ COPY ./package*.json ./
RUN npm clean-install RUN npm clean-install
FROM node:20.9.0 AS builder FROM node:20.12.2 AS builder
ENV NEXT_TELEMETRY_DISABLED=1
ENV IS_STANDALONE=true
WORKDIR /usr/src/application WORKDIR /usr/src/application
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
COPY ./ ./ COPY ./ ./
RUN npm run build RUN npm run build
FROM gcr.io/distroless/nodejs20-debian12:latest AS runner FROM node:20.12.2-slim AS runner
WORKDIR /usr/src/application
ENV NODE_ENV=production ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0 ENV HOSTNAME=0.0.0.0
ENV NEXT_TELEMETRY_DISABLED=1 ENV NEXT_TELEMETRY_DISABLED=1
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules ENV IS_STANDALONE=true
COPY --from=builder /usr/src/application/.next/standalone ./ WORKDIR /usr/src/application
COPY --from=builder /usr/src/application/.next/static ./.next/static RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 applicationrunner
COPY --from=builder /usr/src/application/public ./public USER applicationrunner
COPY --from=builder /usr/src/application/i18n/translations ./i18n/translations COPY --from=builder-dependencies --chown=applicationrunner:nodejs /usr/src/application/node_modules ./node_modules
COPY --from=builder /usr/src/application/next.config.js ./next.config.js COPY --from=builder --chown=applicationrunner:nodejs /usr/src/application/.next/standalone ./
COPY --from=builder --chown=applicationrunner:nodejs /usr/src/application/.next/static ./.next/static
COPY --from=builder --chown=applicationrunner:nodejs /usr/src/application/public ./public
COPY --from=builder --chown=applicationrunner:nodejs /usr/src/application/i18n/translations ./i18n/translations
COPY --from=builder --chown=applicationrunner:nodejs /usr/src/application/next.config.js ./next.config.js
CMD ["./server.js"] CMD ["./server.js"]

View File

@ -1,6 +1,6 @@
MIT License # MIT License
Copyright (c) Théo LUDWIG Copyright (c) Théo LUDWIG <contact@theoludwig.fr>
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,7 +1,7 @@
<h1 align="center"><a href="https://theoludwig.fr/">Théo LUDWIG</a></h1> <h1 align="center"><a href="https://theoludwig.fr/">Théo LUDWIG</a></h1>
<p align="center"> <p align="center">
<strong>Developer Full Stack • Open-Source enthusiast</strong> <strong>Developer Full Stack • Open-Source Enthusiast</strong>
</p> </p>
<p align="center"> <p align="center">
@ -25,10 +25,10 @@
"pronouns": "He/Him", "pronouns": "He/Him",
"birthDate": "31/03/2003", "birthDate": "31/03/2003",
"nationality": "Alsace, France", "nationality": "Alsace, France",
"interests": ["Developer Full Stack", "Open-Source enthusiast"], "interests": ["Developer Full Stack", "Open-Source Enthusiast"],
"skills": { "skills": {
"programmingLanguages": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"], "programmingLanguages": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"],
"frontend": ["HTML", "CSS", "Tailwind CSS", "React.js/Next.js"], "frontend": ["HTML/CSS", "Tailwind CSS", "React.js/Next.js"],
"backend": ["Laravel", "Node.js", "Fastify", "PostgreSQL"], "backend": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
"tools": ["GNU/Linux", "Arch Linux", "Visual Studio Code", "Git", "Docker"] "tools": ["GNU/Linux", "Arch Linux", "Visual Studio Code", "Git", "Docker"]
} }
@ -40,6 +40,6 @@
## 📈 Statistics ## 📈 Statistics
<p align=center> <p align=center>
<img height=175 align="center" src="https://github-readme-stats.vercel.app/api?username=theoludwig&show_icons=true&theme=dark" /> <img height=175 align="center" src="https://github-readme-stats.vercel.app/api?username=theoludwig&show_icons=true&theme=dark" alt="Théo LUDWIG's GitHub Stats" />
<img height=175 align="center" src="https://github-readme-stats.vercel.app/api/top-langs/?username=theoludwig&hide=html,css,javascript&langs_count=8&layout=compact&theme=dark" /> <img height=175 align="center" src="https://github-readme-stats.vercel.app/api/top-langs/?username=theoludwig&hide=html,css,javascript&langs_count=8&layout=compact&theme=dark" alt="Théo LUDWIG's Programming Languages" />
</p> </p>

View File

@ -2,7 +2,7 @@ import { Loader } from "@/components/design/Loader"
const Loading = (): JSX.Element => { const Loading = (): JSX.Element => {
return ( return (
<main className="flex flex-col flex-1 items-center justify-center"> <main className="flex flex-1 flex-col items-center justify-center">
<Loader /> <Loader />
</main> </main>
) )

View File

@ -2,7 +2,7 @@ import { Loader } from "@/components/design/Loader"
const Loading = (): JSX.Element => { const Loading = (): JSX.Element => {
return ( return (
<main className="flex flex-col flex-1 items-center justify-center"> <main className="flex flex-1 flex-col items-center justify-center">
<Loader /> <Loader />
</main> </main>
) )

View File

@ -25,7 +25,9 @@ const BlogPage = async (): Promise<JSX.Element> => {
return ( return (
<main className="flex flex-1 flex-col flex-wrap items-center"> <main className="flex flex-1 flex-col flex-wrap items-center">
<div className="mt-10 flex flex-col items-center"> <div className="mt-10 flex flex-col items-center">
<h1 className="text-4xl font-semibold">Blog</h1> <h1 className="text-4xl font-semibold text-primary dark:text-primary-dark">
Blog
</h1>
<p className="mt-6 text-center" data-cy="blog-post-date"> <p className="mt-6 text-center" data-cy="blog-post-date">
{description} {description}
</p> </p>

View File

@ -14,11 +14,11 @@ const ErrorHandling = (props: ErrorHandlingProps): JSX.Element => {
}, [error]) }, [error])
return ( return (
<main className="flex flex-col flex-1 items-center justify-center"> <main className="flex flex-1 flex-col items-center justify-center">
<h1 className="my-6 text-4xl font-semibold"> <h1 className="my-6 text-4xl font-semibold">
Error{" "} Error{" "}
<span <span
className="text-yellow dark:text-yellow-dark" className="text-primary dark:text-primary-dark"
data-cy="status-code" data-cy="status-code"
> >
500 500

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -7,6 +7,10 @@
word-break: break-word; word-break: break-word;
} }
.text-base {
line-height: 1.75rem;
}
.prose { .prose {
@apply !max-w-5xl scroll-smooth text-gray dark:text-gray-300; @apply !max-w-5xl scroll-smooth text-gray dark:text-gray-300;
} }
@ -25,7 +29,12 @@
.prose a, .prose a,
.prose strong { .prose strong {
@apply text-yellow dark:text-yellow-dark; @apply !font-semibold text-primary dark:text-primary-dark;
}
strong,
b {
@apply font-bold;
} }
.prose h2, .prose h2,

View File

@ -12,8 +12,8 @@ import { getTheme } from "@/theme/theme.server"
const title = "Théo LUDWIG" const title = "Théo LUDWIG"
const description = const description =
"Théo LUDWIG - Developer Full Stack • Open-Source enthusiast" "Théo LUDWIG - Developer Full Stack • Open-Source Enthusiast"
const image = "/images/icon-96x96.png" const image = "/images/logo.png"
const url = new URL("https://theoludwig.fr") const url = new URL("https://theoludwig.fr")
const locale = "fr-FR, en-US" const locale = "fr-FR, en-US"
@ -36,9 +36,6 @@ export const metadata: Metadata = {
locale, locale,
type: "website", type: "website",
}, },
icons: {
icon: "/images/icon-96x96.png",
},
twitter: { twitter: {
card: "summary", card: "summary",
title, title,
@ -68,7 +65,7 @@ const RootLayout = (props: RootLayoutProps): JSX.Element => {
colorScheme: theme, colorScheme: theme,
}} }}
> >
<body className="bg-white font-headline text-black dark:bg-black dark:text-white flex flex-col min-h-screen"> <body className="flex min-h-screen flex-col bg-white font-headline text-black dark:bg-black dark:text-white">
<Header /> <Header />
{children} {children}
<Footer /> <Footer />

View File

@ -2,7 +2,7 @@ import { Loader } from "@/components/design/Loader"
const Loading = (): JSX.Element => { const Loading = (): JSX.Element => {
return ( return (
<main className="flex flex-col flex-1 items-center justify-center"> <main className="flex flex-1 flex-col items-center justify-center">
<Loader /> <Loader />
</main> </main>
) )

View File

@ -6,11 +6,11 @@ const NotFound = (): JSX.Element => {
const i18n = getI18n() const i18n = getI18n()
return ( return (
<main className="flex flex-col flex-1 items-center justify-center"> <main className="flex flex-1 flex-col items-center justify-center">
<h1 className="my-6 text-4xl font-semibold"> <h1 className="my-6 text-4xl font-semibold">
{i18n.translate("errors.error")}{" "} {i18n.translate("errors.error")}{" "}
<span <span
className="text-yellow dark:text-yellow-dark" className="text-primary dark:text-primary-dark"
data-cy="status-code" data-cy="status-code"
> >
404 404
@ -20,7 +20,7 @@ const NotFound = (): JSX.Element => {
{i18n.translate("errors.not-found")}{" "} {i18n.translate("errors.not-found")}{" "}
<Link <Link
href="/" href="/"
className="text-yellow hover:underline dark:text-yellow-dark" className="text-primary hover:underline dark:text-primary-dark"
> >
{i18n.translate("errors.return-to-home-page")} {i18n.translate("errors.return-to-home-page")}
</Link> </Link>

View File

@ -21,7 +21,9 @@ export const BlogPost = async (props: BlogPostProps): Promise<JSX.Element> => {
return ( return (
<main className="break-wrap-words flex flex-1 flex-col flex-wrap items-center justify-center"> <main className="break-wrap-words flex flex-1 flex-col flex-wrap items-center justify-center">
<div className="my-10 flex flex-col items-center text-center"> <div className="my-10 flex flex-col items-center text-center">
<h1 className="text-3xl font-semibold">{blogPost.frontmatter.title}</h1> <h1 className="text-3xl font-semibold text-primary dark:text-primary-dark">
{blogPost.frontmatter.title}
</h1>
<p className="mt-2" data-cy="blog-post-date"> <p className="mt-2" data-cy="blog-post-date">
{date.format( {date.format(
new Date(blogPost.frontmatter.publishedOn), new Date(blogPost.frontmatter.publishedOn),

View File

@ -31,7 +31,7 @@ const Heading = (
href={`#${id}`} href={`#${id}`}
className="invisible !text-black group-hover:visible dark:!text-white" className="invisible !text-black group-hover:visible dark:!text-white"
> >
<FontAwesomeIcon className="mr-2 inline h-4 w-4" icon={faLink} /> <FontAwesomeIcon className="mr-2 inline size-4" icon={faLink} />
</Link> </Link>
{children} {children}
</h2> </h2>
@ -90,16 +90,26 @@ export const BlogPostContent = async (
alt={alt} alt={alt}
width={1000} width={1000}
height={1000} height={1000}
className="h-auto w-auto" className="size-auto"
/> />
</span> </span>
) )
}, },
a: (props) => { a: (props) => {
const { href = "" } = props const { href = "", ...rest } = props
if (href.startsWith("#")) { if (href.startsWith("#")) {
return <a {...props} /> return <a {...props} />
} }
if (href.startsWith("../posts/")) {
return (
<a
href={href
.replace("../posts/", "/blog/")
.replace(".md", "")}
{...rest}
/>
)
}
return <a target="_blank" rel="noopener noreferrer" {...props} /> return <a target="_blank" rel="noopener noreferrer" {...props} />
}, },
}} }}

View File

@ -23,7 +23,10 @@ export const BlogPosts = async (): Promise<JSX.Element> => {
data-cy={post.slug} data-cy={post.slug}
> >
<ShadowContainer className="cursor-pointer p-6 transition duration-200 ease-in-out hover:-translate-y-2"> <ShadowContainer className="cursor-pointer p-6 transition duration-200 ease-in-out hover:-translate-y-2">
<h2 data-cy="blog-post-title" className="text-xl font-semibold"> <h2
data-cy="blog-post-title"
className="text-xl font-semibold text-primary dark:text-primary-dark"
>
{post.frontmatter.title} {post.frontmatter.title}
</h2> </h2>
<p data-cy="blog-post-date" className="mt-2"> <p data-cy="blog-post-date" className="mt-2">

View File

@ -19,13 +19,13 @@ A clean code is a code that is **easy** to **read** and easy to **understand**.
But I promise it is not a code that is easy to write, in fact it is really **hard to write Clean Code**. But I promise it is not a code that is easy to write, in fact it is really **hard to write Clean Code**.
We could ask ourselves, what is **easy** to **read** and easy to **understand** ? We could ask ourselves, what is **easy** to **read** and easy to **understand**?
It depends of many factors, and is somewhat relative to each one of us. The **perfect** Clean code **doesn't exist**, but we can try to be **as perfect as possible**. It depends of many factors, and is somewhat relative to each one of us. The **perfect** Clean code **doesn't exist**, but we can try to be **as perfect as possible**.
## Why is it so important? ## Why is it so important?
Code like that works great, but it is not enough, even if the code will be read by the computer and understood by the machine, we should not forget that the code is **written by human** and will be also **read by human** not only a machine. Code that works is great, but not enough, even if the code will be read and understood by the computer, we should not forget that the code is **written by human** and will be also **read by human** not only a machine.
For example the [Linux kernel](https://www.kernel.org/), is one of the biggest open source project with many contributors worldwide. Last data shows that it is about **20 millions** lines of code. For example the [Linux kernel](https://www.kernel.org/), is one of the biggest open source project with many contributors worldwide. Last data shows that it is about **20 millions** lines of code.

View File

@ -84,7 +84,10 @@ git add .
git add <file> git add <file>
# Commit changes # Commit changes
git commit -m "chore: initial commit" git commit -m "Commit message"
# Commit changes in the past
git commit --date "10 day ago" -m "Commit message"
# Add remote repository # Add remote repository
git remote add <remote> <url> git remote add <remote> <url>

View File

@ -41,13 +41,13 @@ Find the right balance, between abstraction and simple implementation, start sim
When you start a new project, you should focus on the core of the project, not on the details, to release as soon as possible, a working usable version of your project also called a [**Minimum Viable Product** (MVP)](https://en.wikipedia.org/wiki/Minimum_viable_product), it is better than a half-functioning, over-engineered project. When you start a new project, you should focus on the core of the project, not on the details, to release as soon as possible, a working usable version of your project also called a [**Minimum Viable Product** (MVP)](https://en.wikipedia.org/wiki/Minimum_viable_product), it is better than a half-functioning, over-engineered project.
I made this mistake while developing [Thream](https://thream.theoludwig.fr), your **open source** platform to stay close with your friends and communities, **talk**, chat, **collaborate**, share and **have fun**. I made this mistake while developing [Thream](../posts/thream-v1-0-0.md), your **open source** platform to stay close with your friends and communities, **talk**, chat, **collaborate**, share and **have fun**.
Basically, I thought it was cool, to do a "big" v1.0.0 release with a lot of features, but in fact, it was not, because I could not even show what I was developing (to the end-users, not technical people) as I was making multiple features at the same time and also mainly focused on the **REST API** side and not at all the **website (frontend)**. Basically, I thought it was cool, to do a "big" v1.0.0 release with a lot of features, but in fact, it was not, because I could not even show what I was developing (to the end-users, not technical people) as I was making multiple features at the same time and also mainly focused on the **REST API** side and not at all the **website (frontend)**.
What I recommend you to do is to start with a **v1.0.0** release as soon as possible with the minimum required features needed for your project idea, and then gradually add new features and release new versions. What I recommend you to do is to start with a **v1.0.0** release as soon as possible with the minimum required features needed for your project idea, and then gradually add new features and release new versions.
In my example for [Thream](https://thream.theoludwig.fr), I could release a v1.0.0 without these features: In my example for [Thream](../posts/thream-v1-0-0.md), I could release a v1.0.0 without these features:
- English/French translation (could be only English) - English/French translation (could be only English)
- Light/Dark theme (could be only Dark) - Light/Dark theme (could be only Dark)
@ -55,7 +55,7 @@ In my example for [Thream](https://thream.theoludwig.fr), I could release a v1.0
- User public profile - User public profile
- Channels (maybe could be only one channel per guild to start with) - Channels (maybe could be only one channel per guild to start with)
And probably more, what was really required with [Thream](https://thream.theoludwig.fr), is that users could authenticate, create a community of friends, and then they could communicate with each other with messages in real-time, really that was enough. And probably more, what was really required with [Thream](../posts/thream-v1-0-0.md), is that users could authenticate, create a community of friends, and then they could communicate with each other with messages in real-time, really that was enough.
And then with this basis, I could release, v1.1.0, v1.2.0 etc. with more features, and release new versions more often to show the progress of the project, it is also more motivating to have users testing our project and to **get feedback sooner**. And then with this basis, I could release, v1.1.0, v1.2.0 etc. with more features, and release new versions more often to show the progress of the project, it is also more motivating to have users testing our project and to **get feedback sooner**.

View File

@ -15,7 +15,7 @@ We don't want to "reinvent the wheel" and rewrite everything from scratch for ea
However, it is important to draw a line between what dependencies are worth the cost and which are not. However, it is important to draw a line between what dependencies are worth the cost and which are not.
Most likely adding a [JavaScript npm package `is-odd`](https://www.npmjs.com/package/is-odd) to check if a number is odd or even for example, is not worth it. Writing it ourselves is easier and allows a better maintenance in the long term. Most likely adding a [JavaScript npm package `is-odd`](https://www.npmjs.com/package/is-odd) to check if a number is odd or even for example, is not worth it. Writing it ourselves allows a better maintenance in the long term.
Learning **how to solve problems** and how to write efficient code is very important and also a very broad and complicated topic, so this blog post will only be an **introduction to the subject**, and will not go in depth. Learning **how to solve problems** and how to write efficient code is very important and also a very broad and complicated topic, so this blog post will only be an **introduction to the subject**, and will not go in depth.
@ -240,7 +240,7 @@ Here is a list of classes of functions that are commonly encountered when analyz
### Estimating efficiency ### Estimating efficiency
By checking the time complexity of an algorithm, it is possible to check before implementing the algorithm,that it is efficient enough for the problem. By checking the time complexity of an algorithm, it is possible to check before implementing the algorithm, that it is efficient enough for the problem.
Example: assume that the time limit for a problem is 1 second and the input size is $n = 10^5$. If the time complexity is $O(n^2)$, the algorithm will perform about $(10^5)^2 = 10^{10}$ operations. Example: assume that the time limit for a problem is 1 second and the input size is $n = 10^5$. If the time complexity is $O(n^2)$, the algorithm will perform about $(10^5)^2 = 10^{10}$ operations.
@ -286,7 +286,7 @@ Contiguous subarray is any sub series of elements in a given array that are cont
**Explanation:** The subarray with the largest sum is `[2, 4, -3, 5, 2]` which has a sum of `10`. **Explanation:** The subarray with the largest sum is `[2, 4, -3, 5, 2]` which has a sum of `10`.
### Worst solution: Brute force ### Worst solution: Brute force ($O(n^3)$)
```python ```python
def maximum_subarray_sum_cubic(array: list[int]) -> int: def maximum_subarray_sum_cubic(array: list[int]) -> int:
@ -309,7 +309,7 @@ def maximum_subarray_sum_cubic(array: list[int]) -> int:
return best_sum return best_sum
``` ```
### Better solution: Linear time ### Better solution: Linear time ($O(n)$)
```python ```python
def maximum_subarray_sum_linear(array: list[int]) -> int: def maximum_subarray_sum_linear(array: list[int]) -> int:

View File

@ -5,15 +5,25 @@ isPublished: true
publishedOn: "2022-04-11T10:24:55.206Z" publishedOn: "2022-04-11T10:24:55.206Z"
--- ---
⚠️ **Thream** is **not maintained anymore**, and is no longer accessible on ~~[thream.theoludwig.fr](https://thream.theoludwig.fr)`~~.
While the project taught me a lot, it had too much ambitions for new features, with nearly no users, and no contributors.
You can still use the code as you wish and fork it to maintain it yourself, as the code is completely open source on [GitHub](https://github.com/Thream).
This blog post is still available to explain the project, and how it was implemented.
---
Hello! 👋 Hello! 👋
After months of hard work, [Thream v1.0.0](https://thream.theoludwig.fr/) has been released! 🎉 After months of hard work, [Thream v1.0.0](https://github.com/Thream) has been released! 🎉
[**Thream**](https://thream.theoludwig.fr/) is your open-source platform to stay close with your friends and communities, talk, chat, collaborate, share and have fun. [**Thream**](https://github.com/Thream) is your open-source platform to stay close with your friends and communities, talk, chat, collaborate, share and have fun.
## Presentation ## Presentation
[**Thream**](https://thream.theoludwig.fr/) is a social network to stay close with your friends and communities to talk, chat, collaborate and share. [**Thream**](https://github.com/Thream) is a social network to stay close with your friends and communities to talk, chat, collaborate and share.
The project is largely inspired by [Discord](https://discord.com), a proprietary instant messaging service, but differentiates itself by its **non-profit open source philosophy** and will integrate special features. The project is largely inspired by [Discord](https://discord.com), a proprietary instant messaging service, but differentiates itself by its **non-profit open source philosophy** and will integrate special features.
@ -23,7 +33,7 @@ The idea is that a user can create an account to authenticate with an email addr
![The Thream app on a community page](../../public/images/posts/thream-v1-0-0/thream-ui.png) ![The Thream app on a community page](../../public/images/posts/thream-v1-0-0/thream-ui.png)
[**Thream**](https://thream.theoludwig.fr/) is a website that works on any recent browser, accessible on [thream.theoludwig.fr](https://thream.theoludwig.fr/). [**Thream**](https://github.com/Thream) is a website that works on any recent browser.
## History ## History
@ -115,5 +125,3 @@ The other interest of the project is that it is completely **open-source**, and
**Thream** is **non-profit** and therefore has no financial goal, deadline or specific feature target, which makes the design of the project a hobby and a way to learn new concepts. **Thream** is **non-profit** and therefore has no financial goal, deadline or specific feature target, which makes the design of the project a hobby and a way to learn new concepts.
Feel free to give feebacks and suggestions to improve the project, and to report any bug you find. Feel free to give feebacks and suggestions to improve the project, and to report any bug you find.
**Thream** is available: [**thream.theoludwig.fr**](https://thream.theoludwig.fr/).

View File

@ -9,7 +9,7 @@ export const FooterText = (): JSX.Element => {
<p> <p>
<Link <Link
href="/" href="/"
className="text-yellow hover:underline dark:text-yellow-dark" className="font-semibold text-primary hover:underline dark:text-primary-dark"
> >
Théo LUDWIG Théo LUDWIG
</Link>{" "} </Link>{" "}

View File

@ -16,7 +16,7 @@ export const FooterVersion = (props: FooterVersionProps): JSX.Element => {
Version{" "} Version{" "}
<a <a
data-cy="version-link" data-cy="version-link"
className="text-yellow hover:underline dark:text-yellow-dark" className="font-semibold text-primary hover:underline dark:text-primary-dark"
href={versionLink} href={versionLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"

View File

@ -1,9 +1,10 @@
import { getVersion } from "@/utils/getVersion"
import { FooterText } from "./FooterText" import { FooterText } from "./FooterText"
import { FooterVersion } from "./FooterVersion" import { FooterVersion } from "./FooterVersion"
export const Footer = async (): Promise<JSX.Element> => { export const Footer = async (): Promise<JSX.Element> => {
const { readPackage } = await import("read-pkg") const version = await getVersion()
const { version } = await readPackage()
return ( return (
<footer className="flex flex-col items-center justify-center border-t-2 border-gray-600 bg-white py-6 text-lg dark:border-gray-400 dark:bg-black"> <footer className="flex flex-col items-center justify-center border-t-2 border-gray-600 bg-white py-6 text-lg dark:border-gray-400 dark:bg-black">

View File

@ -82,7 +82,7 @@ export const Locales = (props: LocalesProps): JSX.Element => {
return ( return (
<li <li
key={locale} key={locale}
className="flex h-12 w-full items-center justify-center hover:bg-[#4f545c] hover:bg-opacity-20" className="flex h-12 w-full items-center justify-center hover:bg-[#4f545c]/20"
onClick={async () => { onClick={async () => {
return await handleLocale(locale) return await handleLocale(locale)
}} }}

View File

@ -30,35 +30,35 @@ export const SwitchTheme = (props: SwitchThemeProps): JSX.Element => {
<div <div
data-cy="switch-theme-dark" data-cy="switch-theme-dark"
className={classNames( className={classNames(
"absolute bottom-0 left-[8px] top-0 mb-auto mt-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out", "absolute inset-y-0 left-[8px] my-auto h-[10px] w-[14px] leading-[0] transition-opacity duration-[250ms] ease-in-out",
{ {
"opacity-100": theme === "dark", "opacity-100": theme === "dark",
"opacity-0": theme === "light", "opacity-0": theme === "light",
}, },
)} )}
> >
<span className="relative flex h-[10px] w-[10px] items-center justify-center"> <span className="relative flex size-[10px] items-center justify-center">
🌜 🌜
</span> </span>
</div> </div>
<div <div
data-cy="switch-theme-light" data-cy="switch-theme-light"
className={classNames( className={classNames(
"absolute bottom-0 right-[10px] top-0 mb-auto mt-auto h-[10px] w-[10px] leading-[0]", "absolute inset-y-0 right-[10px] my-auto size-[10px] leading-[0]",
{ {
"opacity-100": theme === "light", "opacity-100": theme === "light",
"opacity-0": theme === "dark", "opacity-0": theme === "dark",
}, },
)} )}
> >
<span className="relative flex h-[10px] w-[10px] items-center justify-center"> <span className="relative flex size-[10px] items-center justify-center">
🌞 🌞
</span> </span>
</div> </div>
</div> </div>
<div <div
className={classNames( className={classNames(
"absolute top-[1px] box-border h-[22px] w-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out", "absolute top-[1px] box-border size-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out",
{ {
"left-[27px]": theme === "dark", "left-[27px]": theme === "dark",
"left-0": theme === "light", "left-0": theme === "light",
@ -70,7 +70,7 @@ export const SwitchTheme = (props: SwitchThemeProps): JSX.Element => {
data-cy="switch-theme-input" data-cy="switch-theme-input"
type="checkbox" type="checkbox"
aria-label="Dark mode toggle" aria-label="Dark mode toggle"
className="absolute m-[-1px] h-[1px] w-[1px] overflow-hidden border-0 p-0 hidden" className="absolute m-[-1px] hidden size-[1px] overflow-hidden border-0 p-0"
defaultChecked defaultChecked
/> />
</div> </div>

View File

@ -2,6 +2,7 @@ import { cookies } from "next/headers"
import Link from "next/link" import Link from "next/link"
import Image from "next/image" import Image from "next/image"
import Logo from "@/public/images/logo.png"
import { getI18n } from "@/i18n/i18n.server" import { getI18n } from "@/i18n/i18n.server"
import { Locales } from "./Locales" import { Locales } from "./Locales"
@ -14,26 +15,25 @@ export const Header = (): JSX.Element => {
return ( return (
<header className="sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black"> <header className="sticky top-0 z-50 flex w-full justify-between border-b-2 border-gray-600 bg-white px-6 py-2 dark:border-gray-400 dark:bg-black">
<Link href="/"> <Link href="/">
<div className="flex items-center justify-center"> <h1 className="flex items-center justify-center">
<Image <Image
quality={100} quality={100}
width={60} className="size-16"
height={60} src={Logo}
src="/images/icon_small.png"
alt="Théo LUDWIG" alt="Théo LUDWIG"
priority priority
/> />
<strong className="ml-1 hidden font-headline font-semibold text-yellow dark:text-yellow-dark xs:block"> <strong className="ml-1 hidden font-headline font-semibold text-primary dark:text-primary-dark sm:block sm:text-xl">
Théo LUDWIG Théo LUDWIG
</strong> </strong>
</div> </h1>
</Link> </Link>
<div className="flex justify-between"> <div className="flex justify-between">
<div className="flex flex-col items-center justify-center px-6"> <div className="flex flex-col items-center justify-center px-6">
<Link <Link
href="/blog" href="/blog"
data-cy="header-blog-link" data-cy="header-blog-link"
className="text-yellow hover:underline dark:text-yellow-dark" className="font-semibold text-primary hover:underline dark:text-primary-dark"
> >
Blog Blog
</Link> </Link>

View File

@ -1,8 +1,17 @@
import htmlParser from "html-react-parser" import htmlParser from "html-react-parser"
import { faCode, faMicrochip } from "@fortawesome/free-solid-svg-icons"
import { faGit } from "@fortawesome/free-brands-svg-icons"
export const InterestsIcons = {
code: faCode,
"open-source": faGit,
"high-tech": faMicrochip,
} as const
export interface InterestParagraphProps { export interface InterestParagraphProps {
title: string title: string
description: string description: string
id: keyof typeof InterestsIcons
} }
export const InterestParagraph = ( export const InterestParagraph = (
@ -13,7 +22,7 @@ export const InterestParagraph = (
return ( return (
<> <>
<p className="my-6 text-center text-gray dark:text-gray-dark"> <p className="my-6 text-center text-gray dark:text-gray-dark">
<strong className="text-lg font-semibold text-yellow dark:text-yellow-dark"> <strong className="text-lg font-semibold text-primary dark:text-primary-dark">
{title} {title}
</strong> </strong>
<br /> <br />

View File

@ -10,9 +10,9 @@ export const InterestItem = (props: InterestItemProps): JSX.Element => {
const { fontAwesomeIcon, title } = props const { fontAwesomeIcon, title } = props
return ( return (
<li className="interest-item mx-2 my-2 h-8 w-8" title={title}> <li className="m-2 size-8" title={title}>
<FontAwesomeIcon <FontAwesomeIcon
className="block h-full w-full text-yellow dark:text-yellow-dark" className="block size-full text-primary dark:text-primary-dark"
icon={fontAwesomeIcon} icon={fontAwesomeIcon}
/> />
</li> </li>

View File

@ -1,18 +1,28 @@
import { faCode, faMicrochip } from "@fortawesome/free-solid-svg-icons" import { getI18n } from "@/i18n/i18n.server"
import { faGit } from "@fortawesome/free-brands-svg-icons"
import {
InterestsIcons,
type InterestParagraphProps,
} from "../InterestParagraph"
import { InterestItem } from "./InterestItem" import { InterestItem } from "./InterestItem"
export const InterestsList = (): JSX.Element => { export const InterestsList = (): JSX.Element => {
const i18n = getI18n()
let paragraphs = i18n.translate<InterestParagraphProps[]>(
"home.interests.paragraphs",
)
if (!Array.isArray(paragraphs)) {
paragraphs = []
}
return ( return (
<div className="my-4 flex justify-center"> <div className="my-4 flex justify-center">
<ul className="m-0 flex w-96 list-none justify-around p-0"> <ul className="m-0 flex w-96 list-none justify-around p-0">
<InterestItem title="Developer Full Stack" fontAwesomeIcon={faCode} /> {paragraphs.map(({ title, id }) => {
<InterestItem const icon = InterestsIcons[id]
title="Passionate about High-Tech" return <InterestItem key={id} title={title} fontAwesomeIcon={icon} />
fontAwesomeIcon={faMicrochip} })}
/>
<InterestItem title="Open-Source enthusiast" fontAwesomeIcon={faGit} />
</ul> </ul>
</div> </div>
) )

View File

@ -15,7 +15,9 @@ export const Repository = (props: RepositoryProps): JSX.Element => {
<a href={href} target="_blank" rel="noopener noreferrer"> <a href={href} target="_blank" rel="noopener noreferrer">
<div className="flex"> <div className="flex">
<GitHubIcon className="mr-2 h-6" /> <GitHubIcon className="mr-2 h-6" />
<span className="text-yellow dark:text-yellow-dark">{name}</span> <span className="font-semibold text-primary dark:text-primary-dark">
{name}
</span>
</div> </div>
<p className="my-4">{description}</p> <p className="my-4">{description}</p>
</a> </a>

View File

@ -22,9 +22,9 @@ export const OpenSource = (): JSX.Element => {
href="https://github.com/standard/standard/commits?author=theoludwig" href="https://github.com/standard/standard/commits?author=theoludwig"
/> />
<Repository <Repository
name="nrwl/nx" name="DefinitelyTyped/DefinitelyTyped"
description="Smart, Fast and Extensible Build System" description="High quality TypeScript type definitions."
href="https://github.com/nrwl/nx/commits?author=theoludwig" href="https://github.com/DefinitelyTyped/DefinitelyTyped/commits?author=theoludwig"
/> />
<Repository <Repository
name="vercel/next.js" name="vercel/next.js"

View File

@ -24,7 +24,7 @@ export const PortfolioItem = (props: PortfolioItemProps): JSX.Element => {
<div className="flex justify-center"> <div className="flex justify-center">
<Image <Image
quality={100} quality={100}
className="h-auto w-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5" className="size-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5"
width={300} width={300}
height={300} height={300}
src={image} src={image}
@ -32,10 +32,10 @@ export const PortfolioItem = (props: PortfolioItemProps): JSX.Element => {
/> />
</div> </div>
<div className="absolute bottom-0 h-auto overflow-hidden text-center opacity-0 transition-opacity duration-500 group-hover:opacity-100"> <div className="absolute bottom-0 h-auto overflow-hidden text-center opacity-0 transition-opacity duration-500 group-hover:opacity-100">
<h3 className="my-6 text-xl font-semibold text-yellow dark:text-yellow-dark"> <h3 className="my-6 text-2xl font-semibold text-primary dark:text-primary-dark">
{title} {title}
</h3> </h3>
<p className="my-6">{description}</p> <p className="mx-4 my-6 font-semibold">{description}</p>
</div> </div>
</a> </a>
</ShadowContainer> </ShadowContainer>

View File

@ -1,23 +1,21 @@
import htmlParser from "html-react-parser"
import { getI18n } from "@/i18n/i18n.server" import { getI18n } from "@/i18n/i18n.server"
export const ProfileDescriptionBottom = (): JSX.Element => { export const ProfileDescriptionBottom = (): JSX.Element => {
const i18n = getI18n() const i18n = getI18n()
return ( return (
<p className="mb-8 mt-8 text-base font-normal text-gray dark:text-gray-dark"> <div className="my-6 max-w-md text-center text-base text-gray dark:text-gray-dark">
{i18n.translate("home.about.description-bottom")} <p>{htmlParser(i18n.translate("home.about.description-bottom"))}</p>
{i18n.locale === "fr-FR" ? (
<> <br />
<br /> <a
<br /> href="/curriculum-vitae/index.html"
<a className="font-semibold text-primary hover:underline dark:text-primary-dark"
href="/curriculum-vitae/index.html" >
className="text-yellow hover:underline dark:text-yellow-dark" Curriculum vitæ ({i18n.translate("common.fr-FR")})
> </a>
Curriculum vitæ </div>
</a>
</>
) : null}
</p>
) )
} }

View File

@ -5,7 +5,7 @@ export const ProfileInformation = (): JSX.Element => {
return ( return (
<div className="mb-6 border-b-2 border-gray-600 pb-2 font-headline dark:border-gray-400"> <div className="mb-6 border-b-2 border-gray-600 pb-2 font-headline dark:border-gray-400">
<h1 className="mb-2 text-4xl font-semibold text-yellow dark:text-yellow-dark"> <h1 className="mb-2 text-4xl font-semibold text-primary dark:text-primary-dark">
Théo LUDWIG Théo LUDWIG
</h1> </h1>
<h2 className="mb-3 text-base"> <h2 className="mb-3 text-base">

View File

@ -5,6 +5,7 @@ import { useMemo } from "react"
import { useI18n } from "@/i18n/i18n.client" import { useI18n } from "@/i18n/i18n.client"
import { BIRTH_DATE, BIRTH_DATE_STRING, getAge } from "@/utils/getAge" import { BIRTH_DATE, BIRTH_DATE_STRING, getAge } from "@/utils/getAge"
import type { CookiesStore } from "@/utils/constants" import type { CookiesStore } from "@/utils/constants"
import { useIsMounted } from "@/hooks/useIsMounted"
import { ProfileItem } from "./ProfileItem" import { ProfileItem } from "./ProfileItem"
@ -21,18 +22,22 @@ export const ProfileList = (props: ProfileListProps): JSX.Element => {
return getAge(BIRTH_DATE) return getAge(BIRTH_DATE)
}, []) }, [])
const { isMounted } = useIsMounted()
return ( return (
<ul className="m-0 list-none p-0"> <ul className="m-0 list-none p-0">
<ProfileItem <ProfileItem
title={i18n.translate("home.about.pronouns")} title={i18n.translate("home.about.pronouns")}
value={i18n.translate("home.about.pronouns-value")} value={i18n.translate("home.about.pronouns-value")}
/> />
<ProfileItem {isMounted ? (
title={i18n.translate("home.about.birth-date")} <ProfileItem
value={`${BIRTH_DATE_STRING} (${age} ${i18n.translate( title={i18n.translate("home.about.birth-date")}
"home.about.years-old", value={`${BIRTH_DATE_STRING} (${age} ${i18n.translate(
)})`} "home.about.years-old",
/> )})`}
/>
) : null}
<ProfileItem <ProfileItem
title={i18n.translate("home.about.nationality")} title={i18n.translate("home.about.nationality")}
value="Alsace, France" value="Alsace, France"

View File

@ -1,6 +1,6 @@
import Image from "next/image" import Image from "next/image"
import Logo from "public/images/logo.png" import Logo from "@/public/images/logo.png"
export const ProfileLogo = (): JSX.Element => { export const ProfileLogo = (): JSX.Element => {
return ( return (

View File

@ -8,7 +8,7 @@ export const Icon = (props: React.SVGProps<SVGSVGElement>): JSX.Element => {
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
className={classNames( className={classNames(
"h-8 w-8 fill-current text-black dark:text-white", "size-8 fill-current text-black dark:text-white",
className, className,
)} )}
{...rest} {...rest}

View File

@ -9,7 +9,7 @@ import { NPMIcon } from "./SocialMediaIcons/NPMIcon"
export const SocialMediaList = (): JSX.Element => { export const SocialMediaList = (): JSX.Element => {
return ( return (
<ul className="social-media-list m-0 mt-2 list-none py-4 text-center"> <ul className="m-0 mt-2 list-none py-4 text-center">
<SocialMediaItem link="https://github.com/theoludwig" ariaLabel="GitHub"> <SocialMediaItem link="https://github.com/theoludwig" ariaLabel="GitHub">
<GitHubIcon /> <GitHubIcon />
</SocialMediaItem> </SocialMediaItem>

View File

@ -29,20 +29,20 @@ export const SkillComponent = (props: SkillComponentProps): JSX.Element => {
return ( return (
<a <a
href={skillProperties.link} href={skillProperties.link}
className="mx-2 max-w-xl text-yellow hover:underline dark:text-yellow-dark" className="mx-2 max-w-xl text-primary hover:underline dark:text-primary-dark"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
<div className="text-center"> <div className="text-center">
<Image <Image
className="inline h-16 w-16" className="inline size-16"
quality={100} quality={100}
width={64} width={64}
height={64} height={64}
alt={skill} alt={skill}
src={getImage()} src={getImage()}
/> />
<p className="mt-1">{skill}</p> <p className="mt-1 font-semibold">{skill}</p>
</div> </div>
</a> </a>
) )

View File

@ -13,8 +13,8 @@ export const SkillsSection = (props: SkillsSectionProps): JSX.Element => {
<div className="mx-auto w-full px-4"> <div className="mx-auto w-full px-4">
<div className="flex flex-wrap px-4 py-6"> <div className="flex flex-wrap px-4 py-6">
<div className="flex-1"> <div className="flex-1">
<div className="mb-8 border-b border-gray-600 dark:border-white dark:border-opacity-10"> <div className="mb-8 border-b border-gray-600 dark:border-white/10">
<h3 className="my-3 text-xl font-semibold text-yellow dark:text-yellow-dark"> <h3 className="my-3 text-xl font-semibold text-primary dark:text-primary-dark">
{title} {title}
</h3> </h3>
</div> </div>

View File

@ -16,7 +16,7 @@ export const Loader = (props: LoaderProps): JSX.Element => {
height, height,
}} }}
className={classNames( className={classNames(
"animate-spin inline-block border-[3px] border-current border-t-transparent text-yellow dark:text-yellow-dark rounded-full", "inline-block animate-spin rounded-full border-[3px] border-current border-t-transparent text-primary dark:text-primary-dark",
className, className,
)} )}
role="status" role="status"

View File

@ -4,7 +4,10 @@ export const SectionHeading = (props: SectionHeadingProps): JSX.Element => {
const { children, ...rest } = props const { children, ...rest } = props
return ( return (
<h2 {...rest} className="mb-3 mt-1 text-center text-4xl font-semibold"> <h2
{...rest}
className="mb-3 mt-1 text-center text-4xl font-semibold text-primary dark:text-primary-dark"
>
{children} {children}
</h2> </h2>
) )

View File

@ -5,7 +5,8 @@ services:
restart: "unless-stopped" restart: "unless-stopped"
build: build:
context: "./" context: "./"
network_mode: "host" ports:
- "${PORT-3000}:${PORT-3000}"
environment: environment:
PORT: ${PORT-3000} PORT: ${PORT-3000}
env_file: ".env" env_file: ".env"

View File

@ -3,14 +3,14 @@
"basics": { "basics": {
"name": "Théo LUDWIG", "name": "Théo LUDWIG",
"label": "Développeur Full Stack • Étudiant", "label": "Développeur Full Stack • Étudiant",
"image": "https://theoludwig.fr/images/logo_orange.png", "image": "https://theoludwig.fr/images/logo_background.png",
"email": "contact@theoludwig.fr", "email": "contact@theoludwig.fr",
"age": "31/03/2003", "age": "31/03/2003",
"location": { "location": {
"address": "Alsace, France" "address": "Alsace, France",
}, },
"url": "https://theoludwig.fr", "url": "https://theoludwig.fr",
"summary": "Je suis étudiant à l'université suivant la formation \"BUT Informatique\" et me forme en autodidacte dans l'informatique en suivant des formations en ligne. <br/> Je mets en pratique tout ce que j'apprends et réalise de nombreux projets (disponible sur <a href=\"https://theoludwig.fr\">theoludwig.fr</a>)." "summary": "Je me demande constamment comment améliorer notre présent, afin de rendre notre futur meilleur, particulièrement grâce aux progrès de l'informatique. <br /> Ma priorité réside dans la création d'expériences utilisateurs (UX) intuitives, répondant aux besoins des utilisateurs de la manière la plus efficace que possible.",
}, },
"education": [ "education": [
{ {
@ -24,8 +24,8 @@
"Intégration/Déploiement Continue et Docker", "Intégration/Déploiement Continue et Docker",
"Complexité Algorithmique Théorique et Pratique en C++", "Complexité Algorithmique Théorique et Pratique en C++",
// "Projet développement LLM (Large Language Model) et NLP (Natural Language Processing)", // "Projet développement LLM (Large Language Model) et NLP (Natural Language Processing)",
"Base de données NoSQL (Redis, MongoDB, Cassandra)" "Base de données NoSQL (Redis, MongoDB, Cassandra)",
] ],
}, },
{ {
"startDate": "2022", "startDate": "2022",
@ -38,8 +38,8 @@
"Qualité de développement et Tests automatisés", "Qualité de développement et Tests automatisés",
"Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML", "Patrons et Principes de conceptions (Code maintenable et réutilisable) en UML",
"Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)", "Programmation systèmes en C (Multi-Thread, Serveur/Client UDP/TCP)",
"Sécurisation des accès à la base de données et PL/SQL" "Sécurisation des accès à la base de données et PL/SQL",
] ],
}, },
{ {
"startDate": "2021", "startDate": "2021",
@ -51,16 +51,16 @@
"Développement Orientée Objet en Java", "Développement Orientée Objet en Java",
"Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)", "Programmation systèmes en C (Allocation mémoire, Pointeurs, Structures)",
"Développement d'application Windows Forms (.NET Framework) en C#", "Développement d'application Windows Forms (.NET Framework) en C#",
"Base de données relationnelles et langage SQL" "Base de données relationnelles et langage SQL",
] ],
}, },
{ {
"startDate": "2019", "startDate": "2019",
"endDate": "2021", "endDate": "2021",
"studyType": "Baccalauréat Général (Mathématiques et Numériques Sciences Informatiques)", "studyType": "Baccalauréat Général (Mathématiques et Numériques Sciences Informatiques)",
"institution": "Lycée Heinrich Nessel à Haguenau", "institution": "Lycée Heinrich Nessel à Haguenau",
"score": "Mention Assez Bien" "score": "Mention Assez Bien",
} },
// { // {
// "startDate": "2014", // "startDate": "2014",
// "endDate": "2018", // "endDate": "2018",
@ -78,7 +78,7 @@
"position": "Alternant Développeur Web Full Stack", "position": "Alternant Développeur Web Full Stack",
"startDate": "2023-08-28", "startDate": "2023-08-28",
"endDate": "2024-09-02", "endDate": "2024-09-02",
"duration": "1 an" "duration": "1 an",
}, },
{ {
"summary": "Développement d'un outil GED (Gestion Électronique de Documents) en React.js, Laravel et GraphQL.", "summary": "Développement d'un outil GED (Gestion Électronique de Documents) en React.js, Laravel et GraphQL.",
@ -88,7 +88,7 @@
"position": "Stagiaire Développeur Web Full Stack", "position": "Stagiaire Développeur Web Full Stack",
"startDate": "2023-04-11", "startDate": "2023-04-11",
"endDate": "2023-07-26", "endDate": "2023-07-26",
"duration": "4 mois" "duration": "4 mois",
}, },
// { // {
// "summary": "Agent administratif - Numérisation et archivage des plans électriques initialement sous format papier calque.", // "summary": "Agent administratif - Numérisation et archivage des plans électriques initialement sous format papier calque.",
@ -108,7 +108,7 @@
"position": "Stage initiation métier développeur web", "position": "Stage initiation métier développeur web",
"startDate": "2019-06-17", "startDate": "2019-06-17",
"endDate": "2019-06-21", "endDate": "2019-06-21",
"duration": "1 semaine" "duration": "1 semaine",
}, },
{ {
"description": "interests", "description": "interests",
@ -118,7 +118,7 @@
"position": "Participation en équipe de 5 personnes", "position": "Participation en équipe de 5 personnes",
"startDate": "2021-12-02", "startDate": "2021-12-02",
"endDate": "2021-12-03", "endDate": "2021-12-03",
"duration": "1 semaine" "duration": "1 semaine",
}, },
{ {
"description": "interests", "description": "interests",
@ -129,8 +129,8 @@
"position": "Initiation métier Développeur web", "position": "Initiation métier Développeur web",
"startDate": "2019-06-24", "startDate": "2019-06-24",
"endDate": "2019-06-28", "endDate": "2019-06-28",
"duration": "1 semaine" "duration": "1 semaine",
} },
// { // {
// "summary": "Apprentissage du métier \"Chargé de communication\" et des logiciels de graphisme tels que \"Adobe Photoshop\".", // "summary": "Apprentissage du métier \"Chargé de communication\" et des logiciels de graphisme tels que \"Adobe Photoshop\".",
// "website": "https://es.fr/", // "website": "https://es.fr/",
@ -144,24 +144,24 @@
], ],
"interests": [ "interests": [
{ {
"name": "Enthousiaste de l'Open-Source" "name": "Enthousiaste de l'Open-Source",
}, },
{ {
"name": "Passionné de High-Tech" "name": "Passionné de High-Tech",
} },
], ],
"skills": [ "skills": [
{ {
"keywords": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"], "keywords": ["JavaScript/TypeScript", "Python", "C/C++", "PHP"],
"name": "Langages de programmation" "name": "Langages de programmation",
}, },
{ {
"keywords": ["HTML", "CSS", "Tailwind CSS", "React.js/Next.js"], "keywords": ["HTML", "CSS", "Tailwind CSS", "React.js/Next.js"],
"name": "Frontend" "name": "Frontend",
}, },
{ {
"keywords": ["Laravel", "Node.js", "Fastify", "PostgreSQL"], "keywords": ["Laravel", "Node.js", "Fastify", "PostgreSQL"],
"name": "Backend" "name": "Backend",
}, },
{ {
"keywords": [ "keywords": [
@ -169,13 +169,13 @@
"Arch Linux", "Arch Linux",
"Visual Studio Code", "Visual Studio Code",
"Git", "Git",
"Docker" "Docker",
], ],
"name": "Logiciels et outils" "name": "Logiciels et outils",
}, },
{ {
"keywords": ["Permis B", "Anglais"], "keywords": ["Permis B", "Anglais"],
"name": "Autres" "name": "Autres",
} },
] ],
} }

View File

@ -8,20 +8,36 @@
"name": "curriculum-vitae", "name": "curriculum-vitae",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"jsonc-parser": "3.2.0", "jsonc-parser": "3.2.1",
"modern-normalize": "2.0.0" "modern-normalize": "2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "20.8.10", "@types/node": "20.12.7",
"date-and-time": "3.0.3", "date-and-time": "3.1.1",
"vite": "4.5.0", "vite": "5.2.8",
"vite-plugin-html": "3.2.0" "vite-plugin-html": "3.2.2"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
} }
}, },
"node_modules/@esbuild/android-arm": { "node_modules/@esbuild/android-arm": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
"integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -35,9 +51,9 @@
} }
}, },
"node_modules/@esbuild/android-arm64": { "node_modules/@esbuild/android-arm64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
"integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -51,9 +67,9 @@
} }
}, },
"node_modules/@esbuild/android-x64": { "node_modules/@esbuild/android-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
"integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -67,9 +83,9 @@
} }
}, },
"node_modules/@esbuild/darwin-arm64": { "node_modules/@esbuild/darwin-arm64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
"integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -83,9 +99,9 @@
} }
}, },
"node_modules/@esbuild/darwin-x64": { "node_modules/@esbuild/darwin-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
"integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -99,9 +115,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-arm64": { "node_modules/@esbuild/freebsd-arm64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
"integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -115,9 +131,9 @@
} }
}, },
"node_modules/@esbuild/freebsd-x64": { "node_modules/@esbuild/freebsd-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
"integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -131,9 +147,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm": { "node_modules/@esbuild/linux-arm": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
"integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@ -147,9 +163,9 @@
} }
}, },
"node_modules/@esbuild/linux-arm64": { "node_modules/@esbuild/linux-arm64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
"integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -163,9 +179,9 @@
} }
}, },
"node_modules/@esbuild/linux-ia32": { "node_modules/@esbuild/linux-ia32": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
"integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -179,9 +195,9 @@
} }
}, },
"node_modules/@esbuild/linux-loong64": { "node_modules/@esbuild/linux-loong64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
"integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
"cpu": [ "cpu": [
"loong64" "loong64"
], ],
@ -195,9 +211,9 @@
} }
}, },
"node_modules/@esbuild/linux-mips64el": { "node_modules/@esbuild/linux-mips64el": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
"integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
"cpu": [ "cpu": [
"mips64el" "mips64el"
], ],
@ -211,9 +227,9 @@
} }
}, },
"node_modules/@esbuild/linux-ppc64": { "node_modules/@esbuild/linux-ppc64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
"integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
"cpu": [ "cpu": [
"ppc64" "ppc64"
], ],
@ -227,9 +243,9 @@
} }
}, },
"node_modules/@esbuild/linux-riscv64": { "node_modules/@esbuild/linux-riscv64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
"integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@ -243,9 +259,9 @@
} }
}, },
"node_modules/@esbuild/linux-s390x": { "node_modules/@esbuild/linux-s390x": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
"integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
"cpu": [ "cpu": [
"s390x" "s390x"
], ],
@ -259,9 +275,9 @@
} }
}, },
"node_modules/@esbuild/linux-x64": { "node_modules/@esbuild/linux-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
"integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -275,9 +291,9 @@
} }
}, },
"node_modules/@esbuild/netbsd-x64": { "node_modules/@esbuild/netbsd-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
"integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -291,9 +307,9 @@
} }
}, },
"node_modules/@esbuild/openbsd-x64": { "node_modules/@esbuild/openbsd-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
"integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -307,9 +323,9 @@
} }
}, },
"node_modules/@esbuild/sunos-x64": { "node_modules/@esbuild/sunos-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
"integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -323,9 +339,9 @@
} }
}, },
"node_modules/@esbuild/win32-arm64": { "node_modules/@esbuild/win32-arm64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
"integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@ -339,9 +355,9 @@
} }
}, },
"node_modules/@esbuild/win32-ia32": { "node_modules/@esbuild/win32-ia32": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
"integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@ -355,9 +371,9 @@
} }
}, },
"node_modules/@esbuild/win32-x64": { "node_modules/@esbuild/win32-x64": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
"integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@ -371,45 +387,45 @@
} }
}, },
"node_modules/@jridgewell/gen-mapping": { "node_modules/@jridgewell/gen-mapping": {
"version": "0.3.3", "version": "0.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/set-array": "^1.0.1", "@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.24"
}, },
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/resolve-uri": { "node_modules/@jridgewell/resolve-uri": {
"version": "3.1.1", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/set-array": { "node_modules/@jridgewell/set-array": {
"version": "1.1.2", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@jridgewell/source-map": { "node_modules/@jridgewell/source-map": {
"version": "0.3.5", "version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.9" "@jridgewell/trace-mapping": "^0.3.25"
} }
}, },
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
@ -419,9 +435,9 @@
"dev": true "dev": true
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
"version": "0.3.20", "version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/resolve-uri": "^3.1.0",
@ -476,19 +492,220 @@
"node": ">= 8.0.0" "node": ">= 8.0.0"
} }
}, },
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.2.tgz",
"integrity": "sha512-ahxSgCkAEk+P/AVO0vYr7DxOD3CwAQrT0Go9BJyGQ9Ef0QxVOfjDZMiF4Y2s3mLyPrjonchIMH/tbWHucJMykQ==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.2.tgz",
"integrity": "sha512-lAarIdxZWbFSHFSDao9+I/F5jDaKyCqAPMq5HqnfpBw8dKDiCaaqM0lq5h1pQTLeIqueeay4PieGR5jGZMWprw==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.2.tgz",
"integrity": "sha512-SWsr8zEUk82KSqquIMgZEg2GE5mCSfr9sE/thDROkX6pb3QQWPp8Vw8zOq2GyxZ2t0XoSIUlvHDkrf5Gmf7x3Q==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.2.tgz",
"integrity": "sha512-o/HAIrQq0jIxJAhgtIvV5FWviYK4WB0WwV91SLUnsliw1lSAoLsmgEEgRWzDguAFeUEUUoIWXiJrPqU7vGiVkA==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.2.tgz",
"integrity": "sha512-nwlJ65UY9eGq91cBi6VyDfArUJSKOYt5dJQBq8xyLhvS23qO+4Nr/RreibFHjP6t+5ap2ohZrUJcHv5zk5ju/g==",
"cpu": [
"arm"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.2.tgz",
"integrity": "sha512-Pg5TxxO2IVlMj79+c/9G0LREC9SY3HM+pfAwX7zj5/cAuwrbfj2Wv9JbMHIdPCfQpYsI4g9mE+2Bw/3aeSs2rQ==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.2.tgz",
"integrity": "sha512-cAOTjGNm84gc6tS02D1EXtG7tDRsVSDTBVXOLbj31DkwfZwgTPYZ6aafSU7rD/4R2a34JOwlF9fQayuTSkoclA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.2.tgz",
"integrity": "sha512-4RyT6v1kXb7C0fn6zV33rvaX05P0zHoNzaXI/5oFHklfKm602j+N4mn2YvoezQViRLPnxP8M1NaY4s/5kXO5cw==",
"cpu": [
"ppc64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.2.tgz",
"integrity": "sha512-KNUH6jC/vRGAKSorySTyc/yRYlCwN/5pnMjXylfBniwtJx5O7X17KG/0efj8XM3TZU7raYRXJFFReOzNmL1n1w==",
"cpu": [
"riscv64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.2.tgz",
"integrity": "sha512-xPV4y73IBEXToNPa3h5lbgXOi/v0NcvKxU0xejiFw6DtIYQqOTMhZ2DN18/HrrP0PmiL3rGtRG9gz1QE8vFKXQ==",
"cpu": [
"s390x"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.2.tgz",
"integrity": "sha512-QBhtr07iFGmF9egrPOWyO5wciwgtzKkYPNLVCFZTmr4TWmY0oY2Dm/bmhHjKRwZoGiaKdNcKhFtUMBKvlchH+Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.2.tgz",
"integrity": "sha512-8zfsQRQGH23O6qazZSFY5jP5gt4cFvRuKTpuBsC1ZnSWxV8ZKQpPqOZIUtdfMOugCcBvFGRa1pDC/tkf19EgBw==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.2.tgz",
"integrity": "sha512-H4s8UjgkPnlChl6JF5empNvFHp77Jx+Wfy2EtmYPe9G22XV+PMuCinZVHurNe8ggtwoaohxARJZbaH/3xjB/FA==",
"cpu": [
"arm64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.2.tgz",
"integrity": "sha512-djqpAjm/i8erWYF0K6UY4kRO3X5+T4TypIqw60Q8MTqSBaQNpNXDhxdjpZ3ikgb+wn99svA7jxcXpiyg9MUsdw==",
"cpu": [
"ia32"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.14.2",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.2.tgz",
"integrity": "sha512-teAqzLT0yTYZa8ZP7zhFKEx4cotS8Tkk5XiqNMJhD4CpaWB1BHARE4Qy+RzwnXvSAYv+Q3jAqCVBS+PS+Yee8Q==",
"cpu": [
"x64"
],
"dev": true,
"optional": true,
"os": [
"win32"
]
},
"node_modules/@types/estree": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
"dev": true
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.8.10", "version": "20.12.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.10.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
"integrity": "sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==", "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.11.2", "version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"dev": true, "dev": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
@ -585,9 +802,9 @@
} }
}, },
"node_modules/clean-css": { "node_modules/clean-css": {
"version": "5.3.2", "version": "5.3.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.2.tgz", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
"integrity": "sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==", "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"source-map": "~0.6.0" "source-map": "~0.6.0"
@ -679,9 +896,9 @@
} }
}, },
"node_modules/date-and-time": { "node_modules/date-and-time": {
"version": "3.0.3", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-3.0.3.tgz", "resolved": "https://registry.npmjs.org/date-and-time/-/date-and-time-3.1.1.tgz",
"integrity": "sha512-CmHCeTixc3KA5pcLTVs9JCFhmJMFTBsmSsgHnNed4YDNw9yUOrjjRn3zALy8eMgqmTO+4U8k5jl1peC7IoezfA==", "integrity": "sha512-N9kstidT3P0VUk1iKOFilOZ6251r6iTUNx9M9kvgL2jqOk9mljWZUq5CjAtYwCnppWHbERk5YFQUrSbY7FQOpA==",
"dev": true "dev": true
}, },
"node_modules/dom-serializer": { "node_modules/dom-serializer": {
@ -750,15 +967,15 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "16.3.1", "version": "16.4.5",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
"funding": { "funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1" "url": "https://dotenvx.com"
} }
}, },
"node_modules/dotenv-expand": { "node_modules/dotenv-expand": {
@ -771,9 +988,9 @@
} }
}, },
"node_modules/ejs": { "node_modules/ejs": {
"version": "3.1.9", "version": "3.1.10",
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
"integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"jake": "^10.8.5" "jake": "^10.8.5"
@ -795,9 +1012,9 @@
} }
}, },
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.18.20", "version": "0.20.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
"integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"bin": { "bin": {
@ -807,28 +1024,29 @@
"node": ">=12" "node": ">=12"
}, },
"optionalDependencies": { "optionalDependencies": {
"@esbuild/android-arm": "0.18.20", "@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm64": "0.18.20", "@esbuild/android-arm": "0.20.2",
"@esbuild/android-x64": "0.18.20", "@esbuild/android-arm64": "0.20.2",
"@esbuild/darwin-arm64": "0.18.20", "@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-x64": "0.18.20", "@esbuild/darwin-arm64": "0.20.2",
"@esbuild/freebsd-arm64": "0.18.20", "@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-x64": "0.18.20", "@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/linux-arm": "0.18.20", "@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm64": "0.18.20", "@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-ia32": "0.18.20", "@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-loong64": "0.18.20", "@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-mips64el": "0.18.20", "@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-ppc64": "0.18.20", "@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-riscv64": "0.18.20", "@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-s390x": "0.18.20", "@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-x64": "0.18.20", "@esbuild/linux-s390x": "0.20.2",
"@esbuild/netbsd-x64": "0.18.20", "@esbuild/linux-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.18.20", "@esbuild/netbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.18.20", "@esbuild/openbsd-x64": "0.20.2",
"@esbuild/win32-arm64": "0.18.20", "@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-ia32": "0.18.20", "@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-x64": "0.18.20" "@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
} }
}, },
"node_modules/estree-walker": { "node_modules/estree-walker": {
@ -854,9 +1072,9 @@
} }
}, },
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.15.0", "version": "1.17.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
"integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"reusify": "^1.0.4" "reusify": "^1.0.4"
@ -1038,9 +1256,9 @@
} }
}, },
"node_modules/jsonc-parser": { "node_modules/jsonc-parser": {
"version": "3.2.0", "version": "3.2.1",
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz",
"integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA=="
}, },
"node_modules/jsonfile": { "node_modules/jsonfile": {
"version": "6.1.0", "version": "6.1.0",
@ -1203,9 +1421,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.31", "version": "8.4.38",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
"dev": true, "dev": true,
"funding": [ "funding": [
{ {
@ -1222,9 +1440,9 @@
} }
], ],
"dependencies": { "dependencies": {
"nanoid": "^3.3.6", "nanoid": "^3.3.7",
"picocolors": "^1.0.0", "picocolors": "^1.0.0",
"source-map-js": "^1.0.2" "source-map-js": "^1.2.0"
}, },
"engines": { "engines": {
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
@ -1270,18 +1488,36 @@
} }
}, },
"node_modules/rollup": { "node_modules/rollup": {
"version": "3.29.4", "version": "4.14.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.2.tgz",
"integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", "integrity": "sha512-WkeoTWvuBoFjFAhsEOHKRoZ3r9GfTyhh7Vff1zwebEFLEFjT1lG3784xEgKiTa7E+e70vsC81roVL2MP4tgEEQ==",
"dev": true, "dev": true,
"dependencies": {
"@types/estree": "1.0.5"
},
"bin": { "bin": {
"rollup": "dist/bin/rollup" "rollup": "dist/bin/rollup"
}, },
"engines": { "engines": {
"node": ">=14.18.0", "node": ">=18.0.0",
"npm": ">=8.0.0" "npm": ">=8.0.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.14.2",
"@rollup/rollup-android-arm64": "4.14.2",
"@rollup/rollup-darwin-arm64": "4.14.2",
"@rollup/rollup-darwin-x64": "4.14.2",
"@rollup/rollup-linux-arm-gnueabihf": "4.14.2",
"@rollup/rollup-linux-arm64-gnu": "4.14.2",
"@rollup/rollup-linux-arm64-musl": "4.14.2",
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.2",
"@rollup/rollup-linux-riscv64-gnu": "4.14.2",
"@rollup/rollup-linux-s390x-gnu": "4.14.2",
"@rollup/rollup-linux-x64-gnu": "4.14.2",
"@rollup/rollup-linux-x64-musl": "4.14.2",
"@rollup/rollup-win32-arm64-msvc": "4.14.2",
"@rollup/rollup-win32-ia32-msvc": "4.14.2",
"@rollup/rollup-win32-x64-msvc": "4.14.2",
"fsevents": "~2.3.2" "fsevents": "~2.3.2"
} }
}, },
@ -1318,9 +1554,9 @@
} }
}, },
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.0.2", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@ -1349,9 +1585,9 @@
} }
}, },
"node_modules/terser": { "node_modules/terser": {
"version": "5.24.0", "version": "5.30.3",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.24.0.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.3.tgz",
"integrity": "sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==", "integrity": "sha512-STdUgOUx8rLbMGO9IOwHLpCqolkDITFFQSMYYwKE1N2lY6MVSaeoi10z/EhWxRc6ybqoVmKSkhKYH/XUpl7vSA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@jridgewell/source-map": "^0.3.3", "@jridgewell/source-map": "^0.3.3",
@ -1406,29 +1642,29 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "4.5.0", "version": "5.2.8",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
"integrity": "sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==", "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"esbuild": "^0.18.10", "esbuild": "^0.20.1",
"postcss": "^8.4.27", "postcss": "^8.4.38",
"rollup": "^3.27.1" "rollup": "^4.13.0"
}, },
"bin": { "bin": {
"vite": "bin/vite.js" "vite": "bin/vite.js"
}, },
"engines": { "engines": {
"node": "^14.18.0 || >=16.0.0" "node": "^18.0.0 || >=20.0.0"
}, },
"funding": { "funding": {
"url": "https://github.com/vitejs/vite?sponsor=1" "url": "https://github.com/vitejs/vite?sponsor=1"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "~2.3.2" "fsevents": "~2.3.3"
}, },
"peerDependencies": { "peerDependencies": {
"@types/node": ">= 14", "@types/node": "^18.0.0 || >=20.0.0",
"less": "*", "less": "*",
"lightningcss": "^1.21.0", "lightningcss": "^1.21.0",
"sass": "*", "sass": "*",
@ -1461,9 +1697,9 @@
} }
}, },
"node_modules/vite-plugin-html": { "node_modules/vite-plugin-html": {
"version": "3.2.0", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/vite-plugin-html/-/vite-plugin-html-3.2.0.tgz", "resolved": "https://registry.npmjs.org/vite-plugin-html/-/vite-plugin-html-3.2.2.tgz",
"integrity": "sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==", "integrity": "sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@rollup/pluginutils": "^4.2.0", "@rollup/pluginutils": "^4.2.0",

View File

@ -9,13 +9,13 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"jsonc-parser": "3.2.0", "jsonc-parser": "3.2.1",
"modern-normalize": "2.0.0" "modern-normalize": "2.0.0"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "20.8.10", "@types/node": "20.12.7",
"date-and-time": "3.0.3", "date-and-time": "3.1.1",
"vite": "4.5.0", "vite": "5.2.8",
"vite-plugin-html": "3.2.0" "vite-plugin-html": "3.2.2"
} }
} }

View File

@ -38,7 +38,6 @@ describe("Common > Header", () => {
describe("Switch Language", () => { describe("Switch Language", () => {
it("should switch locale from English (default) to French", () => { it("should switch locale from English (default) to French", () => {
cy.get("h1").contains("Théo LUDWIG")
cy.get("[data-cy=locale-flag-text]").contains("English") cy.get("[data-cy=locale-flag-text]").contains("English")
cy.get("[data-cy=locales-list]").should("not.be.visible") cy.get("[data-cy=locales-list]").should("not.be.visible")
cy.get("[data-cy=locale-click]").click() cy.get("[data-cy=locale-click]").click()
@ -47,15 +46,14 @@ describe("Common > Header", () => {
.contains("French") .contains("French")
.click() .click()
cy.get("[data-cy=locales-list]").should("not.be.visible") cy.get("[data-cy=locales-list]").should("not.be.visible")
cy.get("[data-cy=locale-flag-text]").contains("French") // cy.get("[data-cy=locale-flag-text]").contains("French")
cy.get("h1").contains("Théo LUDWIG")
}) })
it("should close the locale list menu when clicking outside", () => { it("should close the locale list menu when clicking outside", () => {
cy.get("[data-cy=locales-list]").should("not.be.visible") cy.get("[data-cy=locales-list]").should("not.be.visible")
cy.get("[data-cy=locale-click]").click() cy.get("[data-cy=locale-click]").click()
cy.get("[data-cy=locales-list]").should("be.visible") cy.get("[data-cy=locales-list]").should("be.visible")
cy.get("h1").click() cy.get("main h1").click()
cy.get("[data-cy=locales-list]").should("not.be.visible") cy.get("[data-cy=locales-list]").should("not.be.visible")
}) })
}) })

View File

@ -2,7 +2,7 @@ describe("Page /blog/[slug]", () => {
it("should displays the first blog post (`hello-world`)", () => { it("should displays the first blog post (`hello-world`)", () => {
cy.visit("/blog/hello-world") cy.visit("/blog/hello-world")
cy.get("[data-cy=locale-flag-text]").should("not.exist") cy.get("[data-cy=locale-flag-text]").should("not.exist")
cy.get("h1").should("have.text", "👋 Hello, world!") cy.get("main h1").should("have.text", "👋 Hello, world!")
cy.get(".prose a:visible").should("have.attr", "target", "_blank") cy.get(".prose a:visible").should("have.attr", "target", "_blank")
}) })

15
hooks/useIsMounted.ts Normal file
View File

@ -0,0 +1,15 @@
import { useEffect, useState } from "react"
export interface UseIsMountedResult {
isMounted: boolean
}
export const useIsMounted = (): UseIsMountedResult => {
const [isMounted, setIsMounted] = useState(false)
useEffect(() => {
setIsMounted(true)
}, [])
return { isMounted }
}

View File

@ -25,6 +25,6 @@ const translations = {
export const i18n = new I18n(translations, { export const i18n = new I18n(translations, {
defaultLocale: DEFAULT_LOCALE, defaultLocale: DEFAULT_LOCALE,
availableLocales: LOCALES.slice(), availableLocales: [...LOCALES],
enableFallback: true, enableFallback: true,
}) })

View File

@ -1,27 +1,25 @@
{ {
"about": { "about": {
"description": "Developer Full Stack • Open-Source enthusiast", "description": "Developer Full Stack • Open-Source Enthusiast",
"pronouns": "Pronouns", "pronouns": "Pronouns",
"pronouns-value": "He/Him", "pronouns-value": "He/Him",
"birth-date": "Birth date", "birth-date": "Birth date",
"years-old": "years old", "years-old": "years old",
"nationality": "Nationality", "nationality": "Nationality",
"description-bottom": "I am a student in computer science following the French training \"BUT Informatique\" and I am also a self-taught." "description-bottom": "I constantly wonder how to <strong>improve our present, to make our future better</strong>, particularly thanks to the advancements in <strong>computer science</strong>."
}, },
"interests": { "interests": {
"title": "Interests", "title": "Interests",
"paragraphs": [ "paragraphs": [
{ {
"title": "Developer Full Stack", "title": "Developer Full Stack",
"description": "Computer programming is my main hobby, I love it! <br/> Mostly web development for the moment but I'm programming in others programming language too." "description": "My priority is to craft <strong>intuitive user experiences (<abbr title=\"User Experience\">UX</abbr>)</strong>, that meet the needs of the users <strong>in the most efficient way possible</strong>. <br/> Mainly focused on the development of <strong>Web solutions</strong>. <br/> I am also interested in mobile and desktop application development, among other areas within the field of computer science.",
"id": "code"
}, },
{ {
"title": "Open-Source enthusiast", "title": "Open-Source Enthusiast",
"description": "For me, everyone should work, solve problems, build things and think together.<br/> The website is open-source on <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/theoludwig/theoludwig' target='_blank' rel='noopener noreferrer'>GitHub</a>." "description": "I value the <strong>sharing of knowledge and collaboration</strong> to collectively resolve problems. <br /> The source code of the website is available on <a class='text-primary dark:text-primary-dark hover:underline font-semibold' href='https://github.com/theoludwig/theoludwig' target='_blank' rel='noopener noreferrer'>GitHub</a>.",
}, "id": "open-source"
{
"title": "Passionate about High-Tech",
"description": "I always wondered how the future would be. Every day I want to wake up and think that the future will be great and better than the past. Technologies improve gradually over time, which is very useful in many areas."
} }
] ]
}, },
@ -39,12 +37,6 @@
"link": "https://carolo.theoludwig.fr/", "link": "https://carolo.theoludwig.fr/",
"image": "/images/portfolio/Carolo.png" "image": "/images/portfolio/Carolo.png"
}, },
{
"title": "Thream",
"description": "Your open source platform to stay close with your friends and communities, talk, chat, collaborate, share and have fun.",
"link": "https://thream.theoludwig.fr/",
"image": "/images/portfolio/Thream.png"
},
{ {
"title": "Leon", "title": "Leon",
"description": "Leon is your open-source personal assistant.", "description": "Leon is your open-source personal assistant.",

View File

@ -6,22 +6,20 @@
"birth-date": "Date de naissance", "birth-date": "Date de naissance",
"years-old": "ans", "years-old": "ans",
"nationality": "Nationalité", "nationality": "Nationalité",
"description-bottom": "Je suis étudiant à l'université suivant la formation \"BUT Informatique\" et me forme en autodidacte dans l'informatique en suivant des formations en ligne." "description-bottom": "Je me demande constamment comment <strong>améliorer notre présent, afin de rendre notre futur meilleur</strong>, particulièrement grâce aux progrès de l'<strong>informatique</strong>."
}, },
"interests": { "interests": {
"title": "Intérêts", "title": "Intérêts",
"paragraphs": [ "paragraphs": [
{ {
"title": "Développeur Full Stack", "title": "Développeur Full Stack",
"description": "La programmation informatique est mon loisir principal, j'adore! <br/> Principalement du développement Web pour le moment, mais je programme aussi dans d'autres langages de programmation." "description": "Ma priorité réside dans la création d'<strong>expériences utilisateurs (<abbr title=\"User Experience\">UX</abbr>) intuitives</strong>, répondant aux besoins des utilisateurs de la <strong>manière la plus efficace que possible</strong>. <br/> Principalement axé sur l'élaboration de <strong>solutions en Développement Web</strong>. <br/> Je suis également intéressé par le développement d'applications mobiles parmis d'autres domaines de l'informatique.",
"id": "code"
}, },
{ {
"title": "Enthousiaste de l'Open-Source", "title": "Enthousiaste de l'Open-Source",
"description": "Pour moi, tout le monde devrait travailler, résoudre des problèmes, construire des choses et réfléchir ensemble. <br/> Le site est open-source sur <a class='text-yellow dark:text-yellow-dark hover:underline' href='https://github.com/theoludwig/theoludwig' target='_blank' rel='noopener noreferrer'>GitHub</a>." "description": "J'apprécie le <strong>partage des connaissances et la collaboration</strong> pour résoudre des défis collectivement. <br /> Le code source du site est accessible sur <a class='text-primary dark:text-primary-dark hover:underline font-semibold' href='https://github.com/theoludwig/theoludwig' target='_blank' rel='noopener noreferrer'>GitHub</a>.",
}, "id": "open-source"
{
"title": "Passionné de High-Tech",
"description": "Je me suis toujours demandé comment l'avenir serait. Chaque jour, je veux me réveiller et penser que l'avenir sera formidable et meilleur que le passé. Les technolgies s'améliorent progressivement avec le temps, ce qui est très utile dans de nombreux domaines."
} }
] ]
}, },
@ -39,12 +37,6 @@
"link": "https://carolo.theoludwig.fr/", "link": "https://carolo.theoludwig.fr/",
"image": "/images/portfolio/Carolo.png" "image": "/images/portfolio/Carolo.png"
}, },
{
"title": "Thream",
"description": "Votre plateforme open source pour rester proche de vos amis et communautés, parler, discuter, collaborer, partager et amusez-vous.",
"link": "https://thream.theoludwig.fr/",
"image": "/images/portfolio/Thream.png"
},
{ {
"title": "Leon", "title": "Leon",
"description": "Leon est votre assistant personnel open source.", "description": "Leon est votre assistant personnel open source.",

View File

@ -1,7 +1,9 @@
const IS_STANDALONE = process.env.IS_STANDALONE === "true"
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
output: "standalone", output: IS_STANDALONE ? "standalone" : undefined,
eslint: { eslint: {
ignoreDuringBuilds: true, ignoreDuringBuilds: true,
}, },

11727
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "theoludwig", "name": "theoludwig",
"version": "3.1.1", "version": "3.2.4",
"private": true, "private": true,
"repository": { "repository": {
"type": "git", "type": "git",
@ -26,75 +26,77 @@
"test:dev": "start-server-and-test \"dev\" \"http://127.0.0.1:3000\" \"cypress open\"", "test:dev": "start-server-and-test \"dev\" \"http://127.0.0.1:3000\" \"cypress open\"",
"curriculum-vitae:build": "node ./curriculum-vitae/build.js", "curriculum-vitae:build": "node ./curriculum-vitae/build.js",
"release": "semantic-release", "release": "semantic-release",
"postinstall": "husky install" "postinstall": "husky"
}, },
"dependencies": { "dependencies": {
"@fontsource/montserrat": "5.0.15", "@fontsource/montserrat": "5.0.17",
"@formatjs/intl-localematcher": "0.5.0", "@formatjs/intl-localematcher": "0.5.4",
"@fortawesome/fontawesome-svg-core": "6.4.2", "@fortawesome/fontawesome-svg-core": "6.5.2",
"@fortawesome/free-brands-svg-icons": "6.4.2", "@fortawesome/free-brands-svg-icons": "6.5.2",
"@fortawesome/free-solid-svg-icons": "6.4.2", "@fortawesome/free-solid-svg-icons": "6.5.2",
"@fortawesome/react-fontawesome": "0.2.0", "@fortawesome/react-fontawesome": "0.2.0",
"@giscus/react": "2.3.0", "@giscus/react": "3.0.0",
"clsx": "2.0.0", "clsx": "2.1.0",
"date-and-time": "3.0.3", "date-and-time": "3.1.1",
"gray-matter": "4.0.3", "gray-matter": "4.0.3",
"html-react-parser": "5.0.6", "html-react-parser": "5.1.10",
"i18n-js": "4.3.2", "i18n-js": "4.3.2",
"katex": "0.16.9", "katex": "0.16.10",
"negotiator": "0.6.3", "negotiator": "0.6.3",
"next": "14.0.1", "next": "14.1.0",
"next-mdx-remote": "4.4.1", "next-mdx-remote": "4.4.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"read-pkg": "9.0.0", "read-pkg": "9.0.1",
"rehype-katex": "6.0.3", "rehype-katex": "6.0.3",
"rehype-raw": "6.1.1", "rehype-raw": "6.1.1",
"rehype-slug": "5.1.0", "rehype-slug": "5.1.0",
"remark-gfm": "3.0.1", "remark-gfm": "3.0.1",
"remark-math": "5.1.1", "remark-math": "5.1.1",
"sharp": "0.32.6", "sharp": "0.33.3",
"shiki": "0.14.5", "shiki": "0.14.7",
"unified": "10.1.2", "unified": "10.1.2",
"unist-util-visit": "5.0.0", "unist-util-visit": "5.0.0",
"universal-cookie": "6.1.1" "universal-cookie": "7.1.4"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "17.8.1", "@commitlint/cli": "19.1.0",
"@commitlint/config-conventional": "17.8.1", "@commitlint/config-conventional": "19.1.0",
"@saithodev/semantic-release-backmerge": "3.2.1", "@saithodev/semantic-release-backmerge": "4.0.1",
"@semantic-release/git": "10.0.1", "@semantic-release/git": "10.0.1",
"@tailwindcss/typography": "0.5.10", "@tailwindcss/typography": "0.5.12",
"@tsconfig/strictest": "2.0.2", "@total-typescript/ts-reset": "0.5.1",
"@tsconfig/strictest": "2.0.5",
"@types/negotiator": "0.6.3", "@types/negotiator": "0.6.3",
"@types/node": "20.8.10", "@types/node": "20.12.7",
"@types/react": "18.2.36", "@types/react": "18.2.78",
"@types/unist": "3.0.1", "@types/unist": "3.0.2",
"@typescript-eslint/eslint-plugin": "6.10.0", "@typescript-eslint/eslint-plugin": "7.6.0",
"@typescript-eslint/parser": "6.10.0", "@typescript-eslint/parser": "7.6.0",
"autoprefixer": "10.4.16", "autoprefixer": "10.4.19",
"curriculum-vitae": "file:./curriculum-vitae", "curriculum-vitae": "file:./curriculum-vitae",
"cypress": "13.4.0", "cypress": "13.7.3",
"editorconfig-checker": "5.1.1", "editorconfig-checker": "5.1.5",
"eslint": "8.53.0", "eslint": "8.56.0",
"eslint-config-conventions": "12.0.0", "eslint-config-conventions": "14.1.0",
"eslint-config-next": "14.0.1", "eslint-config-next": "14.1.0",
"eslint-config-prettier": "9.0.0", "eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.29.0", "eslint-plugin-import": "2.29.1",
"eslint-plugin-prettier": "5.0.1", "eslint-plugin-prettier": "5.1.3",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "48.0.1", "eslint-plugin-tailwindcss": "3.14.2",
"html-w3c-validator": "1.5.0", "eslint-plugin-unicorn": "51.0.1",
"husky": "8.0.3", "html-w3c-validator": "1.6.1",
"lint-staged": "15.0.2", "husky": "9.0.11",
"markdownlint-cli2": "0.10.0", "lint-staged": "15.2.2",
"markdownlint-rule-relative-links": "2.1.0", "markdownlint-cli2": "0.13.0",
"postcss": "8.4.31", "markdownlint-rule-relative-links": "2.3.2",
"prettier": "3.0.3", "postcss": "8.4.38",
"prettier-plugin-tailwindcss": "0.5.6", "prettier": "3.2.5",
"semantic-release": "21.1.2", "prettier-plugin-tailwindcss": "0.5.13",
"start-server-and-test": "2.0.2", "semantic-release": "23.0.8",
"tailwindcss": "3.3.5", "start-server-and-test": "2.0.3",
"typescript": "5.2.2" "tailwindcss": "3.4.3",
"typescript": "5.4.5"
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

View File

@ -8,23 +8,20 @@ const tailwindConfig = {
darkMode: "class", darkMode: "class",
theme: { theme: {
extend: { extend: {
screens: {
xs: "380px",
},
colors: { colors: {
black: "#181818", black: "#181818",
gray: { gray: {
DEFAULT: "#333333", DEFAULT: "#333333",
dark: "#b2bac2", dark: "#b7c0c9",
}, },
yellow: { primary: {
DEFAULT: "#ff6000", DEFAULT: "#006cff",
dark: "#ffd800", dark: "#00aeff",
}, },
}, },
boxShadow: { boxShadow: {
dark: "0px 0px 4px 4px rgba(0, 0, 0, 0.25)", dark: "0px 0px 2px 2px rgba(0, 0, 0, 0.25)",
light: "0px 0px 4px 4px rgba(0, 0, 0, 0.10)", light: "0px 0px 2px 2px rgba(0, 0, 0, 0.10)",
darkFlag: "0px 1px 10px hsla(0, 0%, 100%, 0.2)", darkFlag: "0px 1px 10px hsla(0, 0%, 100%, 0.2)",
lightFlag: "0px 1px 10px rgba(0, 0, 0, 0.25)", lightFlag: "0px 1px 10px rgba(0, 0, 0, 0.25)",
}, },

View File

@ -3,14 +3,14 @@
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Bundler",
"lib": ["dom", "dom.iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": true, "allowJs": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["./*"] "@/*": ["./*"]
}, },
"types": ["cypress"], "types": ["@total-typescript/ts-reset", "cypress"],
"noEmit": true, "noEmit": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"jsx": "preserve", "jsx": "preserve",
@ -25,5 +25,5 @@
] ]
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules", ".next"]
} }

View File

@ -1,6 +1,6 @@
export const BIRTH_DATE_DAY = "31" as const export const BIRTH_DATE_DAY = "31"
export const BIRTH_DATE_MONTH = "03" as const export const BIRTH_DATE_MONTH = "03"
export const BIRTH_DATE_YEAR = "2003" as const export const BIRTH_DATE_YEAR = "2003"
export const BIRTH_DATE_STRING = export const BIRTH_DATE_STRING =
`${BIRTH_DATE_DAY}/${BIRTH_DATE_MONTH}/${BIRTH_DATE_YEAR}` as const `${BIRTH_DATE_DAY}/${BIRTH_DATE_MONTH}/${BIRTH_DATE_YEAR}` as const
export const BIRTH_DATE_ISO_8601 = export const BIRTH_DATE_ISO_8601 =

8
utils/getVersion.ts Normal file
View File

@ -0,0 +1,8 @@
export const getVersion = async (): Promise<string> => {
if (process.env.NODE_ENV === "development") {
return "0.0.0-development"
}
const { readPackage } = await import("read-pkg")
const { version } = await readPackage()
return version
}