feat: add syntax highlighting for code blocks in blog posts

This commit is contained in:
Mauricio Siu
2025-03-02 15:50:36 -06:00
parent 01d974688e
commit 2f6455027a
6 changed files with 533 additions and 27 deletions

View File

@@ -6,10 +6,12 @@ import Image from "next/image";
import Link from "next/link";
import { notFound } from "next/navigation";
import type { DetailedHTMLProps, HTMLAttributes } from "react";
import type React from "react";
import ReactMarkdown from "react-markdown";
import type { Components } from "react-markdown";
import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";
import { CodeBlock } from "../components/CodeBlock";
import { ZoomableImage } from "./components/ZoomableImage";
type Props = {
@@ -54,6 +56,13 @@ export async function generateStaticParams() {
}));
}
interface CodeProps
extends DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> {
inline?: boolean;
className?: string;
children?: React.ReactNode;
}
export default async function BlogPostPage({ params }: Props) {
const { locale, slug } = params;
const t = await getTranslations({ locale, namespace: "blog" });
@@ -83,12 +92,16 @@ export default async function BlogPostPage({ params }: Props) {
h3: ({ node, ...props }) => (
<h3 className="text-xl text-primary/90 font-bold mt-4 mb-2" {...props} />
),
p: ({ node, ...props }) => (
<p
className="text-base text-muted-foreground leading-relaxed mb-4"
{...props}
/>
),
p: ({ node, children, ...props }) => {
return (
<p
className="text-base text-muted-foreground leading-relaxed mb-4"
{...props}
>
{children}
</p>
);
},
a: ({ node, href, ...props }) => (
<a
href={href}
@@ -138,6 +151,63 @@ export default async function BlogPostPage({ params }: Props) {
{src && <ZoomableImage src={src} alt={alt || ""} />}
</div>
),
code: ({ inline, className, children, ...props }: CodeProps) => {
if (inline) {
return (
<code
className="px-1.5 py-0.5 rounded-md bg-muted font-mono text-sm"
{...props}
>
{children}
</code>
);
}
const match = /language-(\w+)/.exec(className || "");
// Extraer el contenido del código de la estructura anidada
const extractCodeContent = (children: React.ReactNode): string => {
if (typeof children === "string") {
return children;
}
if (Array.isArray(children)) {
return children
.map((child) => {
if (typeof child === "string") {
return child;
}
if (child && typeof child === "object" && "props" in child) {
return extractCodeContent(child.props.children);
}
return "";
})
.join("");
}
if (children && typeof children === "object" && "props" in children) {
return extractCodeContent(children.props.children);
}
return "";
};
const codeContent = extractCodeContent(children)
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
.replace(/&amp;/g, "&")
.trim();
// Wrap CodeBlock in a div to prevent it from being inside a p tag
return (
<div className="not-prose my-6">
<CodeBlock
code={codeContent}
language={match ? match[1] : "text"}
className="my-6"
/>
</div>
);
},
};
return (

View File

@@ -0,0 +1,140 @@
"use client";
import * as xmlPlugin from "@prettier/plugin-xml";
import * as prettier from "prettier";
import { Highlight, themes } from "prism-react-renderer";
import { useEffect, useState } from "react";
interface CodeBlockProps {
code: string;
language: string;
className?: string;
}
export function CodeBlock({ code, language, className = "" }: CodeBlockProps) {
const [formattedCode, setFormattedCode] = useState(code);
useEffect(() => {
const formatCode = async () => {
try {
// Determine the parser based on the language
let parser = language;
let plugins: any[] = [];
// Map common languages to their appropriate parsers
switch (language.toLowerCase()) {
case "tsx":
case "ts":
case "typescript":
parser = "babel-ts";
plugins = ["@babel/plugin-syntax-typescript"];
break;
case "jsx":
case "js":
case "javascript":
parser = "babel";
break;
case "html":
case "xml":
case "svg":
parser = "html";
plugins = [xmlPlugin];
break;
case "json":
parser = "json";
break;
case "css":
case "scss":
case "less":
parser = "css";
break;
default:
// For unknown languages, just clean up the whitespace
setFormattedCode(code.trim());
return;
}
const formatted = await prettier.format(code, {
parser,
plugins,
semi: true,
singleQuote: false,
tabWidth: 2,
useTabs: false,
printWidth: 80,
});
setFormattedCode(formatted.trim());
} catch (error) {
console.warn("Error formatting code:", error);
// If formatting fails, just clean up the whitespace
setFormattedCode(code.trim());
}
};
formatCode();
}, [code, language]);
// Ensure we're working with a string and remove any potential duplicate line breaks
const processedCode =
typeof formattedCode === "string"
? formattedCode.replace(/\n+/g, "\n")
: typeof formattedCode === "object"
? JSON.stringify(formattedCode, null, 2)
: String(formattedCode);
return (
<Highlight
theme={themes.dracula}
code={processedCode}
language={language.toLowerCase()}
>
{({
className: preClassName,
style,
tokens,
getLineProps,
getTokenProps,
}) => (
<div className="relative group">
<pre
className={`${preClassName} ${className} overflow-x-auto p-4 rounded-lg text-[13px] leading-[1.5] font-mono`}
style={{
...style,
backgroundColor: "rgb(40, 42, 54)", // Dracula background
}}
>
<div className="absolute top-3 right-3 opacity-0 group-hover:opacity-100 transition-opacity">
<button
type="button"
onClick={() => {
navigator.clipboard.writeText(processedCode);
}}
className="px-2 py-1 text-xs rounded bg-primary/10 text-primary hover:bg-primary/20 transition-colors"
>
Copy
</button>
</div>
{tokens.map((line, i) => (
<div key={i} {...getLineProps({ line })} className="table-row">
<span
className="table-cell text-right pr-4 select-none w-[2.5em] text-zinc-500 text-xs"
style={{
color: "rgb(98, 114, 164)", // Dracula comment color
}}
>
{i + 1}
</span>
<span className="table-cell">
{line.map((token, key) => (
<span key={key} {...getTokenProps({ token })} />
))}
</span>
</div>
))}
</pre>
</div>
)}
</Highlight>
);
}

View File

@@ -97,6 +97,7 @@ export async function getPost(slug: string): Promise<Post | null> {
slug,
include: ["authors"],
})) as Post;
return result;
} catch (error) {
console.error("Error fetching post:", error);

View File

@@ -12,7 +12,6 @@
},
"browserslist": "defaults, not ie <= 11",
"dependencies": {
"react-photo-view": "^1.2.7",
"@headlessui/react": "^1.7.17",
"@headlessui/tailwindcss": "^0.2.0",
"@radix-ui/react-accordion": "^1.2.1",
@@ -37,16 +36,21 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-ga4": "^2.1.0",
"react-photo-view": "^1.2.7",
"tailwind-merge": "^2.2.2",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"typescript": "5.1.6"
},
"devDependencies": {
"@babel/core": "^7.26.9",
"@babel/parser": "^7.26.9",
"@babel/plugin-syntax-typescript": "^7.25.9",
"@biomejs/biome": "1.7.0",
"@prettier/plugin-xml": "^3.4.1",
"@types/react": "18.3.5",
"@types/react-dom": "18.3.0",
"prettier": "^3.0.1",
"prettier-plugin-tailwindcss": "^0.5.2"
"prettier": "^3.3.3",
"prettier-plugin-tailwindcss": "^0.5.14"
}
}

View File

@@ -43,6 +43,7 @@
"@types/react-dom": "18.3.0"
},
"dependencies": {
"prism-react-renderer": "^2.4.1",
"react-markdown": "^10.0.0",
"rehype-raw": "^7.0.0",
"remark-gfm": "^4.0.1"

326
pnpm-lock.yaml generated
View File

@@ -12,6 +12,9 @@ importers:
.:
dependencies:
prism-react-renderer:
specifier: ^2.4.1
version: 2.4.1(react@18.3.1)
react-markdown:
specifier: ^10.0.0
version: 10.0.0(@types/react@18.3.5)(react@18.3.1)
@@ -160,10 +163,10 @@ importers:
version: 0.364.0(react@18.2.0)
next:
specifier: 14.2.2
version: 14.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
version: 14.2.2(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
next-intl:
specifier: ^3.19.0
version: 3.19.0(next@14.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
version: 3.19.0(next@14.2.2(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0)
react:
specifier: 18.2.0
version: 18.2.0
@@ -189,9 +192,21 @@ importers:
specifier: 5.1.6
version: 5.1.6
devDependencies:
'@babel/core':
specifier: ^7.26.9
version: 7.26.9
'@babel/parser':
specifier: ^7.26.9
version: 7.26.9
'@babel/plugin-syntax-typescript':
specifier: ^7.25.9
version: 7.25.9(@babel/core@7.26.9)
'@biomejs/biome':
specifier: 1.7.0
version: 1.7.0
'@prettier/plugin-xml':
specifier: ^3.4.1
version: 3.4.1(prettier@3.3.3)
'@types/react':
specifier: 18.3.5
version: 18.3.5
@@ -199,10 +214,10 @@ importers:
specifier: 18.3.0
version: 18.3.0
prettier:
specifier: ^3.0.1
specifier: ^3.3.3
version: 3.3.3
prettier-plugin-tailwindcss:
specifier: ^0.5.2
specifier: ^0.5.14
version: 0.5.14(prettier@3.3.3)
packages:
@@ -211,6 +226,10 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
'@ampproject/remapping@2.3.0':
resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
engines: {node: '>=6.0.0'}
'@apidevtools/json-schema-ref-parser@11.7.2':
resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==}
engines: {node: '>= 16'}
@@ -219,14 +238,87 @@ packages:
resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
engines: {node: '>=6.9.0'}
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
'@babel/compat-data@7.26.8':
resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==}
engines: {node: '>=6.9.0'}
'@babel/core@7.26.9':
resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==}
engines: {node: '>=6.9.0'}
'@babel/generator@7.26.9':
resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==}
engines: {node: '>=6.9.0'}
'@babel/helper-compilation-targets@7.26.5':
resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-imports@7.25.9':
resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
engines: {node: '>=6.9.0'}
'@babel/helper-module-transforms@7.26.0':
resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0
'@babel/helper-plugin-utils@7.26.5':
resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==}
engines: {node: '>=6.9.0'}
'@babel/helper-string-parser@7.25.9':
resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.24.7':
resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-identifier@7.25.9':
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
'@babel/helper-validator-option@7.25.9':
resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
engines: {node: '>=6.9.0'}
'@babel/helpers@7.26.9':
resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==}
engines: {node: '>=6.9.0'}
'@babel/highlight@7.24.7':
resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==}
engines: {node: '>=6.9.0'}
'@babel/parser@7.26.9':
resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==}
engines: {node: '>=6.0.0'}
hasBin: true
'@babel/plugin-syntax-typescript@7.25.9':
resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==}
engines: {node: '>=6.9.0'}
peerDependencies:
'@babel/core': ^7.0.0-0
'@babel/template@7.26.9':
resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==}
engines: {node: '>=6.9.0'}
'@babel/traverse@7.26.9':
resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==}
engines: {node: '>=6.9.0'}
'@babel/types@7.26.9':
resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==}
engines: {node: '>=6.9.0'}
'@biomejs/biome@1.7.0':
resolution: {integrity: sha512-mejiRhnAq6UrXtYvjWJUKdstcT58n0/FfKemFf3d2Ou0HxOdS88HQmWtQ/UgyZvOEPD572YbFTb6IheyROpqkw==}
engines: {node: '>=14.21.3'}
@@ -1004,6 +1096,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@prettier/plugin-xml@3.4.1':
resolution: {integrity: sha512-Uf/6/+9ez6z/IvZErgobZ2G9n1ybxF5BhCd7eMcKqfoWuOzzNUxBipNo3QAP8kRC1VD18TIo84no7LhqtyDcTg==}
peerDependencies:
prettier: ^3.0.0
'@radix-ui/number@1.1.0':
resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==}
@@ -1551,6 +1648,9 @@ packages:
'@types/node@22.9.0':
resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==}
'@types/prismjs@1.26.5':
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
'@types/prop-types@15.7.12':
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
@@ -1569,6 +1669,9 @@ packages:
'@ungap/structured-clone@1.2.0':
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
'@xml-tools/parser@1.0.11':
resolution: {integrity: sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==}
JSONStream@1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
hasBin: true
@@ -1729,6 +1832,9 @@ packages:
character-reference-invalid@2.0.1:
resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
chevrotain@7.1.1:
resolution: {integrity: sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==}
chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
@@ -1823,6 +1929,9 @@ packages:
engines: {node: '>=16'}
hasBin: true
convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cosmiconfig-typescript-loader@5.0.0:
resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==}
engines: {node: '>=v16'}
@@ -2144,6 +2253,10 @@ packages:
function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
gensync@1.0.0-beta.2:
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
engines: {node: '>=6.9.0'}
get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
@@ -2195,6 +2308,10 @@ packages:
resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
engines: {node: '>=18'}
globals@11.12.0:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
gopd@1.2.0:
resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
engines: {node: '>= 0.4'}
@@ -2389,6 +2506,11 @@ packages:
resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
hasBin: true
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
hasBin: true
json-parse-even-better-errors@2.3.1:
resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
@@ -2398,6 +2520,11 @@ packages:
json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
hasBin: true
jsonparse@1.3.1:
resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
engines: {'0': node >= 0.2.0}
@@ -2474,6 +2601,9 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
lucide-react@0.364.0:
resolution: {integrity: sha512-eHfdbJExWtTaZ0tBMGtI7PA/MbqV5wt+o4/yitDce17tadH/75Gq3Tq8jSteb3LhLr0eay/j5YUuN4yXjnI3aw==}
peerDependencies:
@@ -2982,6 +3112,11 @@ packages:
engines: {node: '>=14'}
hasBin: true
prism-react-renderer@2.4.1:
resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==}
peerDependencies:
react: '>=16.0.0'
property-information@6.5.0:
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
@@ -3108,6 +3243,9 @@ packages:
regex@4.4.0:
resolution: {integrity: sha512-uCUSuobNVeqUupowbdZub6ggI5/JZkYyJdDogddJr60L764oxC2pMZov1fQ3wM9bdyzUILDG+Sqx6NAKAz9rKQ==}
regexp-to-ast@0.5.0:
resolution: {integrity: sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==}
rehype-raw@7.0.0:
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
@@ -3182,9 +3320,8 @@ packages:
resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
engines: {node: '>=4'}
semver@7.6.2:
resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==}
engines: {node: '>=10'}
semver@6.3.1:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
semver@7.6.3:
@@ -3494,6 +3631,9 @@ packages:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
yaml@2.4.5:
resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==}
engines: {node: '>= 14'}
@@ -3521,6 +3661,11 @@ snapshots:
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
dependencies:
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
'@apidevtools/json-schema-ref-parser@11.7.2':
dependencies:
'@jsdevtools/ono': 7.1.3
@@ -3530,16 +3675,121 @@ snapshots:
'@babel/code-frame@7.24.7':
dependencies:
'@babel/highlight': 7.24.7
picocolors: 1.0.1
picocolors: 1.1.1
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
js-tokens: 4.0.0
picocolors: 1.1.1
'@babel/compat-data@7.26.8': {}
'@babel/core@7.26.9':
dependencies:
'@ampproject/remapping': 2.3.0
'@babel/code-frame': 7.26.2
'@babel/generator': 7.26.9
'@babel/helper-compilation-targets': 7.26.5
'@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9)
'@babel/helpers': 7.26.9
'@babel/parser': 7.26.9
'@babel/template': 7.26.9
'@babel/traverse': 7.26.9
'@babel/types': 7.26.9
convert-source-map: 2.0.0
debug: 4.3.7
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
transitivePeerDependencies:
- supports-color
'@babel/generator@7.26.9':
dependencies:
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@jridgewell/gen-mapping': 0.3.5
'@jridgewell/trace-mapping': 0.3.25
jsesc: 3.1.0
'@babel/helper-compilation-targets@7.26.5':
dependencies:
'@babel/compat-data': 7.26.8
'@babel/helper-validator-option': 7.25.9
browserslist: 4.24.2
lru-cache: 5.1.1
semver: 6.3.1
'@babel/helper-module-imports@7.25.9':
dependencies:
'@babel/traverse': 7.26.9
'@babel/types': 7.26.9
transitivePeerDependencies:
- supports-color
'@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-module-imports': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@babel/traverse': 7.26.9
transitivePeerDependencies:
- supports-color
'@babel/helper-plugin-utils@7.26.5': {}
'@babel/helper-string-parser@7.25.9': {}
'@babel/helper-validator-identifier@7.24.7': {}
'@babel/helper-validator-identifier@7.25.9': {}
'@babel/helper-validator-option@7.25.9': {}
'@babel/helpers@7.26.9':
dependencies:
'@babel/template': 7.26.9
'@babel/types': 7.26.9
'@babel/highlight@7.24.7':
dependencies:
'@babel/helper-validator-identifier': 7.24.7
chalk: 2.4.2
js-tokens: 4.0.0
picocolors: 1.0.1
picocolors: 1.1.1
'@babel/parser@7.26.9':
dependencies:
'@babel/types': 7.26.9
'@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.9)':
dependencies:
'@babel/core': 7.26.9
'@babel/helper-plugin-utils': 7.26.5
'@babel/template@7.26.9':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/parser': 7.26.9
'@babel/types': 7.26.9
'@babel/traverse@7.26.9':
dependencies:
'@babel/code-frame': 7.26.2
'@babel/generator': 7.26.9
'@babel/parser': 7.26.9
'@babel/template': 7.26.9
'@babel/types': 7.26.9
debug: 4.3.7
globals: 11.12.0
transitivePeerDependencies:
- supports-color
'@babel/types@7.26.9':
dependencies:
'@babel/helper-string-parser': 7.25.9
'@babel/helper-validator-identifier': 7.25.9
'@biomejs/biome@1.7.0':
optionalDependencies:
@@ -3653,7 +3903,7 @@ snapshots:
'@commitlint/is-ignored@19.2.2':
dependencies:
'@commitlint/types': 19.0.3
semver: 7.6.2
semver: 7.6.3
'@commitlint/lint@19.2.2':
dependencies:
@@ -4144,6 +4394,11 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@prettier/plugin-xml@3.4.1(prettier@3.3.3)':
dependencies:
'@xml-tools/parser': 1.0.11
prettier: 3.3.3
'@radix-ui/number@1.1.0': {}
'@radix-ui/primitive@1.1.0': {}
@@ -5011,6 +5266,8 @@ snapshots:
dependencies:
undici-types: 6.19.8
'@types/prismjs@1.26.5': {}
'@types/prop-types@15.7.12': {}
'@types/react-dom@18.3.0':
@@ -5028,6 +5285,10 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@xml-tools/parser@1.0.11':
dependencies:
chevrotain: 7.1.1
JSONStream@1.3.5:
dependencies:
jsonparse: 1.3.1
@@ -5180,6 +5441,10 @@ snapshots:
character-reference-invalid@2.0.1: {}
chevrotain@7.1.1:
dependencies:
regexp-to-ast: 0.5.0
chokidar@3.6.0:
dependencies:
anymatch: 3.1.3
@@ -5281,6 +5546,8 @@ snapshots:
meow: 12.1.1
split2: 4.2.0
convert-source-map@2.0.0: {}
cosmiconfig-typescript-loader@5.0.0(@types/node@20.17.16)(cosmiconfig@9.0.0(typescript@5.6.3))(typescript@5.6.3):
dependencies:
'@types/node': 20.17.16
@@ -5676,6 +5943,8 @@ snapshots:
function-bind@1.1.2: {}
gensync@1.0.0-beta.2: {}
get-caller-file@2.0.5: {}
get-east-asian-width@1.2.0: {}
@@ -5735,6 +6004,8 @@ snapshots:
dependencies:
ini: 4.1.1
globals@11.12.0: {}
gopd@1.2.0: {}
graceful-fs@4.2.11: {}
@@ -5981,6 +6252,8 @@ snapshots:
dependencies:
argparse: 2.0.1
jsesc@3.1.0: {}
json-parse-even-better-errors@2.3.1: {}
json-pointer@0.6.2:
@@ -5989,6 +6262,8 @@ snapshots:
json-schema-traverse@1.0.0: {}
json5@2.2.3: {}
jsonparse@1.3.1: {}
kind-of@6.0.3: {}
@@ -6063,6 +6338,10 @@ snapshots:
lru-cache@10.4.3: {}
lru-cache@5.1.1:
dependencies:
yallist: 3.1.1
lucide-react@0.364.0(react@18.2.0):
dependencies:
react: 18.2.0
@@ -6558,11 +6837,11 @@ snapshots:
negotiator@1.0.0: {}
next-intl@3.19.0(next@14.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
next-intl@3.19.0(next@14.2.2(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0))(react@18.2.0):
dependencies:
'@formatjs/intl-localematcher': 0.5.4
negotiator: 0.6.3
next: 14.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
next: 14.2.2(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0
use-intl: 3.19.0(react@18.2.0)
@@ -6571,7 +6850,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
next@14.2.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
next@14.2.2(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@next/env': 14.2.2
'@swc/helpers': 0.5.5
@@ -6581,7 +6860,7 @@ snapshots:
postcss: 8.4.31
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
styled-jsx: 5.1.1(@babel/core@7.26.9)(react@18.2.0)
optionalDependencies:
'@next/swc-darwin-arm64': 14.2.2
'@next/swc-darwin-x64': 14.2.2
@@ -6767,6 +7046,12 @@ snapshots:
prettier@3.3.3: {}
prism-react-renderer@2.4.1(react@18.3.1):
dependencies:
'@types/prismjs': 1.26.5
clsx: 2.1.1
react: 18.3.1
property-information@6.5.0: {}
property-information@7.0.0: {}
@@ -6931,6 +7216,8 @@ snapshots:
regex@4.4.0: {}
regexp-to-ast@0.5.0: {}
rehype-raw@7.0.0:
dependencies:
'@types/hast': 3.0.4
@@ -7048,10 +7335,9 @@ snapshots:
extend-shallow: 2.0.1
kind-of: 6.0.3
semver@7.6.2: {}
semver@6.3.1: {}
semver@7.6.3:
optional: true
semver@7.6.3: {}
sharp@0.33.5:
dependencies:
@@ -7169,10 +7455,12 @@ snapshots:
dependencies:
inline-style-parser: 0.2.3
styled-jsx@5.1.1(react@18.2.0):
styled-jsx@5.1.1(@babel/core@7.26.9)(react@18.2.0):
dependencies:
client-only: 0.0.1
react: 18.2.0
optionalDependencies:
'@babel/core': 7.26.9
styled-jsx@5.1.6(react@18.3.1):
dependencies:
@@ -7430,6 +7718,8 @@ snapshots:
y18n@5.0.8: {}
yallist@3.1.1: {}
yaml@2.4.5: {}
yargs-parser@21.1.1: {}