diff --git a/apps/blade/package.json b/apps/blade/package.json index 5d96bcc78..f4794e4c8 100644 --- a/apps/blade/package.json +++ b/apps/blade/package.json @@ -51,10 +51,12 @@ "lucide-react": "^0.575.0", "luxon": "^3.7.2", "monaco-editor": "^0.56.0", + "motion": "^13.2.0", "next": "^16.2.11", "react": "^19.2.4", "react-dom": "^19.2.4", "recharts": "^3.7.0", + "simple-icons": "^16.30.0", "superjson": "2.2.6", "tw-animate-css": "^1.4.0", "zod": "catalog:" diff --git a/apps/blade/public/adrian-garced/Resume.pdf b/apps/blade/public/adrian-garced/Resume.pdf new file mode 100644 index 000000000..d94d3ebb9 Binary files /dev/null and b/apps/blade/public/adrian-garced/Resume.pdf differ diff --git a/apps/blade/src/app/_components/adrian-garced/footer.tsx b/apps/blade/src/app/_components/adrian-garced/footer.tsx new file mode 100644 index 000000000..a5d15152f --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/footer.tsx @@ -0,0 +1,15 @@ +export default function Footer() { + return ( + + ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/navbar.tsx b/apps/blade/src/app/_components/adrian-garced/navbar.tsx new file mode 100644 index 000000000..cd5dc1ddc --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/navbar.tsx @@ -0,0 +1,63 @@ +import { FileText, Mail } from "lucide-react"; + +import { Tooltip, TooltipContent, TooltipTrigger } from "@forge/ui/tooltip"; + +import { CodebergIcon, GithubIcon } from "~/app/adrian-garced/icons"; + +const NAVICONS = [ + { + tooltip: "Github", + icon: GithubIcon, + url: "https://github.com/pinkytoefoo", + }, + { + tooltip: "Codeberg", + icon: CodebergIcon, + url: "https://codeberg.org/pinkytoefoo", + }, + { + tooltip: "Resume", + icon: FileText, + url: "/adrian-garced/resume.pdf", + }, + { + tooltip: "Email", + icon: Mail, + url: "mailto:agadrian1331@gmail.com", + }, +] as const; + +export default function Navbar() { + return ( + + ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/playground.tsx b/apps/blade/src/app/_components/adrian-garced/playground.tsx new file mode 100644 index 000000000..018cacde1 --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/playground.tsx @@ -0,0 +1,169 @@ +"use client"; + +import { useEffect, useRef } from "react"; +import { + siC, + siCplusplus, + siGit, + siHono, + siPython, + siRust, + siShadcnui, + siSolid, + siSqlite, + siTailwindcss, + siTypescript, + siVercel, +} from "simple-icons"; + +const ICONS = [ + siCplusplus, + siC, + siSqlite, + siGit, + siRust, + siTypescript, + siVercel, + siPython, + siHono, + siSolid, + siTailwindcss, + siShadcnui, +]; + +const SIZE = 60; + +export default function Playground() { + const canvasRef = useRef(null); + const containerRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + const container = containerRef.current; + const ctx = canvas?.getContext("2d"); + + if (!canvas || !container || !ctx) return; + + const icon = new Image(); + + let x = 40; + let y = 40; + let vx = 100; + let vy = 100; + let width = 0; + let height = 0; + let iconIndex = 0; + let frame = 0; + let previous = performance.now(); + + const loadIcon = () => { + const currentIcon = ICONS[iconIndex]; + + if (!currentIcon) return; + + icon.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(` + + + + `)}`; + }; + + const nextIcon = () => { + let next = iconIndex; + + while (next === iconIndex) { + next = Math.floor(Math.random() * ICONS.length); + } + + iconIndex = next; + loadIcon(); + }; + + const resize = () => { + const { width: w, height: h } = container.getBoundingClientRect(); + const dpr = window.devicePixelRatio || 1; + + width = w; + height = h; + + canvas.width = w * dpr; + canvas.height = h * dpr; + canvas.style.width = `${w}px`; + canvas.style.height = `${h}px`; + + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + + x = Math.max(0, Math.min(x, width - SIZE)); + y = Math.max(0, Math.min(y, height - SIZE)); + }; + + const draw = (now: number) => { + const dt = Math.min((now - previous) / 1000, 0.05); + previous = now; + + x += vx * dt; + y += vy * dt; + + let hit = false; + + if (x <= 0 && vx < 0) { + x = 0; + vx *= -1; + hit = true; + } else if (x + SIZE >= width && vx > 0) { + x = width - SIZE; + vx *= -1; + hit = true; + } + + if (y <= 0 && vy < 0) { + y = 0; + vy *= -1; + hit = true; + } else if (y + SIZE >= height && vy > 0) { + y = height - SIZE; + vy *= -1; + hit = true; + } + + if (hit) nextIcon(); + + ctx.clearRect(0, 0, width, height); + + ctx.beginPath(); + ctx.roundRect(x, y, SIZE, SIZE, 8); + ctx.fillStyle = "rgba(245, 245, 245, 0.9)"; + ctx.fill(); + ctx.strokeStyle = "rgba(64, 64, 64, 0.8)"; + ctx.stroke(); + + if (icon.complete) { + ctx.drawImage(icon, x + 7, y + 7, SIZE - 14, SIZE - 14); + } + + frame = requestAnimationFrame(draw); + }; + + resize(); + loadIcon(); + frame = requestAnimationFrame(draw); + + window.addEventListener("resize", resize); + + return () => { + cancelAnimationFrame(frame); + window.removeEventListener("resize", resize); + }; + }, []); + + return ( +
+
+ +
+
+ ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/reveal.tsx b/apps/blade/src/app/_components/adrian-garced/reveal.tsx new file mode 100644 index 000000000..1cf861193 --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/reveal.tsx @@ -0,0 +1,51 @@ +"use client"; + +import { useEffect, useRef, useState } from "react"; + +interface RevealProps { + children: React.ReactNode; + className?: string; +} + +export default function Reveal({ + children, + className = "", +}: RevealProps) { + const ref = useRef(null); + const [visible, setVisible] = useState(false); + + useEffect(() => { + const element = ref.current; + + if (!element) return; + + const observer = new IntersectionObserver( + ([entry]) => { + if (entry?.isIntersecting) { + setVisible(true); + observer.disconnect(); + } + }, + { + threshold: 0.1, + } + ); + + observer.observe(element); + + return () => observer.disconnect(); + }, []); + + return ( +
+ {children} +
+ ); +} \ No newline at end of file diff --git a/apps/blade/src/app/_components/adrian-garced/sections/about-me.tsx b/apps/blade/src/app/_components/adrian-garced/sections/about-me.tsx new file mode 100644 index 000000000..dab41207a --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/about-me.tsx @@ -0,0 +1,103 @@ +import { Book } from "lucide-react"; + +interface CurrentlyReading { + title: string; + author: string; + coverUrl: string | null; +} + +// UNUSED: openlibrary.org fetching is too slow +// async function getCurrentlyReading(): Promise { +// try { +// const res = await fetch( +// "https://openlibrary.org/search.json?q=inside+the+machine&fields=title,author_name,cover_i&limit=1", +// // { next: { revalidate: 60 * 60 * 24 } } // re-fetch at most once a day https://nextjs.org/docs/app/api-reference/functions/fetch#optionsnexttags +// ); +// if (!res.ok) return null; + +// const data = await res.json(); +// const book = data.docs?.[0]; +// if (!book) return null; + +// return { +// title: book.title, +// author: book.author_name?.[0] ?? "Unknown", +// coverUrl: book.cover_i +// ? `https://ia801501.us.archive.org/view_archive.php?archive=/27/items/olcovers87/olcovers87-L.zip&file=870291-L.jpg` +// : null, +// }; +// } catch { +// return null; +// } +// } + +export default function AboutMe({ id }: { id: string }) { + // hardcoding values for to avoid fetching + const book: CurrentlyReading = { + title: "Inside the Machine", + author: "Jon Stokes", + coverUrl: + "https://ia801501.us.archive.org/view_archive.php?archive=/27/items/olcovers87/olcovers87-L.zip&file=870291-L.jpg", + }; + + return ( +
+
+

+ ABOUT ME +

+

+ adrian-g@~{":"} whoami +

+ +

+ I'm interested in fullstack web development, game development, systems + programming, and graphics. Most of my learning comes from building + experimental projects. I am working on trying to finish my projects + and make them resume ready. +

+ +

+ I have been programming since middle-school, doing little things like + creating juiced character controllers in Unity and Godot, making basic + python scripts, and reverse engineering pirating website APIs. But, I + never really felt like I completed anything or had any sort of + tangible impact. +

+ +

+ My goal is to contribute as much as possible to the team and the club, + that way I can finally say that my programming knowledge has had a + real-life impact! +

+
+ + {/*
*/} +
+ {book.coverUrl && ( + {`Cover + )} + +
+ +

+ Currently reading +

+ +
+ +

+ {book.title} +

+ +

{book.author}

+
+
+ {/*
*/} +
+ ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/sections/hobby-data.ts b/apps/blade/src/app/_components/adrian-garced/sections/hobby-data.ts new file mode 100644 index 000000000..755d433de --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/hobby-data.ts @@ -0,0 +1,58 @@ +export enum VisualType { + None, + Image, + Video +} + +export interface BaseProject { + title: string; + description: string; + content: string; + repoUrl: string; + tags: string[]; +} + +export type VideoProject = BaseProject & { + visualType: VisualType.Video; + videoUrl: string; +}; + +export type ImageProject = BaseProject & { + visualType: VisualType.Image; + visualUrl: string; +}; + +export type NoVisualProject = BaseProject & { + visualType: VisualType.None; +}; + +type Project = VideoProject | ImageProject | NoVisualProject; + +export const PROJECTS: Project[] = [ + { + title: "tort", + description: "Cross-Platform shell", + content: "A super basic shell with basic features, like child process creation, builtins, and prompt customization (very hacky). I wrote majority of the project within a weekend as a challenge to myself, however, I do plan on working on this project further and making it more practical, and add more complex features like piping, redirecting, and autocompletion (similar to the fish shell).", + visualType: VisualType.Video, + videoUrl: "https://github.com/user-attachments/assets/ed16a1e4-ab37-4188-89d6-c33ce164c86c", + repoUrl: "https://github.com/pinkytoefoo/tort", + tags: ["C", "Unix", "Windows", "System Calls", "Written within a weekend"] + }, + { + title: "Minecraft Clone", + description: "Minecraft C++ edition", + content: "Written C++ and OpenGL. I hope to experiment with other rendering APIs (primarily Vulkan and DX12) in the future. I plan on doing chunking and terrain generation next.", + visualType: VisualType.Video, + videoUrl: "https://github.com/user-attachments/assets/565d9330-d81a-404d-9639-d2b12d7ff514", + repoUrl: "https://github.com/pinkytoefoo/minecraft-plusplus", + tags: ["C++", "OpenGL", "GLFW", "GLM", "Dear ImGui"] + }, + { + title: "Cuebox", + description: "Desktop soundboard application", + content: "A desktop soundboard exploring Rust, Tauri, audio playback, and virtual audio devices. I currently am working on adding sounds, removing sounds, and adjusting the audio levels.", + visualType: VisualType.None, + repoUrl: "https://codeberg.org/pinkytoefoo/cuebox", + tags: ["Rust", "Tauri", "Audio Playback", "Desktop Apps"] + }, +]; diff --git a/apps/blade/src/app/_components/adrian-garced/sections/hobby.tsx b/apps/blade/src/app/_components/adrian-garced/sections/hobby.tsx new file mode 100644 index 000000000..aa4c780d5 --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/hobby.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { useRef, useState } from "react"; + +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@forge/ui/card"; + +import type { ImageProject, VideoProject } from "./hobby-data"; +import { PROJECTS, VisualType } from "./hobby-data"; + +const slugify = (s: string) => s.toLowerCase().replace(/\s+/g, "-"); + +function ProjectMedia({ project }: { project: VideoProject | ImageProject }) { + const videoRef = useRef(null); + const [isLoaded, setIsLoaded] = useState(false); + + // const handleMouseEnter = () => { + // if (project.visualType === VisualType.Video && videoRef.current) { + // videoRef.current.play().catch(() => {}); + // } + // }; + + // const handleMouseLeave = () => { + // if (project.visualType === VisualType.Video && videoRef.current) { + // videoRef.current.pause(); + // } + // }; + + return ( +
+ {/* loading skeleton wrapper */} + {!isLoaded && ( +
+ Loading visual... +
+ )} + + {project.visualType === VisualType.Video ? ( +
+ ); +} + +export default function HobbyProjects({ id }: { id: string }) { + return ( +
+
+

+ CURRENTLY BUILDING {"(slowly...)"} +

+

+ Hobby projects I'm working on +

+

+ These projects are in very early stages and are primarily worked on in + my spare time. +

+

+ Hover over the cards and click on them to go to repo! +

+
+ + {/* project cards */} + +
+ ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/sections/intro.tsx b/apps/blade/src/app/_components/adrian-garced/sections/intro.tsx new file mode 100644 index 000000000..346dda76d --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/intro.tsx @@ -0,0 +1,35 @@ +export default function Intro({ id }: { id: string }) { + return ( +
+
+

+ COMPUTER SCIENCE MAJOR +

+ +

+ Hi, I'm Adrian! +

+ +

My contact info is on my resume.

+ + +
+
+ ) +} \ No newline at end of file diff --git a/apps/blade/src/app/_components/adrian-garced/sections/my-tools.tsx b/apps/blade/src/app/_components/adrian-garced/sections/my-tools.tsx new file mode 100644 index 000000000..7e48b22a5 --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/my-tools.tsx @@ -0,0 +1,84 @@ +import Playground from "$/adrian-garced/playground"; + +const CATEGORIES = [ + { + title: "Systems & Low-Level", + technologies: ["C", "C++", "Rust", "Linux"], + }, + { + title: "Web Development", + technologies: ["TypeScript", "Next.js", "Hono", "Solid.js"], + }, + { + title: "Graphics & Game Development", + technologies: ["C++", "OpenGL", "GLFW", "GLM", "Godot", "C#"], + }, + { + title: "Currently Learning", + technologies: ["Computer Architecture", "Operating Systems"], + }, +]; + +export default function MyTools({ id }: { id: string }) { + return ( +
+
+

+ WHAT I WORK WITH +

+ +

+ Tech Stack & Tools I use +

+
+ +
+ {CATEGORIES.map((category) => ( +
+

+ {category.title} +

+ +
+ {category.technologies.map((technology) => ( + + {technology} + + ))} +
+
+ ))} +
+
+
+

+ PLAYGROUND +

+ +

+ {/* Sandbox */} +

+ +

+ DVD Animation but for my Technologies +

+ +

+ Inspiration:{" "} + + dvidal + +

+
+
+
+ ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/sections/selected-data.ts b/apps/blade/src/app/_components/adrian-garced/sections/selected-data.ts new file mode 100644 index 000000000..a13a0b14b --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/selected-data.ts @@ -0,0 +1,46 @@ +export interface SelectedProject { + title: string; + description: string; + content: string; + technologies: string[]; + liveUrl?: string; + repoUrl?: string; +} + +export const SELECTED_PROJECTS: SelectedProject[] = [ + { + title: "Image Upscaler", + description: "Image processing API", + content: + "A FastAPI backend using OpenCV for image upscaling and processing. Built to explore Python APIs, and the practical side of deploying service. Although, there is no live link due to bandwidth and high hardware costs of running an image processing API", + technologies: ["Python", "FastAPI", "OpenCV", "UV"], + repoUrl: "", + }, + { + title: "Pastebox", + description: "Simple paste sharing service", + content: + "A lightweight pastebin alternative. Users can submit text and receive a unique URL. Built as an to start learning about databases, persistence, and deploying a small web service. I plan on adding auth, syntax highlighting, and different viewing times", + technologies: ["TypeScript", "Hono", "SQLite", "Tailwind"], + liveUrl: "https://pastebox.koyeb.app/", + repoUrl: "https://github.com/pinkytoefoo/pastebox", + }, + { + title: "Frobby Blog", + description: "A culmination of what I learn", + content: + "A place to document what I'm learning about programming, computers, and the projects I build. The goal is to turn things I learn into something I can revisit, and hopefully share with other people too!", + technologies: ["Astro", "TypeScript", "Tailwind CSS", "Vercel"], + liveUrl: "https://frobby-blog.vercel.app/", + repoUrl: "https://github.com/pinkytoefoo/frobby-blog", + }, + { + title: "Word Counter", + description: "Text analysis tool", + content: + "A small web tool for analyzing text, including word, sentence, and character counts. One of my earlier projects focused on building and shipping a useful tool without adding uneeded complexity.", + technologies: ["TypeScript", "Solid.js", "Web"], + liveUrl: "https://wordcounty.vercel.app/", + repoUrl: "https://github.com/pinkytoefoo/word-counter", + }, +]; diff --git a/apps/blade/src/app/_components/adrian-garced/sections/selected.tsx b/apps/blade/src/app/_components/adrian-garced/sections/selected.tsx new file mode 100644 index 000000000..e53220381 --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/sections/selected.tsx @@ -0,0 +1,95 @@ +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@forge/ui/card"; + +import { SELECTED_PROJECTS } from "./selected-data"; + +const slugify = (s: string) => s.toLowerCase().replace(/\s+/g, "-"); + +export default function SelectedProjects({ id }: { id: string }) { + return ( +
+
+

+ SELECTED PROJECTS +

+ +

+ Things I've built +

+ +

+ A few projects that relate to web development +

+
+ +
+ {SELECTED_PROJECTS.map((project, index) => ( +
+ + + + {project.title} + + + + {project.description} + + + + +

+ {project.content} +

+ +
+ {project.technologies.map((technology) => ( + + {technology} + + ))} +
+ +
+ + Repo → + + + {project.liveUrl && ( + + Live site → + + )} +
+
+
+
+ ))} +
+
+ ); +} diff --git a/apps/blade/src/app/_components/adrian-garced/toc.tsx b/apps/blade/src/app/_components/adrian-garced/toc.tsx new file mode 100644 index 000000000..95f36a19b --- /dev/null +++ b/apps/blade/src/app/_components/adrian-garced/toc.tsx @@ -0,0 +1,88 @@ +"use client" + +import { useEffect, useState } from "react"; + +interface NavSubsection { + id: string; + label: string; +} + +interface NavEntry { + id: string; + label: string; + subsections?: NavSubsection[]; +} + +export default function TableOfContents({ sections }: { sections: NavEntry[] }) { + const [activeId, setActiveId] = useState("projects"); + + useEffect(() => { + const sectionIds = sections.flatMap(s => [s.id, ...(s.subsections?.map(sub => sub.id) ?? [])]); + const activationOffset = 400; + + const updateActiveSection = () => { + let currentSection = sectionIds[0]; + + for (const id of sectionIds) { + const element = document.getElementById(id); + + if (!element) continue; + + const top = element.getBoundingClientRect().top; + + if (top <= activationOffset) { + currentSection = id; + } + } + + if(currentSection) + setActiveId(currentSection); + }; + + updateActiveSection(); + + window.addEventListener("scroll", updateActiveSection, { passive: true }); + window.addEventListener("pageshow", updateActiveSection); + window.addEventListener("resize", updateActiveSection); + + return () => { + window.removeEventListener("scroll", updateActiveSection); + window.removeEventListener("pageshow", updateActiveSection); + window.removeEventListener("resize", updateActiveSection); + }; + }, []); + + return ( + + ) +} \ No newline at end of file diff --git a/apps/blade/src/app/adrian-garced/icons.tsx b/apps/blade/src/app/adrian-garced/icons.tsx new file mode 100644 index 000000000..aec20ed11 --- /dev/null +++ b/apps/blade/src/app/adrian-garced/icons.tsx @@ -0,0 +1,15 @@ +export function GithubIcon({ size = 24, ...props }: React.SVGProps & { size?: number }) { + return ( + + + + ); +} + +export function CodebergIcon({ size = 24, ...props }: React.SVGProps & { size?: number }) { + return ( + + + + ); +} diff --git a/apps/blade/src/app/adrian-garced/page.tsx b/apps/blade/src/app/adrian-garced/page.tsx new file mode 100644 index 000000000..52bf16e61 --- /dev/null +++ b/apps/blade/src/app/adrian-garced/page.tsx @@ -0,0 +1,104 @@ +/* +* TODO: + - [ ] abstract the page into seperate components + - [ ] add selected projects section + - [ ] add more concrete sections +*/ + +// components +import Footer from "$/adrian-garced/footer"; +import Navbar from "$/adrian-garced/navbar"; +import Reveal from "$/adrian-garced/reveal"; +import AboutMe from "$/adrian-garced/sections/about-me"; +import { PROJECTS } from "$/adrian-garced/sections/hobby-data"; +// sections +import Intro from "$/adrian-garced/sections/intro"; +import MyTools from "$/adrian-garced/sections/my-tools"; +import Selected from "$/adrian-garced/sections/selected"; +// shared data +import { SELECTED_PROJECTS } from "$/adrian-garced/sections/selected-data"; +import TableOfContents from "$/adrian-garced/toc"; + +import { Separator } from "@forge/ui/separator"; + +import Projects from "~/app/_components/adrian-garced/sections/hobby"; + +const slugify = (s: string) => s.toLowerCase().replace(/\s+/g, "-"); + +const PAGE_SECTIONS = [ + { id: "intro", label: "Intro" }, + { + id: "selected-projects", + label: "Selected Projects", + subsections: SELECTED_PROJECTS.map((p) => ({ + id: slugify(p.title), + label: p.title, + })), + }, + { + id: "stack", + label: "What I Work With", + subsections: [{ id: "playground", label: "Playground" }], + }, + { + id: "projects", + label: "Hobby Projects", + subsections: PROJECTS.map((p) => ({ + id: slugify(p.title), + label: p.title, + })), + }, + { id: "about-me", label: "About Me" }, +]; + +export default function AdrianG() { + return ( +
+ {/* scroll offset visualizer */} + {/*
+ + ACTIVE SECTION BOUNDARY + +
*/} + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+
+ ); +} diff --git a/apps/blade/src/app/layout.tsx b/apps/blade/src/app/layout.tsx index 2e3f7fc08..93256473b 100644 --- a/apps/blade/src/app/layout.tsx +++ b/apps/blade/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata, Viewport } from "next"; import { GeistMono } from "geist/font/mono"; import { GeistSans } from "geist/font/sans"; +import { Space_Grotesk, IBM_Plex_Mono } from "next/font/google"; import { cn } from "@forge/ui"; @@ -19,6 +20,7 @@ import { } from "./seo"; import "./globals.css"; +import { TooltipProvider } from "@forge/ui/tooltip"; export const metadata: Metadata = { metadataBase: new URL(SITE_URL), @@ -86,9 +88,20 @@ export const viewport: Viewport = { themeColor: "#050505", }; +const spaceGrotesk = Space_Grotesk({ + subsets: ["latin"], + variable: "--font-space-grotesk", +}); + +const ibmPlexMono = IBM_Plex_Mono({ + weight: ["400", "500", "600", "700"], + subsets: ["latin"], + variable: "--font-ibm-plex-mono", +}); + export default function RootLayout(props: { children: React.ReactNode }) { return ( - + + {props.children} +