From 2ce3119355ddda46bb15d0c9ffcdccc4d9e5eed1 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 16:40:50 +0100 Subject: [PATCH 01/12] Projects page --- src/app/page.tsx | 40 +--- src/app/projects/page.tsx | 378 ++++++++++++++++++++++++++++++++++++++ src/components/Web.tsx | 166 +++++++++++++++++ 3 files changed, 547 insertions(+), 37 deletions(-) create mode 100644 src/app/projects/page.tsx create mode 100644 src/components/Web.tsx diff --git a/src/app/page.tsx b/src/app/page.tsx index bb69380..d42c311 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -14,6 +14,7 @@ import { Lock, Sparkles, } from "lucide-react"; +import WebStuff from "@/components/Web"; export default function HomePage() { const [mounted, setMounted] = useState(false); @@ -86,7 +87,7 @@ export default function HomePage() { }; return ( -
+ {/* Animated background */}
- - {/* Footer */} -
-
-
- Logo - -
- © 2025 ORUS. Open source for everyone. Built with 💜 for - developers. -
- -
- - - - - - -
-
-
-
-
+ ); } diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx new file mode 100644 index 0000000..1e1482e --- /dev/null +++ b/src/app/projects/page.tsx @@ -0,0 +1,378 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { motion } from "framer-motion"; +import { + Github, + MessageSquare, + Shield, + Code2, + Rocket, + Lock, + Sparkles, + ExternalLink, + Star, + GitFork, + Terminal, + Globe, + Zap, + Package, +} from "lucide-react"; +import WebStuff from "@/components/Web"; + +export default function ProjectsPage() { + const [mounted, setMounted] = useState(false); + const [activeFilter, setActiveFilter] = useState("all"); + + useEffect(() => { + setMounted(true); + }, []); + + const projects = [ + { + title: "SecureAuth Pro", + description: + "Enterprise-grade authentication system with zero-trust architecture and advanced threat detection.", + category: "security", + icon: Shield, + stars: 2450, + forks: 340, + tech: ["TypeScript", "Node.js", "PostgreSQL"], + status: "Active", + github: "https://github.com/orus-dev", + demo: "#", + }, + { + title: "DevOps Toolkit", + description: + "Complete CI/CD automation suite with container orchestration and infrastructure as code.", + category: "devtools", + icon: Terminal, + stars: 1820, + forks: 290, + tech: ["Go", "Docker", "Kubernetes"], + status: "Active", + github: "https://github.com/orus-dev", + }, + { + title: "CloudSync API", + description: + "Real-time data synchronization service with end-to-end encryption and multi-region support.", + category: "infrastructure", + icon: Globe, + stars: 980, + forks: 145, + tech: ["Rust", "Redis", "GraphQL"], + status: "Beta", + github: "https://github.com/orus-dev", + demo: "#", + }, + { + title: "CodeGuard Scanner", + description: + "Automated code security scanner with AI-powered vulnerability detection and remediation suggestions.", + category: "security", + icon: Lock, + stars: 3200, + forks: 480, + tech: ["Python", "TensorFlow", "FastAPI"], + status: "Active", + github: "https://github.com/orus-dev", + }, + { + title: "MicroFramework", + description: + "Lightweight, blazing-fast microservices framework with built-in service mesh and observability.", + category: "devtools", + icon: Zap, + stars: 1560, + forks: 210, + tech: ["TypeScript", "gRPC", "Prometheus"], + status: "Active", + github: "https://github.com/orus-dev", + demo: "#", + }, + { + title: "PackageHub", + description: + "Private package registry with caching, versioning, and dependency analysis for modern development.", + category: "infrastructure", + icon: Package, + stars: 720, + forks: 95, + tech: ["Node.js", "MongoDB", "S3"], + status: "Beta", + github: "https://github.com/orus-dev", + }, + ]; + + const filters = [ + { id: "all", label: "All Projects" }, + { id: "security", label: "Security" }, + { id: "devtools", label: "Dev Tools" }, + { id: "infrastructure", label: "Infrastructure" }, + ]; + + const filteredProjects = + activeFilter === "all" + ? projects + : projects.filter((p) => p.category === activeFilter); + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { + duration: 0.5, + }, + }, + }; + + return ( + + {/* Hero Section */} + +
+ + + + Open Source Projects + + + + + + Building the Future + + + + + Explore our collection of open-source projects designed to empower + developers and enhance security across the ecosystem. + + + {/* Filter Tabs */} + + {filters.map((filter) => ( + + ))} + +
+
+ + {/* Projects Grid */} + +
+ {filteredProjects.map((project, i) => ( + +
+
+ {/* Header */} +
+
+ +
+ + {project.status} + +
+ + {/* Content */} +

+ {project.title} +

+

+ {project.description} +

+ + {/* Stats */} +
+
+ + {project.stars.toLocaleString()} +
+
+ + {project.forks} +
+
+ + {/* Tech Stack */} +
+ {project.tech.map((tech, idx) => ( + + {tech} + + ))} +
+ + {/* Actions */} +
+ + + Code + + {project.demo && ( + + + Demo + + )} +
+
+ + ))} +
+ + + {/* Stats Section */} + + +
+ {[ + { label: "Projects", value: "24+" }, + { label: "Contributors", value: "180+" }, + { label: "Stars", value: "12.5K+" }, + { label: "Forks", value: "2.1K+" }, + ].map((stat, i) => ( + +
+ {stat.value} +
+
{stat.label}
+
+ ))} +
+
+
+ + {/* CTA Section */} + + +
+ +
+

+ Want to Contribute? +

+

+ All our projects are open source and welcoming to contributors. + Join us in building tools that make a difference. +

+ +
+ + + View on GitHub + + + + + Join Discord + +
+
+ + + + ); +} diff --git a/src/components/Web.tsx b/src/components/Web.tsx new file mode 100644 index 0000000..d54dfed --- /dev/null +++ b/src/components/Web.tsx @@ -0,0 +1,166 @@ +import { motion } from "framer-motion"; +import { Github, MessageSquare } from "lucide-react"; + +export default function WebStuff({ + children, +}: Readonly<{ + children?: React.ReactNode; +}>) { + return ( +
+ {/* Animated background */} +
+
+
+ + {/* Gradient orbs */} +
+ + +
+ + {/* Navigation */} + + + Logo + + + {/* Menu */} + + {["Blog", "Projects", "About"].map((item) => ( + + {item} + + ))} + + + + Join Discord + + + + + {children} + +
+
+
+ Logo + +
+ © 2025 ORUS. Open source for everyone. Built with 💜 for + developers. +
+ +
+ + + + + + +
+
+
+
+
+ ); +} From 6bb44e2e2e9d35e716e269dc0c6a6c0dc1a08599 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 16:48:58 +0100 Subject: [PATCH 02/12] Small fix navbar --- src/app/page.tsx | 75 ------------------------------------------ src/components/Web.tsx | 9 ++--- 2 files changed, 3 insertions(+), 81 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index d42c311..edf4271 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -88,81 +88,6 @@ export default function HomePage() { return ( - {/* Animated background */} -
-
-
- - {/* Gradient orbs */} -
- - -
- - {/* Navigation */} - - - Logo - - -
- {["Blog", "Projects", "About"].map((item, i) => ( - - {item} - - ))} - - - Join Discord - -
-
- {/* Hero Section */} From 4b43c5dacc8bbe86309b8e3cfd847c7edfc9418b Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 17:05:29 +0100 Subject: [PATCH 03/12] Actual projects --- src/app/projects/page.tsx | 90 ++----------------------------------- src/lib/projects.tsx | 94 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 src/lib/projects.tsx diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 1e1482e..c1904b9 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -5,20 +5,13 @@ import { motion } from "framer-motion"; import { Github, MessageSquare, - Shield, - Code2, - Rocket, - Lock, Sparkles, ExternalLink, Star, GitFork, - Terminal, - Globe, - Zap, - Package, } from "lucide-react"; import WebStuff from "@/components/Web"; +import projects from "@/lib/projects"; export default function ProjectsPage() { const [mounted, setMounted] = useState(false); @@ -28,89 +21,12 @@ export default function ProjectsPage() { setMounted(true); }, []); - const projects = [ - { - title: "SecureAuth Pro", - description: - "Enterprise-grade authentication system with zero-trust architecture and advanced threat detection.", - category: "security", - icon: Shield, - stars: 2450, - forks: 340, - tech: ["TypeScript", "Node.js", "PostgreSQL"], - status: "Active", - github: "https://github.com/orus-dev", - demo: "#", - }, - { - title: "DevOps Toolkit", - description: - "Complete CI/CD automation suite with container orchestration and infrastructure as code.", - category: "devtools", - icon: Terminal, - stars: 1820, - forks: 290, - tech: ["Go", "Docker", "Kubernetes"], - status: "Active", - github: "https://github.com/orus-dev", - }, - { - title: "CloudSync API", - description: - "Real-time data synchronization service with end-to-end encryption and multi-region support.", - category: "infrastructure", - icon: Globe, - stars: 980, - forks: 145, - tech: ["Rust", "Redis", "GraphQL"], - status: "Beta", - github: "https://github.com/orus-dev", - demo: "#", - }, - { - title: "CodeGuard Scanner", - description: - "Automated code security scanner with AI-powered vulnerability detection and remediation suggestions.", - category: "security", - icon: Lock, - stars: 3200, - forks: 480, - tech: ["Python", "TensorFlow", "FastAPI"], - status: "Active", - github: "https://github.com/orus-dev", - }, - { - title: "MicroFramework", - description: - "Lightweight, blazing-fast microservices framework with built-in service mesh and observability.", - category: "devtools", - icon: Zap, - stars: 1560, - forks: 210, - tech: ["TypeScript", "gRPC", "Prometheus"], - status: "Active", - github: "https://github.com/orus-dev", - demo: "#", - }, - { - title: "PackageHub", - description: - "Private package registry with caching, versioning, and dependency analysis for modern development.", - category: "infrastructure", - icon: Package, - stars: 720, - forks: 95, - tech: ["Node.js", "MongoDB", "S3"], - status: "Beta", - github: "https://github.com/orus-dev", - }, - ]; - const filters = [ { id: "all", label: "All Projects" }, { id: "security", label: "Security" }, { id: "devtools", label: "Dev Tools" }, { id: "infrastructure", label: "Infrastructure" }, + { id: "communication", label: "Communication" }, ]; const filteredProjects = @@ -304,7 +220,7 @@ export default function ProjectsPage() { >
{[ - { label: "Projects", value: "24+" }, + { label: "Projects", value: `${projects.length}+` }, { label: "Contributors", value: "180+" }, { label: "Stars", value: "12.5K+" }, { label: "Forks", value: "2.1K+" }, diff --git a/src/lib/projects.tsx b/src/lib/projects.tsx new file mode 100644 index 0000000..7ea4c6f --- /dev/null +++ b/src/lib/projects.tsx @@ -0,0 +1,94 @@ +import { + LucideIcon, + MessageSquare, + NetworkIcon, + ServerIcon, +} from "lucide-react"; + +type OrusProject = { + title: string; + description: string; + category: string; + icon: LucideIcon; + stars: number; + forks: number; + tech: string[]; + status: "Active" | "Beta" | "Alpha" | "Planning"; + github: string; + demo?: string; +}; + +const orusProjects: OrusProject[] = [ + { + title: "sentryx", + description: + "Multi-server management panel with high security — helps to host apps or media securely.", + category: "infrastructure", + icon: ServerIcon, + stars: 1, + forks: 3, + tech: ["TypeScript", "Docker"], + status: "Active", + github: "https://github.com/orus-dev/sentryx", + }, + { + title: "ghostnet", + description: + "Privacy-focused protocol / spec designed to resist censorship and surveillance. ", + category: "security", + icon: NetworkIcon, + stars: 1, + forks: 0, + tech: ["Specs", "P2P"], + status: "Active", + github: "https://github.com/orus-dev/ghostnet", + }, + { + title: "voxa", + description: + "A chat application (client side) — part of ORUS’s communication tools.", + category: "communication", + icon: MessageSquare, + stars: 1, + forks: 3, + tech: ["TypeScript"], + status: "Active", + github: "https://github.com/orus-dev/voxa", + }, + { + title: "ghostnet-rs", + description: "Rust implementation / library for the GhostNet protocol.", + category: "security", + icon: MessageSquare, + stars: 0, + forks: 2, + tech: ["Rust", "P2P"], + status: "Active", + github: "https://github.com/orus-dev/ghostnet-rs", + }, + { + title: "orus", + description: "The official ORUS website / landing page repository.", + category: "infrastructure", + icon: MessageSquare, + stars: 0, + forks: 0, + tech: ["TypeScript"], + status: "Active", + github: "https://github.com/orus-dev/orus", + }, + { + title: "drift", + description: + "Productivity / organization app aiming to help users get rid of distractions.", + category: "devtools", + icon: MessageSquare, + stars: 0, + forks: 2, + tech: ["TypeScript"], + status: "Active", + github: "https://github://github.com/orus-dev/drift", + }, +]; + +export default orusProjects; From efcbb33d3075bef8492070f9ae2bddd0d7304d1d Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 17:33:37 +0100 Subject: [PATCH 04/12] Stats --- src/app/api/stats/route.ts | 42 ++++++++++++++++++++++++++++++++++++++ src/app/projects/page.tsx | 25 ++++++++++++++++++++--- src/lib/formatNumber.tsx | 6 ++++++ 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 src/app/api/stats/route.ts create mode 100644 src/lib/formatNumber.tsx diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts new file mode 100644 index 0000000..613efae --- /dev/null +++ b/src/app/api/stats/route.ts @@ -0,0 +1,42 @@ +import { NextRequest, NextResponse } from "next/server"; + +var lastGitRequest = new Date("2023-01-10T12:00:00Z"); +var stats = { stars: 0, forks: 0, contributors: 0 }; + +async function getStats() { + let page = 0; + let stars = 0; + let forks = 0; + let contributors = 0; + + while (true) { + const res = await fetch( + `https://api.github.com/users/orus-dev/repos?per_page=100&page=${page}` + ); + const repos = await res.json(); + + if (repos.length === 0) break; + + for (const repo of repos) { + stars += repo.stargazers_count; + forks += repo.forks_count; + const conRes = await fetch(repo.contributors_url); + contributors += (await conRes.json()).length; + } + + page++; + } + + return { stars, forks, contributors }; +} + +export async function GET(req: NextRequest) { + const now = new Date(); + + if (now.getTime() - lastGitRequest.getTime() > 60 * 60 * 1000) { + stats = await getStats(); + lastGitRequest = now; + } + + return NextResponse.json(stats); +} diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index c1904b9..52b7473 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -12,15 +12,31 @@ import { } from "lucide-react"; import WebStuff from "@/components/Web"; import projects from "@/lib/projects"; +import formatNumber from "@/lib/formatNumber"; export default function ProjectsPage() { const [mounted, setMounted] = useState(false); const [activeFilter, setActiveFilter] = useState("all"); + const [orgStats, setOrgStats] = useState({ + contributors: 0, + stars: 0, + forks: 0, + }); useEffect(() => { setMounted(true); }, []); + useEffect(() => { + async function fetchStats() { + const res = await fetch(`/api/stats`); + + setOrgStats(await res.json()); + } + + fetchStats(); + }, []); + const filters = [ { id: "all", label: "All Projects" }, { id: "security", label: "Security" }, @@ -221,9 +237,12 @@ export default function ProjectsPage() {
{[ { label: "Projects", value: `${projects.length}+` }, - { label: "Contributors", value: "180+" }, - { label: "Stars", value: "12.5K+" }, - { label: "Forks", value: "2.1K+" }, + { + label: "Contributors", + value: `${formatNumber(orgStats.contributors)}+`, + }, + { label: "Stars", value: `${formatNumber(orgStats.stars)}+` }, + { label: "Forks", value: `${formatNumber(orgStats.forks)}+` }, ].map((stat, i) => ( Date: Wed, 3 Dec 2025 17:38:34 +0100 Subject: [PATCH 05/12] Stats fix --- src/app/api/stats/route.ts | 6 +++++- src/app/page.tsx | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts index 613efae..3b6a74a 100644 --- a/src/app/api/stats/route.ts +++ b/src/app/api/stats/route.ts @@ -3,6 +3,10 @@ import { NextRequest, NextResponse } from "next/server"; var lastGitRequest = new Date("2023-01-10T12:00:00Z"); var stats = { stars: 0, forks: 0, contributors: 0 }; +function isIterable(obj: any): obj is Iterable { + return obj != null && typeof obj[Symbol.iterator] === "function"; +} + async function getStats() { let page = 0; let stars = 0; @@ -15,7 +19,7 @@ async function getStats() { ); const repos = await res.json(); - if (repos.length === 0) break; + if (repos.length === 0 || !isIterable(repos)) break; for (const repo of repos) { stars += repo.stargazers_count; diff --git a/src/app/page.tsx b/src/app/page.tsx index edf4271..3c5921c 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -150,14 +150,14 @@ export default function HomePage() { - - Join Community + View Our Projects +
From 4b029576143c214612084484d75db633fc47f93e Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 17:41:51 +0100 Subject: [PATCH 06/12] Hero action --- src/app/page.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 3c5921c..8de8501 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -133,8 +133,7 @@ export default function HomePage() { className="flex flex-col sm:flex-row gap-4 justify-center" > - View Our Projects - View Our Projects + Follow us on From a8fefa1d2662250715f9e8063b0fbf29ebaa3c06 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 17:47:26 +0100 Subject: [PATCH 07/12] Projects hero --- src/app/projects/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 52b7473..879fee8 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -14,7 +14,7 @@ import WebStuff from "@/components/Web"; import projects from "@/lib/projects"; import formatNumber from "@/lib/formatNumber"; -export default function ProjectsPage() { +export default function Projects() { const [mounted, setMounted] = useState(false); const [activeFilter, setActiveFilter] = useState("all"); const [orgStats, setOrgStats] = useState({ @@ -84,7 +84,7 @@ export default function ProjectsPage() { - Open Source Projects + Community Projects @@ -93,7 +93,7 @@ export default function ProjectsPage() { className="text-5xl lg:text-6xl font-bold mb-6" > - Building the Future + What We're Building @@ -101,8 +101,8 @@ export default function ProjectsPage() { variants={itemVariants} className="text-xl text-slate-300 mb-8 leading-relaxed max-w-3xl mx-auto" > - Explore our collection of open-source projects designed to empower - developers and enhance security across the ecosystem. + Explore our projects designed to improve developers and enhance + security across the ecosystem. {/* Filter Tabs */} From 36ce2b9888ad20e6ee48bec4e946d8a50590e035 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 18:18:57 +0100 Subject: [PATCH 08/12] Blog --- src/app/blog/page.tsx | 203 ++++++++++++++++++++++++++++++++++++++++++ src/lib/posts.tsx | 81 +++++++++++++++++ 2 files changed, 284 insertions(+) create mode 100644 src/app/blog/page.tsx create mode 100644 src/lib/posts.tsx diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx new file mode 100644 index 0000000..7ffbb4b --- /dev/null +++ b/src/app/blog/page.tsx @@ -0,0 +1,203 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { motion } from "framer-motion"; +import { + Github, + MessageSquare, + Sparkles, + ExternalLink, + Star, + GitFork, +} from "lucide-react"; +import WebStuff from "@/components/Web"; +import projects from "@/lib/posts"; +import formatNumber from "@/lib/formatNumber"; + +export default function Blog() { + const [mounted, setMounted] = useState(false); + const [activeFilter, setActiveFilter] = useState("all"); + const [orgStats, setOrgStats] = useState({ + contributors: 0, + stars: 0, + forks: 0, + }); + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + async function fetchStats() { + const res = await fetch(`/api/stats`); + + setOrgStats(await res.json()); + } + + fetchStats(); + }, []); + + const filters = [ + { id: "all", label: "All Blogs" }, + { id: "news", label: "News" }, + { id: "guides", label: "Guides" }, + { id: "research", label: "Research" }, + { id: "journal", label: "Journal" }, + ]; + + const filteredPosts = + activeFilter === "all" + ? projects + : projects.filter((p) => p.category === activeFilter); + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { + duration: 0.5, + }, + }, + }; + + return ( + + {/* Hero Section */} + +
+ + + + Latest Posts & Updates + + + + + + Project Updates + + + + + Latest updates, articles, and guides from our team. + + + {/* Filter Tabs */} + + {filters.map((filter) => ( + + ))} + +
+
+ + {/* Blog Posts List */} + +
+ {filteredPosts.map((post, i) => ( + +
+ {/* Optional Thumbnail */} + {post.image && ( +
+ {post.title} +
+ )} + + {/* Content */} +
+
+

+ {post.title} +

+

+ {post.description} +

+ + {/* Metadata */} +
+ {post.author} + • + {new Date(post.date).toLocaleDateString()} + {post.readingTime && ( + <> + • + {post.readingTime} min read + + )} + {post.tags?.map((tag, idx) => ( + + {tag} + + ))} +
+
+ + {/* Read More */} + + Read More + +
+
+
+ ))} +
+
+
+ ); +} diff --git a/src/lib/posts.tsx b/src/lib/posts.tsx new file mode 100644 index 0000000..cb48051 --- /dev/null +++ b/src/lib/posts.tsx @@ -0,0 +1,81 @@ +type PostPreview = { + title: string; + description: string; + author: string; + date: string; + readingTime: number; + tags: string[]; + image: string; + url: string; + category: "news" | "guides" | "journal" | "research"; +}; + +const posts: PostPreview[] = [ + { + title: "Understanding React Server Components", + description: + "Server components in React allow you to offload work to the server while keeping your UI interactive. Learn how to integrate them into your Next.js app.", + author: "Jane Doe", + date: "2025-11-28", + readingTime: 7, + tags: ["React", "Next.js", "Server"], + image: + "data:image/png;base64,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", + url: "/blog/react-server-components", + category: "guides", + }, + { + title: "A Deep Dive into Tailwind CSS 4.0", + description: + "Tailwind CSS keeps evolving. Discover the new features in version 4.0 and how they can streamline your workflow.", + author: "John Smith", + date: "2025-11-20", + readingTime: 5, + tags: ["CSS", "Tailwind", "Frontend"], + image: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQt89y2XtPE7GhSgcohDk9qdcaf8t5--qwPTg&s", + url: "/blog/tailwind-css-4", + category: "guides", + }, + { + title: "Optimizing Next.js for Performance", + description: + "Performance is key in web apps. Learn practical techniques to make your Next.js application lightning-fast.", + author: "Alice Johnson", + date: "2025-11-15", + readingTime: 8, + tags: ["Next.js", "Performance", "WebDev"], + image: + "https://bs-uploads.toptal.io/blackfish-uploads/components/blog_post_page/6389919/cover_image/regular_1708x683/Next-js-Improving-Page-Speed-of-a-Server-Side-React-App-Blog-7d4de23630c897d49e1c5be730ad5695.png", + url: "/blog/nextjs-performance", + category: "guides", + }, + { + title: "State Management in Modern React", + description: + "From Context API to Zustand and Redux Toolkit, we explore modern strategies for managing state in React apps.", + author: "Robert Lee", + date: "2025-11-10", + readingTime: 6, + tags: ["React", "State Management", "Frontend"], + image: + "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQEm17yo2o8rIW-5nNXHk2J2MhKaTSi4cKZvA&s", + url: "/blog/react-state-management", + category: "guides", + }, + { + title: "Getting Started with TypeScript 6", + description: + "TypeScript 6 brings exciting new type features. This guide helps you get started with the latest version.", + author: "Emma Watson", + date: "2025-11-05", + readingTime: 7, + tags: ["TypeScript", "JavaScript", "Programming"], + image: + "data:image/jpeg;base64,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", + url: "/blog/typescript-6-intro", + category: "guides", + }, +]; + +export default posts; From 0e128b5308019d861d9d43b85187d0df8df26829 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 18:50:37 +0100 Subject: [PATCH 09/12] Simple blog system (not quite done) --- blog/test.json | 10 +++ blog/test.md | 1 + src/app/api/blog/[slug]/route.ts | 45 ++++++++++ src/app/api/blog/route.ts | 28 ++++++ src/app/blog/[slug]/page.tsx | 122 ++++++++++++++++++++++++++ src/app/blog/page.tsx | 26 ++---- src/lib/posts.ts | 11 +++ src/lib/posts.tsx | 81 ----------------- src/lib/{projects.tsx => projects.ts} | 0 9 files changed, 224 insertions(+), 100 deletions(-) create mode 100644 blog/test.json create mode 100644 blog/test.md create mode 100644 src/app/api/blog/[slug]/route.ts create mode 100644 src/app/api/blog/route.ts create mode 100644 src/app/blog/[slug]/page.tsx create mode 100644 src/lib/posts.ts delete mode 100644 src/lib/posts.tsx rename src/lib/{projects.tsx => projects.ts} (100%) diff --git a/blog/test.json b/blog/test.json new file mode 100644 index 0000000..53b15cd --- /dev/null +++ b/blog/test.json @@ -0,0 +1,10 @@ +{ + "title": "My Post", + "description": "This is a short description", + "author": "John Doe", + "date": "2025-12-03", + "readingTime": 1, + "tags": ["typescript", "nextjs"], + "image": "/images/my-post.jpg", + "category": "guides" +} diff --git a/blog/test.md b/blog/test.md new file mode 100644 index 0000000..6769dd6 --- /dev/null +++ b/blog/test.md @@ -0,0 +1 @@ +Hello world! \ No newline at end of file diff --git a/src/app/api/blog/[slug]/route.ts b/src/app/api/blog/[slug]/route.ts new file mode 100644 index 0000000..8072b76 --- /dev/null +++ b/src/app/api/blog/[slug]/route.ts @@ -0,0 +1,45 @@ +// app/api/blog/[slug]/route.ts +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; +import path from "path"; +import { PostPreview } from "@/lib/posts"; + +const BLOG_DIR = path.join(process.cwd(), "blog"); + +export async function GET( + req: NextRequest, + { params }: { params: Promise<{ slug: string }> } +) { + try { + const { slug } = await params; + + // Read JSON metadata + const jsonPath = path.join(BLOG_DIR, `${slug}.json`); + if (!fs.existsSync(jsonPath)) { + return NextResponse.json({ error: "Post not found" }, { status: 404 }); + } + const rawData = fs.readFileSync(jsonPath, "utf-8"); + const data: Omit = JSON.parse(rawData); + + // Read Markdown content + const mdPath = path.join(BLOG_DIR, `${slug}.md`); + let content = ""; + if (fs.existsSync(mdPath)) { + content = fs.readFileSync(mdPath, "utf-8"); + } + + return NextResponse.json({ + meta: { + ...data, + url: `/blog/${slug}`, + }, + content, + }); + } catch (err) { + console.error(err); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 } + ); + } +} diff --git a/src/app/api/blog/route.ts b/src/app/api/blog/route.ts new file mode 100644 index 0000000..be6bdda --- /dev/null +++ b/src/app/api/blog/route.ts @@ -0,0 +1,28 @@ +import { NextRequest, NextResponse } from "next/server"; +import fs from "fs"; +import path from "path"; +import { PostPreview } from "@/lib/posts"; + +const BLOG_DIR = path.join(process.cwd(), "blog"); + +export function getAllBlogPreviews(): PostPreview[] { + const files = fs.readdirSync(BLOG_DIR).filter((f) => f.endsWith(".json")); + + return files.map((file) => { + const filePath = path.join(BLOG_DIR, file); + const rawData = fs.readFileSync(filePath, "utf-8"); + const data = JSON.parse(rawData); + + // Generate the URL based on filename + const slug = file.replace(/\.json$/, ""); + + return { + ...data, + url: `/blog/${slug}`, + } as PostPreview; + }); +} + +export function GET(req: NextRequest): NextResponse { + return NextResponse.json(getAllBlogPreviews()); +} diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx new file mode 100644 index 0000000..0e3472c --- /dev/null +++ b/src/app/blog/[slug]/page.tsx @@ -0,0 +1,122 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { motion } from "framer-motion"; +import { Sparkles } from "lucide-react"; +import WebStuff from "@/components/Web"; +import { useParams } from "next/navigation"; +import { PostPreview } from "@/lib/posts"; + +export default function BlogPost() { + const [post, setPost] = useState<(PostPreview & { content: string }) | null>( + null + ); + const [mounted, setMounted] = useState(false); + const { slug } = useParams(); + + useEffect(() => { + setMounted(true); + }, []); + + useEffect(() => { + async function fetchPost() { + const res = await fetch(`/api/blog/${slug}`); + const data = await res.json(); + setPost(data); + } + + fetchPost(); + }, [slug]); + + if (!post) { + return ( + +
+ Loading post... +
+
+ ); + } + + const containerVariants = { + hidden: { opacity: 0 }, + visible: { opacity: 1, transition: { staggerChildren: 0.1 } }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { y: 0, opacity: 1, transition: { duration: 0.5 } }, + }; + + return ( + + Hero Section + + + + + {post.category} + + + + + + {post.title} + + + + + {post.author} + • + {new Date(post.date).toLocaleDateString()} + {post.readingTime && ( + <> + • + {post.readingTime} min read + + )} + {post.tags?.map((tag, idx) => ( + + {tag} + + ))} + + + {/* Optional Featured Image */} + {post.image && ( + + {post.title} + + )} + + {/* Post Content */} + + + + ); +} diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index 7ffbb4b..a320e63 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -2,39 +2,27 @@ import { useState, useEffect } from "react"; import { motion } from "framer-motion"; -import { - Github, - MessageSquare, - Sparkles, - ExternalLink, - Star, - GitFork, -} from "lucide-react"; +import { Sparkles } from "lucide-react"; import WebStuff from "@/components/Web"; -import projects from "@/lib/posts"; -import formatNumber from "@/lib/formatNumber"; +import { PostPreview } from "@/lib/posts"; export default function Blog() { const [mounted, setMounted] = useState(false); const [activeFilter, setActiveFilter] = useState("all"); - const [orgStats, setOrgStats] = useState({ - contributors: 0, - stars: 0, - forks: 0, - }); + const [projects, setProjects] = useState([]); useEffect(() => { setMounted(true); }, []); useEffect(() => { - async function fetchStats() { - const res = await fetch(`/api/stats`); + async function fetchPosts() { + const res = await fetch(`/api/blog`); - setOrgStats(await res.json()); + setProjects(await res.json()); } - fetchStats(); + fetchPosts(); }, []); const filters = [ diff --git a/src/lib/posts.ts b/src/lib/posts.ts new file mode 100644 index 0000000..7b5257f --- /dev/null +++ b/src/lib/posts.ts @@ -0,0 +1,11 @@ +export type PostPreview = { + title: string; + description: string; + author: string; + date: string; + readingTime: number; + tags: string[]; + image: string; + url: string; + category: "news" | "guides" | "journal" | "research"; +}; diff --git a/src/lib/posts.tsx b/src/lib/posts.tsx deleted file mode 100644 index cb48051..0000000 --- a/src/lib/posts.tsx +++ /dev/null @@ -1,81 +0,0 @@ -type PostPreview = { - title: string; - description: string; - author: string; - date: string; - readingTime: number; - tags: string[]; - image: string; - url: string; - category: "news" | "guides" | "journal" | "research"; -}; - -const posts: PostPreview[] = [ - { - title: "Understanding React Server Components", - description: - "Server components in React allow you to offload work to the server while keeping your UI interactive. Learn how to integrate them into your Next.js app.", - author: "Jane Doe", - date: "2025-11-28", - readingTime: 7, - tags: ["React", "Next.js", "Server"], - image: - "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAS0AAACnCAMAAABzYfrWAAABiVBMVEUfICbtHpAyxf////8Akf/6YwD8VQD/agAgJi0gKS8jPUUAAAAiNDz/7Of6VwAUx80pYm4e3LUd2rcc17sVycsXzcYAGicJsOcXHyZRLSN5OR+XQhsa078p85ob1b0Y0MMOutwk6aYq+Jkh4q4Wy8grxZAm7KInTV8oUlkAIBoRwdQj56gMtuHv///WHoMi5KwbmMMgFADxQqAOd9ARwNUNuN4KsuUGsO3/hVUn75/yUKX+aBkTxdAAjv9utv8jLz8AAA0tlaTz9/8vmp/o/PVlIEYTFRzZ2dpJIDj/7/huz/DFxcYzzP//3u3N9P9F0tjP//AUZa5paWzQ+vu1/+KBgYNs8bw7PEDr6+vQ0NEfGBu0+uez8+8tk6eenqB4eHqWlphSU1YeAAAor38jo4wgmZexsbJCQ0cjoY4bialP6LuX/tAmaYYmqYWw5P//n4H/0L0ofKAmZIAXVY8totJpNCBAKCQiRVgor3xc87Cr+dHM/OPC6f+W3f+4/Nd99rr/3ND/sZ4sgolrgGENAAALFklEQVR4nO2diX/cRhWA1VVSVp4lV5M4QNwcbR2TBuK0OTBQMhh3hkYmaQWypIXqMEVLcWJwMATnhP7lzBtpL10r7a51xPPlt5d2Vrv6/N7oaXREkgQCgUAgEBwShFGw/WH9lPpj9BiGhDMbDQ1hQ9N6eG6+GqYeURkwbZzRSHFdnS8VkVTe2stqXQCiuW5vPrMqhdCWLIMugsOoITj4mwcTiMEEIf6GGbb2yKA1e4D20vCODOYSNhhMik5BTL4z+sU1B2zpPZ8JQETSfReiBhuu7/bgiWf7roHbNrMFMYB9eKJZskyRhDXf19sEwqOt+Tohuq1h4riuodkQiUTRfV9jc3Fdh+j8GczPkfhHJM122SzYt/sOm2D7uoErFZELsKUhzGwRjweOj7EuB0+Qz584Cn/QiIQgqhAhpmkq2AqmQiMbmkOO+ohNhU9RZoC/b2GJvbKCGftBGkNEwUfMYB4UBRGr46plTCSILfjt8JthkfU2BBB74TlsKjjowZ3Kkg+0YfgQQjzMYDpXwBcX7ggsPyS3C38A1YQMH7zH5kdt9gxsBY3ga6jrspbsJlftYjKDfsv1WCBomilT3PM8iDPIOB2xxXDAH0tUCfovLPE1GWYNDIgkDWIriFA2SWFTXPiMxdRYiHd4MrRk7/Vgfhr7w3gqNLIhFNUwOqmueQ3o7vu2bKSH2mTkB5kBnUqPYBZHvb4liBD2wHTCUx5gOtxgqb2+LQ97EDEQVhBXzJZJwGuvv4qAj2hYC2052Aj/XrhiF5PhUcF+vYpYMKiO7jgaJIyjBrGlIc2ytP46kScrQh4kIIiDhY3bcmBWlg7zlMZsQdKzL3AMmDl2BrYQ0n0armjrDbcl8Z8KHTDLQJWliAOlgsbCw9ShAwNbLmsMPY9sWjwU2b0K4WjEbPHP+BAw4Fwd2PLYjG1o7o3ZUuGlDl/YEFvQh1C44wnohN2yg4PUURF/AsuCwzYWJr1+BvvQCY3ZknlPFTY1yMBWWK1R1Lel8vRXvXCmtZfFSihKPUIsdo801TRVDyOXUt+hVCPEpiZlK3bSs6ipwcLwNlRHULL61LRY8WFT6iB210MqpW3e75uqwcoNxzKprxCJUjX8GgzzY72ZDS80yp6yD5oq6rFZURdV7SIHrNcgvCaA8hrzmpo/8MnwGNbr/foc4X6pzqbxNtCS3fFnfC2JwoqdPWIJ5o37X8PewP0XJJiO+aZAMP2owW3VP6NqAvZNU9jKTVO2jwUCQb3odre3u91u1T+jCXS3O4/3Vlf39p52t4WwbLrS3mJrc3OT3/avCV9ZbD9ubbaGbO4/2676J9WWrrQ/6or72hO6kul27kZlMV2rTdJ14cfTcGGar+ouxlw1LLoufP7TafjtFLq2Y2kY6nramK7+wufvTMMUtrpPk2UxhK0Y24l5GORigq5bhSjJd2m2uo9TQyspuG599WkR/hmdw4U5UoGt7f10WQk9161PjxXhq1sRWT+YIxXYUtJltVrxKmJGW+e/N0fKt9W9lpGIrcW5x9YP50gFtrK6rVZLmbMt6fwcqcDWXqathTnbOv+jOVI3W5vPoql4xPutkjOxaAmRbylKs5VeyTPuxtrPbOtQKK2C6GSF1n7lFcS/ci1QedVp6oYP67Yez7uCKNzL/zvXQpS35ZPRzW/Guq3SK4h8C1HeVrWULitpQLDYVnU53VaJo4EZwXV//ovVeLp3U0KrQYOn5dFNXi1uxleIAgm2rJNkxbeoBZzus1ZsD9l+GbLa8ZVuA+i2V8d0bbYel5GGyGro8YDbz/Zbm4Ex9rBXzqEjSG2oLam7rTxd3V9s7a/uXSvrMJvm2pJAGKe8Q5Iabat0hK0iCFtFELaKIGwVQdgqgrBVBGErN4ZhSKrstOFRMAHSP1k6uP6GIBN+8nB49nrVv6UBoH5wNeCU++oZBJfo5/MQBpcIrVwEwWVW/TOaAr/+gQitnPCrIYkVYl5YzyVCKzes5xIrxPwQsZlYBCFLIBAcAu3mUoEr5f7h0DlsFKVsX22ls/DkP3+O8SSFbxL4a5y/pPObCL+O8HWEb1PZW+go5epqd1bWrjKW+qz1WQ65wvk44CbngyGn+5wJeI/zPnAx4FLIOeDDgLOMkwG/DLjOud3n8oA7nBsBdwMWOffuLa50yrWl3H8yZirQlWXqZpKq06OuRm2NuTr34bm+q7Mnz464itq6fHlc140BY7bu7S2Ue3xY59lHQ1tLSwfPX7x4/WZ5XNfHV0Zs3Uy0dSbN1sWpbN1OszUeW/dWFzplymqP2XqzIb/cffVyXX69NmZrLLjy2rqYYOvciK2TcVu3c9oKfIGtclOx3bk2tPVcfhUeObi+caXcTLyebevOjYSOq1pbz+XhwZa7cjmZmKYrautOQipWautAHj0y9eWXayVk4qOfTMWDym1d3Xg1dsyzvFVCJj764pPivPO76m0ty+NHiO++GAZXX1g0F2fOxEdfTHOuUw1s7Twct/VAXlu+cgBsAX9LZHImXsrOxKbaev4ycvYB6+e35PVM5NkzsaG2Xu9GbV1Z3lr/LOPUxM+OybP08jPbWqxbbOW1dTq133pLY+sg0su/YiVEtq1jxzIzcVK/1WhbS/J4BbG+Mym2jlWciZXa2hkLrt2N5QK2psnERq8Tl5ZerI/koXwQ2MriKGci6Opv++zKW2vc1q8yOcKZyLerH756sPlgV9444IX81sYEjnAmwuDpzgvm4PXWcPQ0Om4zvu1zlDNxqT/QnDzSHB+ymWkM4i2wFdmDkTC+9UGSrekz8efF+aSuthJGbJLHt6bNxKaOb022dQiZmDYunz3OXI9x+fIzMW13YgPG5SvIxML7E6N7yOprq8RMnLQ/8Yhm4kRbd5oaW3XIxBt1tpW4h0xk4ihZEZYuLFQ29HUxSVfyETbX0/a9ju55HSkeql8nvvlydn4/PQ+LUL2tnb8f/0UzOP6PGzWwdeJ4MzhRC1vvNoU62Jo08peX6TaUU0na61t9v7W0FK8kchx5Gl0nnskuuCLrxFwHnSYdc1q9rTlXp0m1/FtVnc4vtt6LlqdRWWdnia3qR2zm1m8dSidWt35LrBOL2RL1ViFbUCc3gDrYmnU7cYZtxAZuJ6aPQSQdelrmGEQd14kzjZ0mjW9NcZ5Pg8ZO4+eRZdSoqYPNqcOBl6L1VqOr053vl4g829hp9ZnI6q0TZfHuf5ueiWXWW2m2GpSJ//tZeeSPrZpm4sEf0vljHv5UhKZnYqyKyFVuZZ1X/X683HqrRmyy9ycWPD/xEPcnVp+Jojqd2da8x07nfc5+vWwdZiY2/Sx0kYmz2RKZWMSWWCdOYUtkYkjn2TcN3p/47Uq5V2ZRFk59dDUgVs/nGd/KcZWk+OBp2u7qpNhKOIRrGFqnFsq9pFS7s3Lqu+glsaYkeiWt2fk6djWuUb47VfYVpSSls7ByqpmsLHRK/w8HFearmXSUCv53xnZbaSYVXGVRIKg/hCg49c2eRCTx//oMIR61rLaEscRuhGBmDxMpvGGVuEjXCQ4nH3mwaSDXJbZv9GzfcX1s66qHNdXBnupiV5NtTZN8v+25vrhCv2RYSCLI1z3qmYrpqZrleqZBDcsze45CJSoxfY5maVQxRUq2KSLtHiWIaiqi2NWtNjIdH+m2pqoSJRTZOn/XR6p35HMRqb7HVEBsWYgSZsv2qEHblmcbqkOJ2WOxpWvCVgBxfQ1L0G/pyJU0z9L9HtZ8h91Yv0U06LdsX/EcrItMlPj6j68TCfxD1BiuE2ECY/CuIAqu+gcIBAKB4Ijyf7eXQMaS1/yXAAAAAElFTkSuQmCC", - url: "/blog/react-server-components", - category: "guides", - }, - { - title: "A Deep Dive into Tailwind CSS 4.0", - description: - "Tailwind CSS keeps evolving. Discover the new features in version 4.0 and how they can streamline your workflow.", - author: "John Smith", - date: "2025-11-20", - readingTime: 5, - tags: ["CSS", "Tailwind", "Frontend"], - image: - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQt89y2XtPE7GhSgcohDk9qdcaf8t5--qwPTg&s", - url: "/blog/tailwind-css-4", - category: "guides", - }, - { - title: "Optimizing Next.js for Performance", - description: - "Performance is key in web apps. Learn practical techniques to make your Next.js application lightning-fast.", - author: "Alice Johnson", - date: "2025-11-15", - readingTime: 8, - tags: ["Next.js", "Performance", "WebDev"], - image: - "https://bs-uploads.toptal.io/blackfish-uploads/components/blog_post_page/6389919/cover_image/regular_1708x683/Next-js-Improving-Page-Speed-of-a-Server-Side-React-App-Blog-7d4de23630c897d49e1c5be730ad5695.png", - url: "/blog/nextjs-performance", - category: "guides", - }, - { - title: "State Management in Modern React", - description: - "From Context API to Zustand and Redux Toolkit, we explore modern strategies for managing state in React apps.", - author: "Robert Lee", - date: "2025-11-10", - readingTime: 6, - tags: ["React", "State Management", "Frontend"], - image: - "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQEm17yo2o8rIW-5nNXHk2J2MhKaTSi4cKZvA&s", - url: "/blog/react-state-management", - category: "guides", - }, - { - title: "Getting Started with TypeScript 6", - description: - "TypeScript 6 brings exciting new type features. This guide helps you get started with the latest version.", - author: "Emma Watson", - date: "2025-11-05", - readingTime: 7, - tags: ["TypeScript", "JavaScript", "Programming"], - image: - "data:image/jpeg;base64,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", - url: "/blog/typescript-6-intro", - category: "guides", - }, -]; - -export default posts; diff --git a/src/lib/projects.tsx b/src/lib/projects.ts similarity index 100% rename from src/lib/projects.tsx rename to src/lib/projects.ts From fc459e7e62ee9589e06a21b2bcecd8ede07d928a Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 19:10:03 +0100 Subject: [PATCH 10/12] Simple blog post --- blog/test.json | 10 ---------- blog/test.md | 1 - blog/welcome.json | 10 ++++++++++ blog/welcome.md | 7 +++++++ src/app/blog/[slug]/page.tsx | 31 ++++++++++++++++--------------- 5 files changed, 33 insertions(+), 26 deletions(-) delete mode 100644 blog/test.json delete mode 100644 blog/test.md create mode 100644 blog/welcome.json create mode 100644 blog/welcome.md diff --git a/blog/test.json b/blog/test.json deleted file mode 100644 index 53b15cd..0000000 --- a/blog/test.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "title": "My Post", - "description": "This is a short description", - "author": "John Doe", - "date": "2025-12-03", - "readingTime": 1, - "tags": ["typescript", "nextjs"], - "image": "/images/my-post.jpg", - "category": "guides" -} diff --git a/blog/test.md b/blog/test.md deleted file mode 100644 index 6769dd6..0000000 --- a/blog/test.md +++ /dev/null @@ -1 +0,0 @@ -Hello world! \ No newline at end of file diff --git a/blog/welcome.json b/blog/welcome.json new file mode 100644 index 0000000..b2aa4bf --- /dev/null +++ b/blog/welcome.json @@ -0,0 +1,10 @@ +{ + "title": "Welcome to ORUS!", + "description": "ORUS is a organization for people just like you", + "author": "Klesti Selimaj", + "date": "2025-12-03", + "readingTime": 1, + "tags": ["welcome", "first", "ORUS"], + "image": "/orus-logo.svg", + "category": "news" +} diff --git a/blog/welcome.md b/blog/welcome.md new file mode 100644 index 0000000..30f7058 --- /dev/null +++ b/blog/welcome.md @@ -0,0 +1,7 @@ +# Hello, World! 👋 + +Welcome! ORUS is a cybersecurity & dev organization, Simply for developers. + +- 💻 open source: We love the open source community and our projects are all open source. +- 📚 growing community: We are a growing community full of beginners and pros. join here +- 🌐 cybersecurity & development: we develop projects for developers and cybersecurity enthusiasts \ No newline at end of file diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 0e3472c..2596e4c 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -8,9 +8,10 @@ import { useParams } from "next/navigation"; import { PostPreview } from "@/lib/posts"; export default function BlogPost() { - const [post, setPost] = useState<(PostPreview & { content: string }) | null>( - null - ); + const [post, setPost] = useState<{ + content: string; + meta: PostPreview; + } | null>(null); const [mounted, setMounted] = useState(false); const { slug } = useParams(); @@ -50,7 +51,7 @@ export default function BlogPost() { return ( - Hero Section + {/* Hero Section */} - {post.category} + {post.meta.category} @@ -69,7 +70,7 @@ export default function BlogPost() { className="text-5xl lg:text-6xl font-bold mb-6" > - {post.title} + {post.meta.title} @@ -77,16 +78,16 @@ export default function BlogPost() { variants={itemVariants} className="flex flex-wrap items-center gap-4 text-sm text-slate-400 mb-8" > - {post.author} + {post.meta.author} • - {new Date(post.date).toLocaleDateString()} - {post.readingTime && ( + {new Date(post.meta.date).toLocaleDateString()} + {post.meta.readingTime && ( <> • - {post.readingTime} min read + {post.meta.readingTime} min read )} - {post.tags?.map((tag, idx) => ( + {post.meta.tags?.map((tag, idx) => ( {/* Optional Featured Image */} - {post.image && ( + {post.meta.image && ( {post.title} )} From 64ae7d0bb717ab314350bb381224476f926f6d43 Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 19:30:13 +0100 Subject: [PATCH 11/12] Blog markdown --- package-lock.json | 2035 +++++++++++++++++++++++++++++++++- package.json | 13 +- src/app/blog/[slug]/page.tsx | 8 +- src/components/Markdown.tsx | 112 ++ 4 files changed, 2161 insertions(+), 7 deletions(-) create mode 100644 src/components/Markdown.tsx diff --git a/package-lock.json b/package-lock.json index e495e05..76901eb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,21 @@ "version": "0.1.0", "dependencies": { "framer-motion": "^12.23.24", + "highlight.js": "^11.11.1", + "katex": "^0.16.25", "lucide-react": "^0.555.0", "next": "16.0.6", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "react-markdown": "^10.1.0", + "rehype-autolink-headings": "^7.1.0", + "rehype-highlight": "^7.0.2", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "rehype-slug": "^6.0.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -976,6 +987,60 @@ "tailwindcss": "4.1.17" } }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==", + "license": "MIT" + }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.25", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.25.tgz", @@ -990,7 +1055,6 @@ "version": "19.2.7", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1006,6 +1070,28 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001757", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", @@ -1026,19 +1112,126 @@ ], "license": "CC-BY-4.0" }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", + "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-legacy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", + "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-reference-invalid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", + "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decode-named-character-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", + "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", + "license": "MIT", + "dependencies": { + "character-entities": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -1049,6 +1242,19 @@ "node": ">=8" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.3", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", @@ -1063,6 +1269,46 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/escape-string-regexp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", + "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/framer-motion": { "version": "12.23.24", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", @@ -1090,6 +1336,12 @@ } } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "license": "ISC" + }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1097,6 +1349,345 @@ "dev": true, "license": "ISC" }, + "node_modules/hast-util-from-dom": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-5.0.1.tgz", + "integrity": "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==", + "license": "ISC", + "dependencies": { + "@types/hast": "^3.0.0", + "hastscript": "^9.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", + "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.1.0", + "hast-util-from-parse5": "^8.0.0", + "parse5": "^7.0.0", + "vfile": "^6.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-2.0.0.tgz", + "integrity": "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-from-dom": "^5.0.0", + "hast-util-from-html": "^2.0.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-heading-rank": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-heading-rank/-/hast-util-heading-rank-3.0.0.tgz", + "integrity": "sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-is-element": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", + "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-parse-selector": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", + "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", + "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^6.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-parse5/node_modules/property-information": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", + "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/hast-util-to-string": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-string/-/hast-util-to-string-3.0.1.tgz", + "integrity": "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-text": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", + "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unist-util-find-after": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hastscript": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", + "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "hast-util-parse-selector": "^4.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/is-alphabetical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", + "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-alphanumerical": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", + "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", + "license": "MIT", + "dependencies": { + "is-alphabetical": "^2.0.0", + "is-decimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-decimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", + "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", @@ -1107,6 +1698,22 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/katex": { + "version": "0.16.25", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", + "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -1368,6 +1975,31 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lucide-react": { "version": "0.555.0", "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.555.0.tgz", @@ -1387,6 +2019,901 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/markdown-table": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", + "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/mdast-util-find-and-replace": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", + "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "escape-string-regexp": "^5.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", + "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", + "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", + "license": "MIT", + "dependencies": { + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-gfm-autolink-literal": "^2.0.0", + "mdast-util-gfm-footnote": "^2.0.0", + "mdast-util-gfm-strikethrough": "^2.0.0", + "mdast-util-gfm-table": "^2.0.0", + "mdast-util-gfm-task-list-item": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-autolink-literal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", + "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "ccount": "^2.0.0", + "devlop": "^1.0.0", + "mdast-util-find-and-replace": "^3.0.0", + "micromark-util-character": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-strikethrough": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", + "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", + "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "markdown-table": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-gfm-task-list-item": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", + "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-math": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-3.0.0.tgz", + "integrity": "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "longest-streak": "^3.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.1.0", + "unist-util-remove-position": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-newline-to-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz", + "integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-find-and-replace": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", + "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", + "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.0.0", + "debug": "^4.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-core-commonmark": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", + "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "micromark-factory-destination": "^2.0.0", + "micromark-factory-label": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-factory-title": "^2.0.0", + "micromark-factory-whitespace": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-subtokenize": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-extension-gfm": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", + "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", + "license": "MIT", + "dependencies": { + "micromark-extension-gfm-autolink-literal": "^2.0.0", + "micromark-extension-gfm-footnote": "^2.0.0", + "micromark-extension-gfm-strikethrough": "^2.0.0", + "micromark-extension-gfm-table": "^2.0.0", + "micromark-extension-gfm-tagfilter": "^2.0.0", + "micromark-extension-gfm-task-list-item": "^2.0.0", + "micromark-util-combine-extensions": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-autolink-literal": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", + "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-footnote": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", + "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-core-commonmark": "^2.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-strikethrough": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", + "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-resolve-all": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-table": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", + "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-tagfilter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", + "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-gfm-task-list-item": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", + "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-3.1.0.tgz", + "integrity": "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==", + "license": "MIT", + "dependencies": { + "@types/katex": "^0.16.0", + "devlop": "^1.0.0", + "katex": "^0.16.0", + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-factory-destination": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", + "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-label": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", + "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-space": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", + "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-title": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", + "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-factory-whitespace": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", + "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-factory-space": "^2.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-character": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", + "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-chunked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", + "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-classify-character": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", + "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-combine-extensions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", + "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-chunked": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-numeric-character-reference": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", + "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-encode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", + "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-html-tag-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", + "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-normalize-identifier": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", + "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-resolve-all": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", + "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-sanitize-uri": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", + "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "micromark-util-character": "^2.0.0", + "micromark-util-encode": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, + "node_modules/micromark-util-subtokenize": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", + "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "devlop": "^1.0.0", + "micromark-util-chunked": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0" + } + }, + "node_modules/micromark-util-symbol": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", + "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, + "node_modules/micromark-util-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", + "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT" + }, "node_modules/motion-dom": { "version": "12.23.23", "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", @@ -1402,6 +2929,12 @@ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", "license": "MIT" }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1500,6 +3033,43 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/parse-entities": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", + "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^2.0.0", + "character-entities-legacy": "^3.0.0", + "character-reference-invalid": "^2.0.0", + "decode-named-character-reference": "^1.0.0", + "is-alphanumerical": "^2.0.0", + "is-decimal": "^2.0.0", + "is-hexadecimal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/parse-entities/node_modules/@types/unist": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", + "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1535,6 +3105,16 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/property-information": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", @@ -1556,6 +3136,216 @@ "react": "^19.2.0" } }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/rehype-autolink-headings": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/rehype-autolink-headings/-/rehype-autolink-headings-7.1.0.tgz", + "integrity": "sha512-rItO/pSdvnvsP4QRB1pmPiNHUskikqtPojZKJPPPAVx9Hj8i8TwMBhofrrAYRhYOOBZH9tgmG5lPqDLuIWPWmw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-is-element": "^3.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-highlight": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", + "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-to-text": "^4.0.0", + "lowlight": "^3.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-katex": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-7.0.1.tgz", + "integrity": "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/katex": "^0.16.0", + "hast-util-from-html-isomorphic": "^2.0.0", + "hast-util-to-text": "^4.0.0", + "katex": "^0.16.0", + "unist-util-visit-parents": "^6.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/rehype-slug": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/rehype-slug/-/rehype-slug-6.0.0.tgz", + "integrity": "sha512-lWyvf/jwu+oS5+hL5eClVd3hNdmwM1kAC0BUvEGD19pajQMIzcNUd/k9GsfQ+FfECvX+JE+e9/btsKH0EjJT6A==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "github-slugger": "^2.0.0", + "hast-util-heading-rank": "^3.0.0", + "hast-util-to-string": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-breaks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-4.0.0.tgz", + "integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-newline-to-break": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-gfm": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", + "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-gfm": "^3.0.0", + "micromark-extension-gfm": "^3.0.0", + "remark-parse": "^11.0.0", + "remark-stringify": "^11.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-math": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-6.0.0.tgz", + "integrity": "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-math": "^3.0.0", + "micromark-extension-math": "^3.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-stringify": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", + "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-to-markdown": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -1629,6 +3419,48 @@ "node": ">=0.10.0" } }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, "node_modules/styled-jsx": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", @@ -1673,6 +3505,26 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -1699,6 +3551,183 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" + }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-find-after": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", + "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-remove-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", + "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-visit": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", + "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index 6a93590..a752e92 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,21 @@ }, "dependencies": { "framer-motion": "^12.23.24", + "highlight.js": "^11.11.1", + "katex": "^0.16.25", "lucide-react": "^0.555.0", "next": "16.0.6", "react": "19.2.0", - "react-dom": "19.2.0" + "react-dom": "19.2.0", + "react-markdown": "^10.1.0", + "rehype-autolink-headings": "^7.1.0", + "rehype-highlight": "^7.0.2", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "rehype-slug": "^6.0.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/src/app/blog/[slug]/page.tsx b/src/app/blog/[slug]/page.tsx index 2596e4c..a7bc4e0 100644 --- a/src/app/blog/[slug]/page.tsx +++ b/src/app/blog/[slug]/page.tsx @@ -6,6 +6,7 @@ import { Sparkles } from "lucide-react"; import WebStuff from "@/components/Web"; import { useParams } from "next/navigation"; import { PostPreview } from "@/lib/posts"; +import Markdown from "@/components/Markdown"; export default function BlogPost() { const [post, setPost] = useState<{ @@ -112,11 +113,12 @@ export default function BlogPost() { )} {/* Post Content */} - + > + + ); diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx new file mode 100644 index 0000000..06757fe --- /dev/null +++ b/src/components/Markdown.tsx @@ -0,0 +1,112 @@ +import React, { useState } from "react"; +import ReactMarkdown from "react-markdown"; +import remarkGfm from "remark-gfm"; +import remarkMath from "remark-math"; +import rehypeKatex from "rehype-katex"; +import rehypeHighlight from "rehype-highlight"; +import rehypeSlug from "rehype-slug"; +import rehypeAutolinkHeadings from "rehype-autolink-headings"; +import remarkBreaks from "remark-breaks"; +import rehypeRaw from "rehype-raw"; +import "katex/dist/katex.min.css"; +import "highlight.js/styles/github-dark.css"; +import "highlight.js/styles/github.css"; +import { CopyIcon } from "lucide-react"; + +interface MarkdownProps { + content: string; + components?: Record; + darkMode?: boolean; +} + +const Markdown: React.FC = ({ + content, + components, + darkMode = true, +}) => { + // Copy to clipboard handler + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + }; + + return ( +
+ ( +

+ ), + h2: ({ node, ...props }) => ( +

+ ), + h3: ({ node, ...props }) => ( +

+ ), + a: ({ href, children, ...props }) => ( + + {children} + + ), + code: ({ className, children, ...props }) => { + const language = className?.replace("language-", "") || ""; + return ( +
+
+                  
+                    {children}
+                  
+                
+ +
+ ); + }, + details: ({ node, ...props }) => ( +
+ ), + summary: ({ node, ...props }) => ( + + ), + ul: ({ ...props }) => ( +
    + ), + ol: ({ ...props }) => ( +
      + ), + hr: ({ ...props }) =>
      , + ...components, + }} + > + {content} + +

+ ); +}; + +export default Markdown; From 5c9b9ccb1700b24c3b581ce7445f8615191b5aab Mon Sep 17 00:00:00 2001 From: Leo dev Date: Wed, 3 Dec 2025 20:45:19 +0100 Subject: [PATCH 12/12] Blog and projects animation --- src/app/blog/page.tsx | 36 +++++++++++++----------------------- src/app/projects/page.tsx | 5 ++++- src/lib/formatNumber.tsx | 1 + 3 files changed, 18 insertions(+), 24 deletions(-) diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx index a320e63..9976d1b 100644 --- a/src/app/blog/page.tsx +++ b/src/app/blog/page.tsx @@ -39,9 +39,8 @@ export default function Blog() { : projects.filter((p) => p.category === activeFilter); const containerVariants = { - hidden: { opacity: 0 }, + hidden: {}, visible: { - opacity: 1, transition: { staggerChildren: 0.1, }, @@ -49,13 +48,11 @@ export default function Blog() { }; const itemVariants = { - hidden: { y: 20, opacity: 0 }, + hidden: { opacity: 0, y: 20 }, visible: { - y: 0, opacity: 1, - transition: { - duration: 0.5, - }, + y: 0, + transition: { duration: 0.9 }, }, }; @@ -115,22 +112,18 @@ export default function Blog() { {/* Blog Posts List */} - +
{filteredPosts.map((post, i) => ( -
- {/* Optional Thumbnail */} {post.image && (
)} - {/* Content */}

@@ -151,7 +143,6 @@ export default function Blog() { {post.description}

- {/* Metadata */}
{post.author} • @@ -173,7 +164,6 @@ export default function Blog() {

- {/* Read More */}
- + ))}
- +
); } diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index 879fee8..822338b 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -140,8 +140,11 @@ export default function Projects() { key={i} layout className="relative group" + initial={{ opacity: 0, y: 20 }} + whileInView={{ opacity: 1, y: 0 }} + transition={{ duration: 0.6, ease: "easeOut" }} + viewport={{ once: true, amount: 0.3 }} whileHover={{ y: -8 }} - viewport={{ once: false }} >
diff --git a/src/lib/formatNumber.tsx b/src/lib/formatNumber.tsx index cccc1ab..25295bd 100644 --- a/src/lib/formatNumber.tsx +++ b/src/lib/formatNumber.tsx @@ -1,4 +1,5 @@ export default function formatNumber(num: number): string { + if (num === null) return num; if (num < 1000) return num.toString(); if (num < 1_000_000) return (num / 1000).toFixed(num % 1000 === 0 ? 0 : 1) + "k";