Repository: mitul-s/mitul.ca Branch: master Commit: 9d1be5d0baed Files: 89 Total size: 204.8 KB Directory structure: gitextract_iezpmlwg/ ├── .gitignore ├── LICENSE.txt ├── README.md ├── app/ │ ├── (with-layout)/ │ │ ├── about/ │ │ │ └── page.tsx │ │ ├── layout.tsx │ │ ├── page.client.tsx │ │ └── page.tsx │ ├── (without-root-layout)/ │ │ ├── layout.tsx │ │ ├── p/ │ │ │ ├── 2024/ │ │ │ │ ├── page.mdx │ │ │ │ └── smol-txt.tsx │ │ │ ├── 2025/ │ │ │ │ └── page.mdx │ │ │ ├── components/ │ │ │ │ ├── callout.tsx │ │ │ │ ├── figure.tsx │ │ │ │ ├── img-container.tsx │ │ │ │ └── small-text.tsx │ │ │ ├── layout.tsx │ │ │ ├── q1-2025/ │ │ │ │ ├── page.mdx │ │ │ │ └── two-imgs.tsx │ │ │ ├── q2-2025/ │ │ │ │ └── page.mdx │ │ │ ├── q3-2025/ │ │ │ │ └── page.mdx │ │ │ └── substack-embed.module.css │ │ └── visitors/ │ │ ├── actions.ts │ │ ├── all/ │ │ │ ├── page.tsx │ │ │ └── visitors-all.module.css │ │ ├── gang/ │ │ │ └── page.tsx │ │ ├── login/ │ │ │ └── page.tsx │ │ ├── notes.module.css │ │ └── page.tsx │ ├── actions.ts │ ├── api/ │ │ ├── login/ │ │ │ └── route.ts │ │ ├── md/ │ │ │ └── route.ts │ │ ├── send/ │ │ │ └── route.ts │ │ └── spotify/ │ │ └── route.ts │ ├── feed.xml/ │ │ └── route.ts │ ├── globals.css │ ├── layout.tsx │ ├── llms.txt/ │ │ └── route.ts │ ├── robots.txt │ └── sitemap.ts ├── atoms/ │ └── guestbook.tsx ├── components/ │ ├── collapsible.tsx │ ├── copy-email-button.tsx │ ├── email-template.tsx │ ├── footer/ │ │ ├── footer-date.tsx │ │ └── index.tsx │ ├── gallery.tsx │ ├── json-ld.tsx │ ├── link-primitive.tsx │ ├── morphing-dialog.tsx │ ├── music-player.tsx │ ├── now-playing-client.tsx │ ├── photo.tsx │ ├── scroll-area.tsx │ ├── section.tsx │ ├── shader.tsx │ ├── theme-switcher.tsx │ ├── tree-client.tsx │ ├── tree.tsx │ ├── twitter-x-loop.tsx │ ├── video-hover-preview.tsx │ ├── video-pause-button.tsx │ └── visitors/ │ ├── approve-btn.tsx │ ├── cta.tsx │ ├── drag.tsx │ ├── field.tsx │ ├── guestbook-entries.tsx │ ├── note.tsx │ ├── polaroid.tsx │ ├── stickers.tsx │ └── visitors.module.css ├── content.ts ├── hooks/ │ ├── useClickOutside.tsx │ └── useMaxZIndex.tsx ├── lib/ │ ├── blog-posts.ts │ ├── literal.ts │ ├── openai.ts │ ├── redis.ts │ ├── spotify.ts │ └── utils.ts ├── mdx-components.tsx ├── microfrontends.json ├── next-env.d.ts ├── next.config.mjs ├── package.json ├── postcss.config.js ├── proxy.ts ├── public/ │ └── font/ │ ├── ABCMonumentGrotesk-Medium-Trial.otf │ └── ABCMonumentGrotesk-Regular-Trial.otf └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* # local env files .env .env.local .env.development.local .env.test.local .env.production.local # vercel .vercel .env*.local ================================================ FILE: LICENSE.txt ================================================ MIT License Copyright (c) 2024 Mitul Shah Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Twitter URL](https://img.shields.io/twitter/url/https/twitter.com/typicalmitul.svg?style=social&label=Follow%20%40typicalmitul&ref_src=twsrc%5Etfw)](https://twitter.com/typicalmitul) Hi! My name is Mitul 🏄‍♂️ Welcome to the code that controls my evergrowing space on the internet. Building out my personal website has brought me a lot of joy over the years, and it's transformed many, many times. I actually try to update it every few weeks. If you have any questions, suggestions – feel free to [tweet me](https://twitter.com/typicalmitul) 👋 ##### Built with * [Next.js](https://nextjs.org/) * [Vercel](https://vercel.com/) * [Tailwind CSS](https://tailwindcss.com/) * [Radix Primitives](https://radix-ui.com/) ### Getting Started _Note: I do have an .env.example file, but it could take awhile to get those keys. You could delete all the code related to those endpoints if it makes things easier._ ```bash $ git clone https://github.com/mitul-s/$ mitul.ca.git $ cd mitul.ca $ npm install $ npm run dev # or yarn if you prefer that ``` Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. --- #### Deploy on Vercel [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https%3A%2F%2Fgithub.com%2Fmitul-s%2Fmitul.ca) ================================================ FILE: app/(with-layout)/about/page.tsx ================================================ import LinkPrimitive from "@/components/link-primitive"; import { beliefs, bucketList, Status } from "@/content"; import { ArrowLeft } from "@phosphor-icons/react/dist/ssr/ArrowLeft"; import { cva } from "class-variance-authority"; import Link from "next/link"; import type { Metadata } from "next/types"; import Section from "@/components/section"; export const metadata: Metadata = { title: "About", alternates: { canonical: "https://mitul.ca/about", }, }; const bucketItem = cva(["self-start"], { variants: { status: { none: "", completed: ["line-through", "text-gray-11"], progress: [ "before:content-['']", "before:w-1", "before:h-1", "before:bg-accent", "before:inline-flex", "before:-mt-px", "before:rounded-full", "before:animate-pulse", "before:mr-1", "flex", "items-center", ], }, }, }); const BucketItem = ({ item, status, }: { item: string; status: keyof typeof Status; }) => { return
  • {item}
  • ; }; const About = () => { return (
    Home

    Hey, my name is Mitul and welcome to my space on the internet. I'm a self-taught{" "} design engineer {" "} based in Toronto, Canada.

    Learning to code has felt like a superpower for me, it allows me to bring any idea I can imagine to life. I love creating and focusing on the little things that enhance our experiences as we dive into the abyss of the web.

    Apart from all of that, a strong sense of curiosity about the world has always driven me. Travel, and specifically the diverse experiences gained from exploring different places, cultures, and landscapes, have significantly influenced my personal growth.

    My life thrives on both chaos and serendipity. I'm just tryna channel the same spirit of adventure as Ferris Bueller.

      {beliefs.map((belief) => { return
    • {belief}
    • ; })}
      {bucketList.map((item) => { return ( ); })}
    ); }; export default About; ================================================ FILE: app/(with-layout)/layout.tsx ================================================ import type { Viewport } from "next"; export const viewport: Viewport = { themeColor: "#0F0F0F", }; const Layout = ({ children }: { children: React.ReactNode }) => { return (
    {children}
    ); }; export default Layout; ================================================ FILE: app/(with-layout)/page.client.tsx ================================================ "use client"; import { Globe, Terminal } from "@phosphor-icons/react"; import { track } from "@vercel/analytics"; import Link from "next/link"; interface ProjectProps { title: string; description: string; hrefs: { live?: string; code?: string; }; } const Project = ({ title, description, hrefs: { live, code }, }: ProjectProps) => { return (

    {title}

    {description}

    {live ? ( track("project_live_link_clicked", { title })} > Live{" "} ) : null} {code ? ( track("project_code_link_clicked", { title })} > Code{" "} ) : null}
    ); }; export { Project }; ================================================ FILE: app/(with-layout)/page.tsx ================================================ import { Accordion, AccordionItem } from "@/components/collapsible"; import VideoHoverPreview from "@/components/video-hover-preview"; import { experiences, photos } from "@/content"; import { ArrowRight } from "@phosphor-icons/react/dist/ssr/ArrowRight"; import Link from "next/link"; import Gallery from "@/components/gallery"; import { PencilSimpleLine } from "@phosphor-icons/react/dist/ssr/PencilSimpleLine"; import TwitterXMotion from "@/components/twitter-x-loop"; import { CopyEmailButton, CopyEmailButtonAlt, } from "@/components/copy-email-button"; import Footer from "@/components/footer"; import { cn } from "@/lib/utils"; import { ScribbleLoop } from "@phosphor-icons/react/dist/ssr/ScribbleLoop"; import MusicPlayer from "@/components/music-player"; import Shader from "@/components/shader"; import { Project } from "./page.client"; import VideoPauseButton from "@/components/video-pause-button"; const DottedSpacer = ({ lines = 3, className, }: { lines?: number; className?: string; }) => { return (
    {Array.from({ length: lines }).map((_, index) => ( ))}
    ); }; const SectionTitle = ({ children }: { children: React.ReactNode }) => { return (

    {children}

    ); }; const Section = ({ title, children, }: { title?: string; children: React.ReactNode; }) => { return (
    {title ? {title} : null} {children}
    ); }; const SocialLink = ({ href, social, children, }: { href: string; social: string; children: React.ReactNode; }) => { return (

    {social}

    {children}
    ); }; const SectionContent = ({ children }: { children: React.ReactNode }) => { return
    {children}
    ; }; export default function Home() { return ( <>

    Mitul Shah

    Photographer, design engineer, and a bit more.

    Crafting memorable interfaces with a deep attention to detail. I dedicate most my time to continuous learning and refining my skillset.

    I'm a creative{" "} doing what I can't

    Currently
    Sign guestbook
    {experiences.map((role) => { return ( ); })}

    I've built up my craft as a photographer over a number of years and thrived in turning it into an indepedent business.

    Notable achievements include
    • being a personal photographer for the Uber CEO
    • featured in local Toronto newspapers
    • having a photo as a wallpaper in every Google device

    Today, my focus is on music photography where I capture my favourite artists at concerts or festivals. You can learn a little more by visiting my portfolio below.

    Visit my portfolio

    Let's hang, up for a chat or just say hi. If you're into photography, film or music, I'd love to hear from you.

    Mail

    mitulxshah@gmail.com
    Compose
    @typicalmitul mitul-s
    ); } ================================================ FILE: app/(without-root-layout)/layout.tsx ================================================ const Layout = ({ children }: { children: React.ReactNode }) => { return <>{children}; }; export default Layout; ================================================ FILE: app/(without-root-layout)/p/2024/page.mdx ================================================ import Smol from "./smol-txt"; import Figure from "./../components/figure"; import { BlogPostJsonLd } from "@/components/json-ld"; import tropez from "./tropez.jpeg"; import timechamber from "./timechamber.jpeg"; export const metadata = { title: "[Annual Review] 2024", description: "Everything I want.", authors: [{ name: "Mitul Shah", url: "https://mitul.ca" }], alternates: { canonical: "/p/2024", }, openGraph: { type: "article", title: "[Annual Review] 2024 / Mitul Shah", description: "Everything I want.", publishedTime: "2024-12-31", authors: ["Mitul Shah"], }, twitter: { card: "summary_large_image", title: "[Annual Review] 2024 / Mitul Shah", description: "Everything I want.", }, }; # [Annual Review] 2024
    I know I say it all the time, but every year is the best year of my life. I will always do my best to achieve everything I set out to. The past few years were dedicated to exploration and understanding — 2024 allowed me to reap the rewards of that journey. I can't pinpoint exactly what shifted, maybe it doesn't matter, but for the first time, everything feels right. I feel in control. This clarity has reflected positively in all my relationships; with friends, love, and family. Building an understanding of what you want and what you will tolerate makes things easier for both you and everyone around you. ## Goals Comparing my [2023](https://futureland.tv/@mitul/%E2%9C%A6-2023-%E2%9C%A6) journal to [2024](https://futureland.tv/@mitul/%E2%88%97-2024-%E2%88%97), it's obvious there's nearly no structure lol. This was intentional as I only had two primary goals for the year. For the smaller ones, things didn't always go as planned, but I'm okay with that. ### Code My creative aspirations this year kind of ended up on a back burner. While I initially set out to complete six projects; it was not a priority as I was traveling. I managed to bring three and a half projects to life which I'm super proud of. - [Montreal in Motion](https://typicalmitul.com/montreal-in-motion) - [Guestbook](/visitors) - A website for a friend and a hackathon project I have a massive project that's close to completion, but it's been ongoing for so long that I just can't get myself back into it. I'll keep trying. An unexpected highlight was that [Places to Read](https://placestoread.xyz) went viral through an Instagram reel. The response was overwhelming – over 300 submissions from strangers across the globe, each sharing their own favourite reading spaces. It was a good reminder of why I believe being able to code is a superpower. ### Photography My concert photography declined significantly this year, mainly because I felt burnt out. I shot fewer than 10 shows this year—I haven't kept exact count. While it's personally fulfilling, it offers few tangible benefits right now. In contrast, I photographed +40 acts last year including festivals and didn't make a dime (not that $ is the priority). It's tough. I'm at a crossroads: though I'm confident in my skills, I'm unsure how to grow this work into something bigger. ### Cooking and relationships I love cooking for people. I hosted a potluck, partly to see if I could make food people enjoy. More importantly, there was something deeply satisfying about creating a space where different friend groups could come together over a shared meal, each person bringing their own piece of themselves to the table. ## Highlights ### New Demos 1 The year started off with [New Demos](https://www.newdemos.ca/) 1, where I got the opportunity to present my project [Montreal in Motion](https://typicalmitul.com/montreal-in-motion). The night after, I landed a contract gig that funded my travels. ND1 kicked off a domino effect in Toronto that brought 1,000s of people together for the sake of progress and growth. It also sparked countless new meaningful friendships which shaped my summer back home after my travels. I'm forever grateful to [internetVin](https://x.com/internetvin) and [Tommy Trinh](https://x.com/tommytrxnh) for what they've done for the city of Toronto. ### The Last Rodeo: Four months of travel Travel has been a core part of my life for the past two years and I wanted to do one last, long backpacking trip, specifically one where I wasn't tied to a job or major responsibilities that affected how I travelled. I don't know if I'll ever do this again, but I'm so glad I did it one last time. - London - Turkey (Istanbul, Antalya, Cappadocia, back to Istanbul) - [Bosnia and Herzegovina](https://x.com/typicalmitul/status/1777433751737258475) (Sarjevo, Mostar) - Crotia (Split, Zagreb) - Slovenia (Ljubljana, Lake Bled) - Budapest - Prague - 6 weeks in Spain (Barcelona, Valencia, Alicante, Malaga, Granada, Madrid, Barcelona again and back to Madrid, Seville, Cadiz, Tarifa) - Morocco via boat from Spain (Tangier, Chefchaouen, Fes, Casablanca, Essaouira, nine nights in Taghazout) - Porto, Portugal to end my travels Part of me still misses travelling, though I do recognize it as a form of escapism. While I'll always deeply cherish the lifelong friends and memories I've made, I'm glad to be back home. I mean, I can't lie, it's pretty cool I can randomly bump into travel friends in so many places of the world. I missed out on a lot in Toronto, and it was a trade-off I was willing to make. I knew this trip was something I had to do. I could write a million words on my adventures, but that would be cliche. The overall goal was simple: travel until I land a full time job. ### Vercel While travelling, I landed a role at [Vercel](https://x.com/typicalmitul/status/1812914498619253065) as a Design Engineer. This was a dream company and I'm grateful to be here. It's an absolute honour to be working alongside the people I've always looked up to. Next.js changed my life, and that's probably an understatement. ## 2025 A goal I've been trying to achieve for nearly two years is almost in fruition. That's my main priority for the next little bit. Every year is quite significantly different from the last, but 2025 is when I introduce stability into my life and I am excited for that.
    --- ================================================ FILE: app/(without-root-layout)/p/2024/smol-txt.tsx ================================================ import Link from "next/link"; import React from "react"; const Smol = () => { return (

    Previous years [ 2023 ]

    ); }; export default Smol; ================================================ FILE: app/(without-root-layout)/p/2025/page.mdx ================================================ import Smol from "./smol-txt"; import Figure from "./../components/figure"; import ImageContainer from "../components/img-container"; import Callout from "../components/callout"; import monet from "./opengraph-image.jpg"; import { BlogPostJsonLd } from "@/components/json-ld"; export const metadata = { title: "[Annual Review] 2025", description: "A year in review: moving, adjusting, and finding breathing room", authors: [{ name: "Mitul Shah", url: "https://mitul.ca" }], alternates: { canonical: "/p/2025", }, openGraph: { type: "article", title: "[Annual Review] 2025 / Mitul Shah", description: "A year in review: moving, adjusting, and finding breathing room", publishedTime: "2025-12-31", authors: ["Mitul Shah"], }, twitter: { card: "summary_large_image", title: "[Annual Review] 2025 / Mitul Shah", description: "A year in review: moving, adjusting, and finding breathing room", }, }; # [Annual Review] 2025
    You can also read this on Substack I started the year in the middle of Buenos Aires watching fireworks with strangers and wondered why everyone was wearing white. I missed my flight home and reconsidered all my life decisions, but thankfully I had friends in the city that I could spend time with. I was living with the belief I would soon move to New York City once I was home, but little did I know, my drives to Detroit and Buffalo would end with rejections. I switched to a sublet on King St West and made a point to spend time with my friends while I waited for my visa approval, knowing they soon wouldn’t be a 10 minute walk from me. I spent most of my days at [New Stadium](https://x.com/newsystems_). By March, my visa was approved and I got ready to move away from home for the first time. June, I was in New York City. September, I signed a lease. I’ll be spending my new year in Guatemala and El Salvador, likely hiking, surfing, being on the beach and getting the time away that I sometimes find myself missing. Most often when my travel friends reach out reminding me of our memories together.
    I met [Shen](https://shen.land) in person this year and she's a great reminder to be more myself. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/IMG_3948.PNG) I was the first guest on [Rudy’s podcast](https://open.spotify.com/episode/3ZFlTNlsLa8e2nYINUG0UF?si=73c3fe14627e4450).
    I hosted weekly dinners. I made new friends. I lost new friends. I started working from an office again. I flew home to see the Blue Jays win (and my friends and family). The Blue Jays lost. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/IMG_7821.jpg) There’s a certain beauty in knowing I achieved the things I planned for this year. Write more – I mean, here you are. Take photos. Move to New York City. Get my first apartment. Travel less. I think I did it all. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/IMG_7112.jpg) Though there is something disorienting about getting what you wanted. An old friend always used to ask me what’s next. After years of thinking about it, what’s next is not the point. -- I went to Naples and watched them win the championship, my first time in Italy. With a need for stability in my life, I only travelled twice this year. Quite significantly less than the years prior. We’re probably going to pick that back up by a bit. I said goodbye to my friends. I still can’t believe Daniil cooked for everyone.
    Life is a never ending set of side quests. I don’t really like letting the mundane set in. Focus on building your Dad lore. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/Annual%20Review%202025%20Image%20%281%29.jpg) There’s a lot this year I did but it primarily revolved around moving to a new city for the first time. I haven’t really figured things out here yet, but I finally feel like I have breathing room to get back to things I’d like to do. The side quests. I found my first apartment. Though it took awhile, it was nice to jump between Bushwick, Williamsburg and eventually settle into LES. I hosted a little Friendsgiving party; it wasn’t anything like the one I hosted back home in Toronto but it’s a learning process. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/IMG_1834.jpeg) Being in a new city hasn’t been the easiest; I miss the sense of familiarity more than anything. I am happier here, or happier overall. The reality, the texture of moving to a new big city and acknowledging what the changes actually feel like rather than what they’re supposed to feel like is a bit hard, but it’s part of the human experience. Can I make my life feel like an episode of Friends/Seinfeld/HIMYM? I’d bet so. It just takes time. It already feels like a loop of Ferris Bueller’s Day Off. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/Annual%20Review%202025%20Image%20%283%29.jpg) -- I don't know who I am if I am not in a state of discomfort. It’s more I feel like I’ve reached a position where I’m deeply familiar with the things I do love and enjoy, and have done a great job of instilling them in my life but yet lost touch with them as I played with new, unfamiliar things for the sake of curiosity. That’s probably the best way I can phrase it.
    So for the upcoming year, I want to be extremely disciplined. Here’s some examples of what that could include: Write more. Let’s double or triple the amount of write ups I share publicly. No more solo travelling. Either a) travel with friends, or b) meet travel friends in their respective countries. Solo travel did wonders for me, it helped me shape who I am. Travelling with friends is hard, there’s different levels of comfort, schedules, risk tolerance and such but the times where it all aligns, makes it all worth it. Take even more photos but print them. I told my friend the other day, I don’t want to forget what I looked like today. I want to capture myself, my friends and everything in meaningful ways - something more than a 0.5*. So when the time comes and I share my photos (with friends, my kids, my family), there’s a certain level of love thought attached to it. I got a 3D printer. I want to design something of my own. I’m going to pick up film photography again. I have been anti-film as every camera I’ve owned has broke, but I’ll remember to be more careful this year. --- Never did I enjoy the beach much, but now I seem to be pulled towards it. I think a lot of the influence certain people have on my life, I changed because of them. I want to spend more time in the ocean, more time surfing, maybe learn to swim (better). Anyways. Everyday is an adventure, and it will continue to be. If you haven’t noticed I’m bad at telling the “why”, I kind of don’t want to unless someone asks me explicitly. If you think we’d get along, or I can help you, or you can help me. Email me here [mitulxshah@gmail.com](mailto:mitulxshah@gmail.com). ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/IMG_1145.jpg) **2026** Though internalized, a reminder, whatever it takes. ![.png](https://inqeleafibjx2dzc.public.blob.vercel-storage.com/main/p2025/Annual%20Review%202025%20Image%20%285%29.jpg) ***** See previous years [[2024](/p/2024), [2023](https://futureland.tv/@mitul/entry/385302)] See the quarters for 2025 [[Q1](/p/q1-2025), [Q2](/p/q2-2025), [Q3](/p/q3-2025)] ================================================ FILE: app/(without-root-layout)/p/components/callout.tsx ================================================ "use client"; import { ArrowUpRight } from "@phosphor-icons/react"; import Link from "next/link"; const Callout = ({ children }: { children: React.ReactNode }) => { return (

    {children}

    ); }; export default Callout; ================================================ FILE: app/(without-root-layout)/p/components/figure.tsx ================================================ import Image, { type StaticImageData } from "next/image"; interface FigureProps { src: string | StaticImageData; alt: string; caption?: string; width?: number; height?: number; } const Figure = (props: FigureProps) => { const { src, alt, caption, width, height } = props; const isExternalUrl = typeof src === "string"; // For external URLs without dimensions, use unoptimized mode const imageProps = isExternalUrl && !width ? { unoptimized: true, width: 1400, height: 900 } : width && height ? { width, height } : {}; if (caption !== undefined) { return (
    {alt}
    {caption}
    ); } return ( {alt} ); }; export default Figure; ================================================ FILE: app/(without-root-layout)/p/components/img-container.tsx ================================================ const ImgContainer = ({ children }: { children: React.ReactNode }) => { return (
    {children}
    ); }; export default ImgContainer; ================================================ FILE: app/(without-root-layout)/p/components/small-text.tsx ================================================ const SmallText = ({ children }: { children: React.ReactNode }) => { return (
    {children}
    ); }; export default SmallText; ================================================ FILE: app/(without-root-layout)/p/layout.tsx ================================================ import type { Viewport } from "next"; import Link from "next/link"; import styles from "./substack-embed.module.css"; export const viewport: Viewport = { themeColor: "#F9F2E2", }; const Layout = ({ children }: { children: React.ReactNode }) => { return (
    {children}