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

Compare commits

..

17 Commits

Author SHA1 Message Date
dd09092842 chore(release): 3.3.1 [skip ci] 2024-07-06 21:01:51 +00:00
f64acb68c7 fix: stop using flag image, use emoji instead 2024-07-06 02:33:49 +02:00
3074945c54 chore(release): 3.3.0 [skip ci] 2024-05-23 20:35:01 +00:00
fc0dfdda5f chore(blog): update shiki to v1.6.0 and update next-mdx-remote to v5.0.0 2024-05-23 22:30:13 +02:00
f62964c62a ci: update GitHub Actions 2024-05-23 21:41:05 +02:00
8ec113c9cb feat(blog): update Git Ultimate Guide to add trick about cherry-pick and diff-commits alias 2024-05-23 10:29:35 +02:00
8a59e9034f chore(release): 3.2.6 [skip ci] 2024-05-21 18:23:28 +00:00
d7121ea833 style: fix tailwindcss linting 2024-05-21 20:18:05 +02:00
c10f690622 build(deps): update dependencies to latest 2024-05-21 20:15:57 +02:00
6915072ab9 chore: delete unused config 2024-05-21 19:31:45 +02:00
dd803bcc51 test: fix should display hello-world blog post 2024-05-21 19:17:29 +02:00
efa33f26ec fix(blog): headings should be aligned with the text, not shifted 2024-05-21 19:06:12 +02:00
5f3dfad988 chore(release): 3.2.5 [skip ci] 2024-05-16 08:09:25 +00:00
b231381cb3 fix: client-side age calculation, more glanular check for isMounted
Allows to render as much as possible on the server side.
While keeping the calculation of the age on the client side to avoid hydratation mismatch.
2024-05-16 10:06:43 +02:00
bbb2e56512 fix: usage of correct heading levels and html tags 2024-05-16 09:56:19 +02:00
66cf6d7438 fix: add scroll behavior: smooth 2024-05-16 09:32:20 +02:00
2a635bf3ba fix: add hover effects 2024-05-16 09:26:05 +02:00
37 changed files with 4097 additions and 2767 deletions

View File

@ -1 +0,0 @@
FROM mcr.microsoft.com/devcontainers/javascript-node:20

View File

@ -1,9 +0,0 @@
services:
workspace:
build:
context: "./"
dockerfile: "./Dockerfile"
volumes:
- "..:/workspace:cached"
command: "sleep infinity"
network_mode: "host"

View File

@ -1,24 +0,0 @@
{
"name": "theoludwig",
"dockerComposeFile": "./compose.yaml",
"service": "workspace",
"workspaceFolder": "/workspace",
"customizations": {
"vscode": {
"settings": {
"remote.autoForwardPorts": false,
"remote.localPortHost": "allInterfaces"
}
},
"extensions": [
"editorconfig.editorconfig",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"davidanson.vscode-markdownlint",
"bradlc.vscode-tailwindcss",
"mikestead.dotenv",
"ms-azuretools.vscode-docker"
]
},
"remoteUser": "node"
}

View File

@ -3,13 +3,9 @@
"extends": [
"conventions",
"next/core-web-vitals",
"plugin:tailwindcss/recommended",
"prettier"
"plugin:tailwindcss/recommended"
],
"plugins": ["prettier"],
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["import", "promise", "unicorn"],
"settings": {
"tailwindcss": {
"callees": ["classNames"]
@ -19,7 +15,6 @@
}
},
"rules": {
"prettier/prettier": "error",
"react/self-closing-comp": [
"error",
{
@ -33,7 +28,11 @@
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser"
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"parserOptions": {
"project": "./tsconfig.json"
}
}
]
}

View File

@ -10,7 +10,7 @@ jobs:
build:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.1.6"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.2"

View File

@ -10,7 +10,7 @@ jobs:
lint:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.1.6"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.2"
@ -37,6 +37,6 @@ jobs:
run: "npm run lint:prettier"
- name: "lint:dotenv"
uses: "dotenv-linter/action-dotenv-linter@v2.18.0"
uses: "dotenv-linter/action-dotenv-linter@v2.21.0"
with:
github_token: ${{ secrets.github_token }}

View File

@ -8,7 +8,7 @@ jobs:
release:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.1.6"
with:
fetch-depth: 0
persist-credentials: false

View File

@ -10,7 +10,7 @@ jobs:
test-unit:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.1.6"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.2"
@ -27,7 +27,7 @@ jobs:
test-e2e:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.1.6"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.2"

View File

@ -1,10 +0,0 @@
image: "gitpod/workspace-full"
tasks:
- before: "cp .env.example .env"
init: "npm clean-install"
command: "npm run dev"
ports:
- port: 3000
onOpen: "open-preview"

View File

@ -29,8 +29,6 @@ The commit message guidelines adheres to [Conventional Commits](https://www.conv
## Getting Started
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/theoludwig/theoludwig)
### Prerequisites
- [Node.js](https://nodejs.org/) >= 20.0.0
@ -66,6 +64,6 @@ npm run dev
docker compose up --build
```
### Services started
### Service started
- `website`: <http://127.0.0.1:3000>
`website`: <http://127.0.0.1:3000>

View File

@ -62,15 +62,17 @@ code {
code .line::before {
content: counter(step);
counter-increment: step;
width: 1rem;
margin-right: 1.5rem;
display: inline-block;
margin-right: 1rem;
text-align: right;
color: rgba(133, 133, 133, 0.8);
word-wrap: normal;
word-break: normal;
}
code .line:last-child {
display: none;
}
.katex .base {
display: inline !important;
white-space: normal !important;

View File

@ -57,10 +57,13 @@ const RootLayout = (props: RootLayoutProps): JSX.Element => {
return (
<html
lang={i18n.locale}
className={classNames({
dark: theme === "dark",
light: theme === "light",
})}
className={classNames(
{
dark: theme === "dark",
light: theme === "light",
},
"scroll-smooth",
)}
style={{
colorScheme: theme,
}}

View File

@ -1,22 +1,22 @@
import { faLink } from "@fortawesome/free-solid-svg-icons"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { nodeTypes } from "@mdx-js/mdx"
import rehypeShikiFromHighlighter from "@shikijs/rehype/core"
import { MDXRemote } from "next-mdx-remote/rsc"
import { cookies } from "next/headers"
import Image from "next/image"
import Link from "next/link"
import { cookies } from "next/headers"
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"
import { faLink } from "@fortawesome/free-solid-svg-icons"
import { MDXRemote } from "next-mdx-remote/rsc"
import { nodeTypes } from "@mdx-js/mdx"
import rehypeRaw from "rehype-raw"
import remarkGfm from "remark-gfm"
import rehypeSlug from "rehype-slug"
import remarkMath from "remark-math"
import rehypeKatex from "rehype-katex"
import { getHighlighter } from "shiki"
import rehypeRaw from "rehype-raw"
import rehypeSlug from "rehype-slug"
import remarkGfm from "remark-gfm"
import remarkMath from "remark-math"
import { getHighlighterCore } from "shiki/core"
import "katex/dist/katex.min.css"
import { getTheme } from "@/theme/theme.server"
import { remarkSyntaxHighlightingPlugin } from "@/blog/remarkSyntaxHighlightingPlugin"
import { BlogPostComments } from "@/blog/BlogPostComments"
import { getTheme } from "@/theme/theme.server"
const Heading = (
props: React.DetailedHTMLProps<
@ -26,14 +26,14 @@ const Heading = (
): JSX.Element => {
const { children, id = "" } = props
return (
<h2 {...props} className="group">
<Link
href={`#${id}`}
className="invisible !text-black group-hover:visible dark:!text-white"
>
<FontAwesomeIcon className="mr-2 inline size-4" icon={faLink} />
<h2 {...props}>
<Link href={`#${id}`} className="group relative hover:no-underline">
<FontAwesomeIcon
className="absolute bottom-2 left-[-26px] mr-2 hidden size-4 !text-black group-hover:inline dark:!text-white"
icon={faLink}
/>
{children}
</Link>
{children}
</h2>
)
}
@ -50,8 +50,19 @@ export const BlogPostContent = async (
const cookiesStore = cookies()
const theme = getTheme()
const highlighter = await getHighlighter({
theme: `${theme}-plus`,
const highlighter = await getHighlighterCore({
themes: [
import("shiki/themes/light-plus.mjs"),
import("shiki/themes/dark-plus.mjs"),
],
langs: [
import("shiki/langs/markdown.mjs"),
import("shiki/langs/shell.mjs"),
import("shiki/langs/javascript.mjs"),
import("shiki/langs/typescript.mjs"),
import("shiki/langs/python.mjs"),
],
loadWasm: import("shiki/wasm"),
})
return (
@ -61,15 +72,18 @@ export const BlogPostContent = async (
source={content}
options={{
mdxOptions: {
remarkPlugins: [
remarkGfm,
[remarkSyntaxHighlightingPlugin, { highlighter }],
remarkMath,
],
remarkPlugins: [remarkGfm, remarkMath],
rehypePlugins: [
rehypeSlug,
[rehypeRaw, { passThrough: nodeTypes }],
rehypeKatex,
[
rehypeShikiFromHighlighter,
highlighter,
{
theme: `${theme}-plus`,
},
],
],
},
}}

View File

@ -9,37 +9,34 @@ export const BlogPosts = async (): Promise<JSX.Element> => {
return (
<div className="flex w-full items-center justify-center p-8">
<div className="w-[1600px]" data-cy="blog-posts">
{posts.map((post, index) => {
<ul className="w-[1600px]" data-cy="blog-posts">
{posts.map((post) => {
const postPublishedOn = date.format(
new Date(post.frontmatter.publishedOn),
"DD/MM/YYYY",
)
return (
<Link
href={`/blog/${post.slug}`}
key={index}
locale="en"
data-cy={post.slug}
>
<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 text-primary dark:text-primary-dark"
>
{post.frontmatter.title}
</h2>
<p data-cy="blog-post-date" className="mt-2">
{postPublishedOn}
</p>
<p data-cy="blog-post-description" className="mt-3">
{post.frontmatter.description}
</p>
</ShadowContainer>
</Link>
<li key={post.slug}>
<Link href={`/blog/${post.slug}`} locale="en" data-cy={post.slug}>
<ShadowContainer className="cursor-pointer p-6 transition-all duration-300 ease-in-out hover:scale-[1.02]">
<h2
data-cy="blog-post-title"
className="text-xl font-semibold text-primary dark:text-primary-dark"
>
{post.frontmatter.title}
</h2>
<p data-cy="blog-post-date" className="mt-2">
{postPublishedOn}
</p>
<p data-cy="blog-post-description" className="mt-3">
{post.frontmatter.description}
</p>
</ShadowContainer>
</Link>
</li>
)
})}
</div>
</ul>
</div>
)
}

View File

@ -154,6 +154,17 @@ git reset --soft <branch>
# (by first being on the branch where you want to apply the commit)
git cherry-pick <commit>
# To avoid creating duplicated commits with cherry-pick, we can use rebase after cherry-pick.
# <target-branch> being the commit where you want to apply the commit to cherry-pick.
# <from-branch> being the branch where the commit to cherry-pick is.
git rebase <target-branch> <from-branch>
# If, by mistake, you have started a branch from the wrong base branch, you can rebase the branch on the correct base branch.
# For example, if you have started a branch `feature-2` from `feature` instead of `develop`, you can rebase the branch on `develop`.
git rebase --onto <new-base-branch> <old-base-branch> <branch>
# For example:
git rebase --onto develop feature feature-2
# To list all commits that differ between two branches
git log <branch1>..<branch2> # commits in branch2 that are not in branch1 (branch2 ahead of branch1, branch2 behind branch1)
git log <branch2>..<branch1> # commits in branch1 that are not in branch2 (branch1 ahead of branch2, branch1 behind branch2)
@ -245,6 +256,32 @@ There are many ways to organize the work, but the most popular ones are:
They are called **Git workflows**, or **Git branching strategies**.
## Tips and tricks
### `diff-commits` alias
The `git diff` command allows you to compare the changes between two commits, branches, etc.
Sometimes, you want to compare what commits have been made between two branches, without looking at the changes in the files, to do so, we can create an `alias` in `.gitconfig`:
```sh
[alias]
diff-commits = !sh -c 'echo -n "Commits in $2 not in $1 \\(" && printf "%d" $(git cherry -v $1 $2 | wc -l) && echo "\\)" && git cherry -v $1 $2 && echo "" && echo -n "Commits in $1 not in $2 \\(" && printf "%d" $(git cherry -v $2 $1 | wc -l) && echo "\\)" && git cherry -v $2 $1' -
```
With this alias, we can compare the commits between `main` and `develop` branches for example:
```sh
$ git diff-commits main develop
Commits in develop not in main (2)
+ 9b80e0724df8454b43bc3935a1bffb67615572d7 feat: new feature
+ 50721f8ecb60ff023bdccc1873ec1e20ee0b21a0 feat: new feature 2
Commits in main not in develop (1)
- f7bb9d2af7763e0a311099e880e8bf7d6b51bf4d fix: urgent hotfix
```
## Conclusion
`git` is the tool that every programmer should know to do collaborative work (not only, `git` is also very powerful even when working alone) and keep track of changes across a set of files.

View File

@ -1,32 +0,0 @@
import type { Plugin, Transformer } from "unified"
import type { Literal, Node } from "unist"
import { visit } from "unist-util-visit"
import type { Highlighter } from "shiki"
export interface RemarkSyntaxHighlightingPluginOptions {
highlighter: Highlighter
}
export interface RemarkSyntaxHighlightingNode extends Node {
lang: string
meta: string
children: undefined
value: string
data: Record<string, unknown>
}
export const remarkSyntaxHighlightingPlugin: Plugin<
[RemarkSyntaxHighlightingPluginOptions],
Literal
> = (options) => {
const transformer: Transformer<RemarkSyntaxHighlightingNode> = (tree) => {
visit<RemarkSyntaxHighlightingNode, string>(tree, "code", (node) => {
node.type = "html"
node.children = undefined
node.value = options.highlighter.codeToHtml(node.value, {
lang: node.lang,
})
})
}
return transformer
}

View File

@ -1,7 +1,5 @@
import Image from "next/image"
import type { CookiesStore } from "@/utils/constants"
import { useI18n } from "@/i18n/i18n.client"
import type { CookiesStore } from "@/utils/constants"
export interface LocaleFlagProps {
locale: string
@ -14,17 +12,8 @@ export const LocaleFlag = (props: LocaleFlagProps): JSX.Element => {
const i18n = useI18n(cookiesStore)
return (
<>
<Image
quality={100}
width={35}
height={35}
src={`/images/locales/${locale}.svg`}
alt={locale}
/>
<p data-cy="locale-flag-text" className="mx-2 text-base">
{i18n.translate(`common.${locale}`)}
</p>
</>
<p data-cy="locale-flag-text" className="mx-2 text-lg font-semibold">
{i18n.translate(`common.${locale}`)}
</p>
)
}

View File

@ -58,7 +58,7 @@ export const SwitchTheme = (props: SwitchThemeProps): JSX.Element => {
</div>
<div
className={classNames(
"absolute top-[1px] box-border size-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out",
"absolute top-px box-border size-[22px] rounded-[50%] bg-[#fafafa] text-white transition-all duration-[250ms] ease-in-out",
{
"left-[27px]": theme === "dark",
"left-0": theme === "light",
@ -70,7 +70,7 @@ export const SwitchTheme = (props: SwitchThemeProps): JSX.Element => {
data-cy="switch-theme-input"
type="checkbox"
aria-label="Dark mode toggle"
className="absolute m-[-1px] hidden size-[1px] overflow-hidden border-0 p-0"
className="absolute -m-px hidden size-px overflow-hidden border-0 p-0"
defaultChecked
/>
</div>

View File

@ -14,8 +14,11 @@ export const Header = (): JSX.Element => {
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">
<Link href="/">
<h1 className="flex items-center justify-center">
<h1>
<Link
href="/"
className="flex items-center justify-center transition-all duration-300 ease-in-out hover:scale-105"
>
<Image
quality={100}
className="size-16"
@ -26,8 +29,8 @@ export const Header = (): JSX.Element => {
<strong className="ml-1 hidden font-headline font-semibold text-primary dark:text-primary-dark sm:block sm:text-xl">
Théo LUDWIG
</strong>
</h1>
</Link>
</Link>
</h1>
<div className="flex justify-between">
<div className="flex flex-col items-center justify-center px-6">
<Link

View File

@ -20,14 +20,11 @@ export const InterestParagraph = (
const { title, description } = props
return (
<>
<p className="my-6 text-center text-gray dark:text-gray-dark">
<strong className="text-lg font-semibold text-primary dark:text-primary-dark">
{title}
</strong>
<br />
<span>{htmlParser(description)}</span>
</p>
</>
<div className="my-6 text-center text-gray dark:text-gray-dark">
<h3 className="text-lg font-semibold text-primary dark:text-primary-dark">
{title}
</h3>
<p className="my-2">{htmlParser(description)}</p>
</div>
)
}

View File

@ -16,8 +16,8 @@ export const Interests = (): JSX.Element => {
return (
<div className="max-w-full">
{paragraphs.map((paragraph, index) => {
return <InterestParagraph key={index} {...paragraph} />
{paragraphs.map((paragraph) => {
return <InterestParagraph key={paragraph.id} {...paragraph} />
})}
<InterestsList />
</div>

View File

@ -11,16 +11,18 @@ export const Repository = (props: RepositoryProps): JSX.Element => {
const { name, description, href } = props
return (
<ShadowContainer className="relative !mb-4 max-h-32 cursor-pointer p-6 transition-transform duration-200 ease-in-out hover:-translate-y-2">
<li>
<a href={href} target="_blank" rel="noopener noreferrer">
<div className="flex">
<GitHubIcon className="mr-2 h-6" />
<span className="font-semibold text-primary dark:text-primary-dark">
{name}
</span>
</div>
<p className="my-4">{description}</p>
<ShadowContainer className="relative !mb-4 max-h-32 cursor-pointer p-6 transition-all duration-300 ease-in-out hover:scale-[1.03]">
<h3 className="flex">
<GitHubIcon className="mr-2 h-6" />
<span className="font-semibold text-primary dark:text-primary-dark">
{name}
</span>
</h3>
<p className="my-4">{description}</p>
</ShadowContainer>
</a>
</ShadowContainer>
</li>
)
}

View File

@ -10,7 +10,7 @@ export const OpenSource = (): JSX.Element => {
<p className="text-center">
{i18n.translate("home.open-source.description")}
</p>
<div className="my-6 grid grid-cols-1 gap-6 md:w-10/12 md:grid-cols-2">
<ul className="my-6 grid grid-cols-1 gap-6 md:w-10/12 md:grid-cols-2">
<Repository
name="nodejs/node"
description="Node.js JavaScript runtime ✨🐢🚀✨"
@ -31,7 +31,7 @@ export const OpenSource = (): JSX.Element => {
description="The React Framework"
href="https://github.com/vercel/next.js/commits?author=theoludwig"
/>
</div>
</ul>
</div>
)
}

View File

@ -13,7 +13,7 @@ export const PortfolioItem = (props: PortfolioItemProps): JSX.Element => {
const { title, description, link, image } = props
return (
<ShadowContainer className="relative cursor-pointer items-center sm:ml-10">
<li>
<a
className="group inline-flex justify-center"
target="_blank"
@ -21,23 +21,25 @@ export const PortfolioItem = (props: PortfolioItemProps): JSX.Element => {
href={link}
aria-label={title}
>
<div className="flex justify-center">
<Image
quality={100}
className="size-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5"
width={300}
height={300}
src={image}
alt={title}
/>
</div>
<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-2xl font-semibold text-primary dark:text-primary-dark">
{title}
</h3>
<p className="mx-4 my-6 font-semibold">{description}</p>
</div>
<ShadowContainer className="relative cursor-pointer items-center sm:ml-10">
<div className="flex justify-center">
<Image
quality={100}
className="size-auto transition-opacity duration-500 group-hover:opacity-20 dark:group-hover:opacity-5"
width={300}
height={300}
src={image}
alt={title}
/>
</div>
<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-2xl font-semibold text-primary dark:text-primary-dark">
{title}
</h3>
<p className="mx-4 my-6 font-semibold">{description}</p>
</div>
</ShadowContainer>
</a>
</ShadowContainer>
</li>
)
}

View File

@ -12,10 +12,10 @@ export const Portfolio = (): JSX.Element => {
}
return (
<div className="flex w-full flex-wrap justify-center px-3">
{items.map((item, index) => {
return <PortfolioItem key={index} {...item} />
<ul className="flex w-full flex-wrap justify-center px-3">
{items.map((item) => {
return <PortfolioItem key={item.title} {...item} />
})}
</div>
</ul>
)
}

View File

@ -30,14 +30,17 @@ export const ProfileList = (props: ProfileListProps): JSX.Element => {
title={i18n.translate("home.about.pronouns")}
value={i18n.translate("home.about.pronouns-value")}
/>
{isMounted ? (
<ProfileItem
title={i18n.translate("home.about.birth-date")}
value={`${BIRTH_DATE_STRING} (${age} ${i18n.translate(
"home.about.years-old",
)})`}
/>
) : null}
<ProfileItem
title={i18n.translate("home.about.birth-date")}
value={
isMounted
? `${BIRTH_DATE_STRING} (${age} ${i18n.translate(
"home.about.years-old",
)})`
: BIRTH_DATE_STRING
}
/>
<ProfileItem
title={i18n.translate("home.about.nationality")}
value="Alsace, France"

View File

@ -13,7 +13,7 @@ export const SocialMediaItem = (props: SocialMediaItemProps): JSX.Element => {
aria-label={ariaLabel}
target="_blank"
rel="noopener noreferrer"
className="relative inline-block bg-transparent"
className="relative inline-block bg-transparent transition-all duration-300 ease-in-out hover:scale-110"
>
{children}
</a>

View File

@ -27,13 +27,13 @@ export const SkillComponent = (props: SkillComponentProps): JSX.Element => {
}
return (
<a
href={skillProperties.link}
className="mx-2 max-w-xl text-primary hover:underline dark:text-primary-dark"
target="_blank"
rel="noopener noreferrer"
>
<div className="text-center">
<li>
<a
href={skillProperties.link}
className="mx-2 flex max-w-xl flex-col items-center justify-center text-center text-primary hover:underline dark:text-primary-dark"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="inline size-16"
quality={100}
@ -43,7 +43,7 @@ export const SkillComponent = (props: SkillComponentProps): JSX.Element => {
src={getImage()}
/>
<p className="mt-1 font-semibold">{skill}</p>
</div>
</a>
</a>
</li>
)
}

View File

@ -18,7 +18,7 @@ export const SkillsSection = (props: SkillsSectionProps): JSX.Element => {
{title}
</h3>
</div>
<div className="flex flex-wrap justify-around">{children}</div>
<ul className="flex flex-wrap justify-around">{children}</ul>
</div>
</div>
</div>

View File

@ -57,8 +57,8 @@ export const Section = (props: SectionProps): JSX.Element => {
</p>
) : null}
<div className="w-full px-3">
<ShadowContainer>
<div className="w-full px-16 py-4 leading-8">{children}</div>
<ShadowContainer className="w-full px-2 py-4 leading-8 sm:px-16">
{children}
</ShadowContainer>
</div>
</section>

File diff suppressed because it is too large Load Diff

View File

@ -13,9 +13,9 @@
"modern-normalize": "2.0.0"
},
"devDependencies": {
"@types/node": "20.12.7",
"date-and-time": "3.1.1",
"vite": "5.2.8",
"@types/node": "20.12.12",
"date-and-time": "3.3.0",
"vite": "5.2.11",
"vite-plugin-html": "3.2.2"
}
}

View File

@ -3,7 +3,6 @@ describe("Page /blog/[slug]", () => {
cy.visit("/blog/hello-world")
cy.get("[data-cy=locale-flag-text]").should("not.exist")
cy.get("main h1").should("have.text", "👋 Hello, world!")
cy.get(".prose a:visible").should("have.attr", "target", "_blank")
})
it("should redirect to /404 if the blog post doesn't exist", () => {

View File

@ -1,6 +1,6 @@
{
"en-US": "English",
"fr-FR": "French",
"en-US": "🇺🇸 English",
"fr-FR": "🇫🇷 French",
"all-rights-reserved": "All rights reserved",
"home": "Home"
}

View File

@ -1,6 +1,6 @@
{
"en-US": "Anglais",
"fr-FR": "Français",
"en-US": "🇺🇸 Anglais",
"fr-FR": "🇫🇷 Français",
"all-rights-reserved": "Tous droits réservés",
"home": "Accueil"
}

5951
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "theoludwig",
"version": "3.2.4",
"version": "3.3.1",
"private": true,
"repository": {
"type": "git",
@ -13,7 +13,8 @@
"scripts": {
"dev": "next dev",
"start": "next start",
"build": "npm run curriculum-vitae:build && next build",
"build": "npm run build:curriculum-vitae && next build",
"build:curriculum-vitae": "node ./curriculum-vitae/build.js",
"lint:commit": "commitlint",
"lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint-cli2",
@ -24,77 +25,72 @@
"test:html-w3c-validator": "start-server-and-test \"start\" \"http://127.0.0.1:3000\" \"html-w3c-validator\"",
"test:e2e": "start-server-and-test \"start\" http://127.0.0.1:3000 \"cypress run\"",
"test:dev": "start-server-and-test \"dev\" \"http://127.0.0.1:3000\" \"cypress open\"",
"curriculum-vitae:build": "node ./curriculum-vitae/build.js",
"release": "semantic-release",
"postinstall": "husky"
},
"dependencies": {
"@fontsource/montserrat": "5.0.17",
"@fontsource/montserrat": "5.0.18",
"@formatjs/intl-localematcher": "0.5.4",
"@fortawesome/fontawesome-svg-core": "6.5.2",
"@fortawesome/free-brands-svg-icons": "6.5.2",
"@fortawesome/free-solid-svg-icons": "6.5.2",
"@fortawesome/react-fontawesome": "0.2.0",
"@fortawesome/react-fontawesome": "0.2.2",
"@giscus/react": "3.0.0",
"clsx": "2.1.0",
"date-and-time": "3.1.1",
"@shikijs/rehype": "1.6.0",
"clsx": "2.1.1",
"date-and-time": "3.3.0",
"gray-matter": "4.0.3",
"html-react-parser": "5.1.10",
"i18n-js": "4.3.2",
"i18n-js": "4.4.3",
"katex": "0.16.10",
"negotiator": "0.6.3",
"next": "14.1.0",
"next-mdx-remote": "4.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"next-mdx-remote": "5.0.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"read-pkg": "9.0.1",
"rehype-katex": "6.0.3",
"rehype-raw": "6.1.1",
"rehype-slug": "5.1.0",
"remark-gfm": "3.0.1",
"remark-math": "5.1.1",
"sharp": "0.33.3",
"shiki": "0.14.7",
"unified": "10.1.2",
"unist-util-visit": "5.0.0",
"rehype-katex": "7.0.0",
"rehype-raw": "7.0.0",
"rehype-slug": "6.0.0",
"remark-gfm": "4.0.0",
"remark-math": "6.0.0",
"sharp": "0.33.4",
"shiki": "1.6.0",
"universal-cookie": "7.1.4"
},
"devDependencies": {
"@commitlint/cli": "19.1.0",
"@commitlint/config-conventional": "19.1.0",
"@commitlint/cli": "19.2.2",
"@commitlint/config-conventional": "19.2.2",
"@saithodev/semantic-release-backmerge": "4.0.1",
"@semantic-release/git": "10.0.1",
"@tailwindcss/typography": "0.5.12",
"@tailwindcss/typography": "0.5.13",
"@total-typescript/ts-reset": "0.5.1",
"@tsconfig/strictest": "2.0.5",
"@types/negotiator": "0.6.3",
"@types/node": "20.12.7",
"@types/react": "18.2.78",
"@types/unist": "3.0.2",
"@typescript-eslint/eslint-plugin": "7.6.0",
"@typescript-eslint/parser": "7.6.0",
"@types/node": "20.12.12",
"@types/react": "18.3.2",
"@typescript-eslint/eslint-plugin": "7.10.0",
"@typescript-eslint/parser": "7.10.0",
"autoprefixer": "10.4.19",
"curriculum-vitae": "file:./curriculum-vitae",
"cypress": "13.7.3",
"cypress": "13.10.0",
"editorconfig-checker": "5.1.5",
"eslint": "8.56.0",
"eslint-config-conventions": "14.1.0",
"eslint": "8.57.0",
"eslint-config-conventions": "14.2.0",
"eslint-config-next": "14.1.0",
"eslint-config-prettier": "9.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-prettier": "5.1.3",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-tailwindcss": "3.14.2",
"eslint-plugin-unicorn": "51.0.1",
"html-w3c-validator": "1.6.1",
"eslint-plugin-tailwindcss": "3.17.0",
"eslint-plugin-unicorn": "53.0.0",
"html-w3c-validator": "1.6.2",
"husky": "9.0.11",
"lint-staged": "15.2.2",
"lint-staged": "15.2.4",
"markdownlint-cli2": "0.13.0",
"markdownlint-rule-relative-links": "2.3.2",
"postcss": "8.4.38",
"prettier": "3.2.5",
"prettier-plugin-tailwindcss": "0.5.13",
"semantic-release": "23.0.8",
"prettier-plugin-tailwindcss": "0.5.14",
"semantic-release": "23.1.1",
"start-server-and-test": "2.0.3",
"tailwindcss": "3.4.3",
"typescript": "5.4.5"