1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 12:38:27 -04:00

consistent arrow functions

This commit is contained in:
Jake Jarvis 2025-03-12 18:10:11 -04:00
parent 6a57fde2f1
commit e61ca889a7
Signed by: jake
SSH Key Fingerprint: SHA256:nCkvAjYA6XaSPUqc4TfbBQTpzr8Xj7ritg/sGInCdkc
39 changed files with 121 additions and 126 deletions

View File

@ -15,7 +15,7 @@ NEXT_PUBLIC_UMAMI_WEBSITE_ID=
# optional. the base URL of a self-hosted Umami instance (including https://) to proxy requests to. if the website ID is # optional. the base URL of a self-hosted Umami instance (including https://) to proxy requests to. if the website ID is
# set but this isn't, the managed Umami Cloud endpoint is used. # set but this isn't, the managed Umami Cloud endpoint is used.
# https://umami.is/docs/bypass-ad-blockers # https://umami.is/docs/bypass-ad-blockers
NEXT_PUBLIC_UMAMI_HOST= NEXT_PUBLIC_UMAMI_URL=
# optional. enables comments on blog posts via GitHub discussions. # optional. enables comments on blog posts via GitHub discussions.
# https://giscus.app/ # https://giscus.app/

View File

@ -4,12 +4,12 @@ import type { hits as Hits } from "@prisma/client";
export const revalidate = 900; // 15 mins export const revalidate = 900; // 15 mins
export async function GET(): Promise< export const GET = async (): Promise<
NextResponse<{ NextResponse<{
total: Pick<Hits, "hits">; total: Pick<Hits, "hits">;
pages: Hits[]; pages: Hits[];
}> }>
> { > => {
// fetch all rows from db sorted by most hits // fetch all rows from db sorted by most hits
const pages = await prisma.hits.findMany({ const pages = await prisma.hits.findMany({
orderBy: [ orderBy: [
@ -28,4 +28,4 @@ export async function GET(): Promise<
}); });
return NextResponse.json({ total, pages }); return NextResponse.json({ total, pages });
} };

View File

@ -20,7 +20,7 @@ export const metadata: Metadata = {
}, },
}; };
export default function Page() { const Page = () => {
return ( return (
<> <>
<PageTitle canonical="/birthday">1996.mov</PageTitle> <PageTitle canonical="/birthday">1996.mov</PageTitle>
@ -28,4 +28,6 @@ export default function Page() {
<Video src={["/static/birthday/birthday.webm", "/static/birthday/birthday.mp4"]} poster={thumbnail.src} /> <Video src={["/static/birthday/birthday.webm", "/static/birthday/birthday.mp4"]} poster={thumbnail.src} />
</> </>
); );
} };
export default Page;

View File

@ -3,7 +3,7 @@
import { headers } from "next/headers"; import { headers } from "next/headers";
import { z } from "zod"; import { z } from "zod";
import { Resend } from "resend"; import { Resend } from "resend";
import config from "../../lib/config/constants"; import config from "../../lib/config";
const schema = z.object({ const schema = z.object({
name: z.string().min(1, { message: "Name is required" }), name: z.string().min(1, { message: "Name is required" }),
@ -12,7 +12,7 @@ const schema = z.object({
["cf-turnstile-response"]: z.string().min(1, { message: "CAPTCHA not completed" }), ["cf-turnstile-response"]: z.string().min(1, { message: "CAPTCHA not completed" }),
}); });
export async function sendMessage( export const sendMessage = async (
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
prevState: any, prevState: any,
formData: FormData formData: FormData
@ -21,7 +21,7 @@ export async function sendMessage(
message: string; message: string;
errors?: z.inferFormattedError<typeof schema>; errors?: z.inferFormattedError<typeof schema>;
payload?: FormData; payload?: FormData;
}> { }> => {
try { try {
const validatedFields = schema.safeParse(Object.fromEntries(formData)); const validatedFields = schema.safeParse(Object.fromEntries(formData));
@ -85,4 +85,4 @@ export async function sendMessage(
payload: formData, payload: formData,
}; };
} }
} };

View File

@ -71,7 +71,7 @@ const ContactForm = () => {
}} }}
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path d="M22.27 19.385H1.73A1.73 1.73 0 010 17.655V6.345a1.73 1.73 0 011.73-1.73h20.54A1.73 1.73 0 0124 6.345v11.308a1.73 1.73 0 01-1.73 1.731zM5.769 15.923v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.46v7.847zM21.232 12h-2.309V8.077h-2.307V12h-2.308l3.461 4.039z"></path> <path d="M22.27 19.385H1.73A1.73 1.73 0 010 17.655V6.345a1.73 1.73 0 011.73-1.73h20.54A1.73 1.73 0 0124 6.345v11.308a1.73 1.73 0 01-1.73 1.731zM5.769 15.923v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.46v7.847zM21.232 12h-2.309V8.077h-2.307V12h-2.308l3.461 4.039z" />
</svg>{" "} </svg>{" "}
Basic{" "} Basic{" "}
<Link href="https://commonmark.org/help/" title="Markdown reference sheet" style={{ fontWeight: 600 }}> <Link href="https://commonmark.org/help/" title="Markdown reference sheet" style={{ fontWeight: 600 }}>

View File

@ -18,7 +18,7 @@ export const metadata: Metadata = {
}, },
}; };
export default function Page() { const Page = () => {
return ( return (
<div <div
style={{ style={{
@ -46,4 +46,6 @@ export default function Page() {
<ContactForm /> <ContactForm />
</div> </div>
); );
} };
export default Page;

View File

@ -21,7 +21,7 @@ export const metadata: Metadata = {
}, },
}; };
export default function Page() { const Page = () => {
return ( return (
<> <>
<PageTitle canonical="/hillary">HRC.mov</PageTitle> <PageTitle canonical="/hillary">HRC.mov</PageTitle>
@ -60,4 +60,6 @@ export default function Page() {
</p> </p>
</> </>
); );
} };
export default Page;

View File

@ -4,7 +4,7 @@ import { ThemeProvider } from "../contexts/ThemeContext";
import Header from "../components/Header"; import Header from "../components/Header";
import Footer from "../components/Footer"; import Footer from "../components/Footer";
import { SkipToContentLink, SkipToContentTarget } from "../components/SkipToContent"; import { SkipToContentLink, SkipToContentTarget } from "../components/SkipToContent";
import config from "../lib/config/constants"; import config from "../lib/config";
import type { Metadata } from "next"; import type { Metadata } from "next";
import type { Person, WithContext } from "schema-dts"; import type { Person, WithContext } from "schema-dts";
@ -37,9 +37,14 @@ export const metadata: Metadata = {
{ {
url: meJpg.src, url: meJpg.src,
alt: `${config.siteName} ${config.shortDescription}`, alt: `${config.siteName} ${config.shortDescription}`,
width: meJpg.width,
height: meJpg.height,
}, },
], ],
}, },
twitter: {
creator: `@${config.authorSocial?.twitter}`,
},
alternates: { alternates: {
canonical: "/", canonical: "/",
types: { types: {
@ -83,7 +88,7 @@ const jsonLd: WithContext<Person> = {
], ],
}; };
export default function RootLayout({ children }: { children: React.ReactNode }) { const RootLayout = ({ children }: { children: React.ReactNode }) => {
return ( return (
<html lang={config.siteLocale} suppressHydrationWarning> <html lang={config.siteLocale} suppressHydrationWarning>
<head> <head>
@ -110,4 +115,6 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</body> </body>
</html> </html>
); );
} };
export default RootLayout;

View File

@ -21,7 +21,7 @@ export const metadata: Metadata = {
}, },
}; };
export default function Page() { const Page = () => {
return ( return (
<> <>
<PageTitle canonical="/leo">TheLab.mov</PageTitle> <PageTitle canonical="/leo">TheLab.mov</PageTitle>
@ -49,4 +49,6 @@ export default function Page() {
</p> </p>
</> </>
); );
} };
export default Page;

View File

@ -1,4 +1,4 @@
import config from "../lib/config/constants"; import config from "../lib/config";
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
const manifest = (): MetadataRoute.Manifest => { const manifest = (): MetadataRoute.Manifest => {

View File

@ -11,7 +11,7 @@ export const metadata: Metadata = {
}, },
}; };
export default async function Page() { const Page = () => {
return ( return (
<div style={{ textAlign: "center" }}> <div style={{ textAlign: "center" }}>
<Video <Video
@ -28,4 +28,6 @@ export default async function Page() {
<Link href="/">Go home?</Link> <Link href="/">Go home?</Link>
</div> </div>
); );
} };
export default Page;

View File

@ -8,7 +8,7 @@ import Loading from "../../../components/Loading";
import HitCounter from "./counter"; import HitCounter from "./counter";
import { getPostSlugs, getFrontMatter } from "../../../lib/helpers/posts"; import { getPostSlugs, getFrontMatter } from "../../../lib/helpers/posts";
import { metadata as defaultMetadata } from "../../layout"; import { metadata as defaultMetadata } from "../../layout";
import config from "../../../lib/config/constants"; import config from "../../../lib/config";
import type { Metadata, Route } from "next"; import type { Metadata, Route } from "next";
import type { Article, WithContext } from "schema-dts"; import type { Article, WithContext } from "schema-dts";
@ -20,16 +20,16 @@ export const dynamicParams = false;
// https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering#using-partial-prerendering // https://nextjs.org/docs/app/building-your-application/rendering/partial-prerendering#using-partial-prerendering
export const experimental_ppr = true; export const experimental_ppr = true;
export async function generateStaticParams() { export const generateStaticParams = async () => {
const slugs = await getPostSlugs(); const slugs = await getPostSlugs();
// map slugs into a static paths object required by next.js // map slugs into a static paths object required by next.js
return slugs.map((slug) => ({ return slugs.map((slug) => ({
slug, slug,
})); }));
} };
export async function generateMetadata({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> { export const generateMetadata = async ({ params }: { params: Promise<{ slug: string }> }): Promise<Metadata> => {
const { slug } = await params; const { slug } = await params;
const frontmatter = await getFrontMatter(slug); const frontmatter = await getFrontMatter(slug);
@ -54,9 +54,9 @@ export async function generateMetadata({ params }: { params: Promise<{ slug: str
canonical: `/notes/${slug}`, canonical: `/notes/${slug}`,
}, },
}; };
} };
export default async function Page({ params }: { params: Promise<{ slug: string }> }) { const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
const { slug } = await params; const { slug } = await params;
const frontmatter = await getFrontMatter(slug); const frontmatter = await getFrontMatter(slug);
@ -153,4 +153,6 @@ export default async function Page({ params }: { params: Promise<{ slug: string
)} )}
</> </>
); );
} };
export default Page;

View File

@ -1,7 +1,7 @@
import Link from "../../components/Link"; import Link from "../../components/Link";
import Time from "../../components/Time"; import Time from "../../components/Time";
import { getAllPosts } from "../../lib/helpers/posts"; import { getAllPosts } from "../../lib/helpers/posts";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import { metadata as defaultMetadata } from "../layout"; import { metadata as defaultMetadata } from "../layout";
import type { ReactElement } from "react"; import type { ReactElement } from "react";
import type { Metadata, Route } from "next"; import type { Metadata, Route } from "next";
@ -23,7 +23,7 @@ export const metadata: Metadata = {
}, },
}; };
export default async function Page() { const Page = async () => {
// parse the year of each note and group them together // parse the year of each note and group them together
const notes = await getAllPosts(); const notes = await getAllPosts();
const notesByYear: { const notesByYear: {
@ -58,5 +58,7 @@ export default async function Page() {
// grouped posts enter this component ordered chronologically -- we want reverse chronological // grouped posts enter this component ordered chronologically -- we want reverse chronological
const reversed = sections.reverse(); const reversed = sections.reverse();
return <>{reversed}</>; return reversed;
} };
export default Page;

View File

@ -34,7 +34,7 @@ const Link = ({
); );
}; };
export default function Page() { const Page = () => {
return ( return (
<div className={styles.page}> <div className={styles.page}>
<h1> <h1>
@ -286,4 +286,6 @@ export default function Page() {
</p> </p>
</div> </div>
); );
} };
export default Page;

View File

@ -38,7 +38,7 @@ export const metadata: Metadata = {
}, },
}; };
export default async function Page() { const Page = () => {
return ( return (
<div <div
className={styles.wackyWrapper} className={styles.wackyWrapper}
@ -86,7 +86,7 @@ export default async function Page() {
marginRight: "0.1em", marginRight: "0.1em",
}} }}
> >
<path d="M5.712 1.596l-.756.068-.238.55.734-.017zm1.39.927l-.978.137-.326.807.96-.12.345-.824zM4.89 3.535l-.72.05-.24.567.721-.017zm3.724.309l-1.287.068-.394.96 1.27-.052zm1.87.566l-1.579.069-.566 1.357 1.596-.088.548-1.338zm-4.188.037l-.977.153-.343.806.976-.12zm6.144.668l-1.87.135-.637 1.527 1.87-.154zm2.925.219c-.11 0-.222 0-.334.002l-.767 1.85c1.394-.03 2.52.089 3.373.38l-1.748 4.201c-.955-.304-2.082-.444-3.36-.394l-.54 1.305a8.762 8.762 0 0 1 3.365.396l-1.663 4.014c-1.257-.27-2.382-.395-3.387-.344l-.782 1.887c3.363-.446 6.348.822 9.009 3.773L24 9.23c-2.325-2.575-5.2-3.88-8.637-3.896zm-.644.002l-2.024.12-.687 1.68 2.025-.19zm-10.603.05l-.719.036-.224.566h.703l.24-.601zm3.69.397l-1.287.069-.395.959 1.27-.05zM5.54 6.3l-.994.154-.344.807.98-.121zm4.137.066l-1.58.069L7.53 7.77l1.596-.085.55-1.32zm1.955.688l-1.87.135-.636 1.527 1.887-.154zm2.282.19l-2.01.136-.7 1.682 2.04-.19.67-1.63zm-10.57.066l-.739.035-.238.564h.72l.257-.6zm3.705.293l-1.303.085-.394.96 1.287-.034zm11.839.255a6.718 6.718 0 0 1 2.777 1.717l-1.75 4.237c-.617-.584-1.15-.961-1.611-1.149l-1.201-.498zM4.733 8.22l-.976.154-.344.807.961-.12.36-.841zm4.186 0l-1.594.052-.549 1.354L8.37 9.54zm1.957.668L8.99 9.04l-.619 1.508 1.87-.135.636-1.527zm2.247.275l-2.007.12-.703 1.665 2.042-.156zM2.52 9.267l-.718.033-.24.549.718-.016zm3.725.273l-1.289.07-.41.96 1.287-.03.412-1zm1.87.6l-1.596.05-.55 1.356 1.598-.084.547-1.322zm-4.186.037l-.979.136-.324.805.96-.119zm6.14.633l-1.87.154-.653 1.527 1.906-.154zm2.267.275l-2.026.12-.686 1.663 2.025-.172zm-10.569.031l-.739.037-.238.565.72-.016zm3.673.362l-1.289.068-.41.978 1.305-.05zm-2.285.533l-.976.154-.326.805.96-.12.342-.84zm4.153.07l-1.596.066-.565 1.356 1.612-.084zm1.957.666l-1.889.154-.617 1.526 1.886-.15zm2.28.223l-2.025.12-.685 1.665 2.041-.172.67-1.613zm-10.584.05l-.738.053L0 13.64l.72-.02.24-.6zm3.705.31l-1.285.07-.395.976 1.287-.05.393-.997zm11.923.07c1.08.29 2.024.821 2.814 1.613l-1.715 4.183c-.892-.754-1.82-1.32-2.814-1.664l1.715-4.133zm-10.036.515L4.956 14l-.549 1.32 1.578-.066.567-1.338zm-4.184.014l-.996.156-.309.79.961-.106zm6.14.67l-1.904.154-.617 1.527 1.89-.154.632-1.527zm2.231.324l-2.025.123-.686 1.682 2.026-.174zm-6.863.328l-1.3.068-.397.98 1.285-.054zm1.871.584l-1.578.068-.566 1.334 1.595-.064zm1.953.701l-1.867.137-.635 1.51 1.87-.137zm2.23.31l-2.005.122-.703 1.68 2.04-.19.67-1.61z"></path> <path d="M5.712 1.596l-.756.068-.238.55.734-.017zm1.39.927l-.978.137-.326.807.96-.12.345-.824zM4.89 3.535l-.72.05-.24.567.721-.017zm3.724.309l-1.287.068-.394.96 1.27-.052zm1.87.566l-1.579.069-.566 1.357 1.596-.088.548-1.338zm-4.188.037l-.977.153-.343.806.976-.12zm6.144.668l-1.87.135-.637 1.527 1.87-.154zm2.925.219c-.11 0-.222 0-.334.002l-.767 1.85c1.394-.03 2.52.089 3.373.38l-1.748 4.201c-.955-.304-2.082-.444-3.36-.394l-.54 1.305a8.762 8.762 0 0 1 3.365.396l-1.663 4.014c-1.257-.27-2.382-.395-3.387-.344l-.782 1.887c3.363-.446 6.348.822 9.009 3.773L24 9.23c-2.325-2.575-5.2-3.88-8.637-3.896zm-.644.002l-2.024.12-.687 1.68 2.025-.19zm-10.603.05l-.719.036-.224.566h.703l.24-.601zm3.69.397l-1.287.069-.395.959 1.27-.05zM5.54 6.3l-.994.154-.344.807.98-.121zm4.137.066l-1.58.069L7.53 7.77l1.596-.085.55-1.32zm1.955.688l-1.87.135-.636 1.527 1.887-.154zm2.282.19l-2.01.136-.7 1.682 2.04-.19.67-1.63zm-10.57.066l-.739.035-.238.564h.72l.257-.6zm3.705.293l-1.303.085-.394.96 1.287-.034zm11.839.255a6.718 6.718 0 0 1 2.777 1.717l-1.75 4.237c-.617-.584-1.15-.961-1.611-1.149l-1.201-.498zM4.733 8.22l-.976.154-.344.807.961-.12.36-.841zm4.186 0l-1.594.052-.549 1.354L8.37 9.54zm1.957.668L8.99 9.04l-.619 1.508 1.87-.135.636-1.527zm2.247.275l-2.007.12-.703 1.665 2.042-.156zM2.52 9.267l-.718.033-.24.549.718-.016zm3.725.273l-1.289.07-.41.96 1.287-.03.412-1zm1.87.6l-1.596.05-.55 1.356 1.598-.084.547-1.322zm-4.186.037l-.979.136-.324.805.96-.119zm6.14.633l-1.87.154-.653 1.527 1.906-.154zm2.267.275l-2.026.12-.686 1.663 2.025-.172zm-10.569.031l-.739.037-.238.565.72-.016zm3.673.362l-1.289.068-.41.978 1.305-.05zm-2.285.533l-.976.154-.326.805.96-.12.342-.84zm4.153.07l-1.596.066-.565 1.356 1.612-.084zm1.957.666l-1.889.154-.617 1.526 1.886-.15zm2.28.223l-2.025.12-.685 1.665 2.041-.172.67-1.613zm-10.584.05l-.738.053L0 13.64l.72-.02.24-.6zm3.705.31l-1.285.07-.395.976 1.287-.05.393-.997zm11.923.07c1.08.29 2.024.821 2.814 1.613l-1.715 4.183c-.892-.754-1.82-1.32-2.814-1.664l1.715-4.133zm-10.036.515L4.956 14l-.549 1.32 1.578-.066.567-1.338zm-4.184.014l-.996.156-.309.79.961-.106zm6.14.67l-1.904.154-.617 1.527 1.89-.154.632-1.527zm2.231.324l-2.025.123-.686 1.682 2.026-.174zm-6.863.328l-1.3.068-.397.98 1.285-.054zm1.871.584l-1.578.068-.566 1.334 1.595-.064zm1.953.701l-1.867.137-.635 1.51 1.87-.137zm2.23.31l-2.005.122-.703 1.68 2.04-.19.67-1.61z" />
</svg>{" "} </svg>{" "}
Click here for the <em>full</em> experience anyway. Click here for the <em>full</em> experience anyway.
</Link> </Link>
@ -96,7 +96,6 @@ export default async function Page() {
<iframe <iframe
src="https://jakejarvis.github.io/my-first-website/" src="https://jakejarvis.github.io/my-first-website/"
title="My Terrible, Horrible, No Good, Very Bad First Website" title="My Terrible, Horrible, No Good, Very Bad First Website"
sandbox="allow-same-origin allow-scripts allow-popups"
className={styles.iframe} className={styles.iframe}
style={{ height: "500px" }} style={{ height: "500px" }}
/> />
@ -188,4 +187,6 @@ export default async function Page() {
</Figure> </Figure>
</div> </div>
); );
} };
export default Page;

View File

@ -4,7 +4,7 @@ import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link"; import Link from "../../components/Link";
import RelativeTime from "../../components/RelativeTime"; import RelativeTime from "../../components/RelativeTime";
import commaNumber from "comma-number"; import commaNumber from "comma-number";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import { metadata as defaultMetadata } from "../layout"; import { metadata as defaultMetadata } from "../layout";
import type { Metadata } from "next"; import type { Metadata } from "next";
import type { User, Repository } from "@octokit/graphql-schema"; import type { User, Repository } from "@octokit/graphql-schema";
@ -39,7 +39,7 @@ type Project = {
updatedAt: string; updatedAt: string;
}; };
async function getRepos(): Promise<Project[] | null> { const getRepos = async (): Promise<Project[] | null> => {
// don't fail the entire site build if the required API key for this page is missing // don't fail the entire site build if the required API key for this page is missing
if (!process.env.GITHUB_TOKEN) { if (!process.env.GITHUB_TOKEN) {
console.warn(`ERROR: I can't fetch any GitHub projects without "GITHUB_TOKEN" set! Skipping for now...`); console.warn(`ERROR: I can't fetch any GitHub projects without "GITHUB_TOKEN" set! Skipping for now...`);
@ -102,9 +102,9 @@ async function getRepos(): Promise<Project[] | null> {
})); }));
return repos; return repos;
} };
export default async function Page() { const Page = async () => {
const repos = await getRepos(); const repos = await getRepos();
return ( return (
@ -190,11 +190,13 @@ export default async function Page() {
fill: "var(--colors-text)", fill: "var(--colors-text)",
}} }}
> >
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"></path> <path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
</svg>{" "} </svg>{" "}
GitHub... GitHub...
</Link> </Link>
</p> </p>
</> </>
); );
} };
export default Page;

View File

@ -1,4 +1,4 @@
import config from "../lib/config/constants"; import config from "../lib/config";
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
export const dynamic = "force-static"; export const dynamic = "force-static";

View File

@ -1,7 +1,7 @@
import path from "path"; import path from "path";
import glob from "fast-glob"; import glob from "fast-glob";
import { getAllPosts } from "../lib/helpers/posts"; import { getAllPosts } from "../lib/helpers/posts";
import config from "../lib/config/constants"; import config from "../lib/config";
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
export const dynamic = "force-static"; export const dynamic = "force-static";

View File

@ -19,7 +19,7 @@ export const metadata: Metadata = {
}, },
}; };
export default async function Page() { const Page = () => {
return ( return (
<div <div
style={{ style={{
@ -73,4 +73,6 @@ export default async function Page() {
</CodeBlock> </CodeBlock>
</div> </div>
); );
} };
export default Page;

View File

@ -3,7 +3,9 @@ import type { ComponentPropsWithoutRef } from "react";
import styles from "./Blockquote.module.css"; import styles from "./Blockquote.module.css";
const Blockquote = ({ className, ...rest }: ComponentPropsWithoutRef<"blockquote">) => ( export type BlockquoteProps = ComponentPropsWithoutRef<"blockquote">;
const Blockquote = ({ className, ...rest }: BlockquoteProps) => (
<blockquote className={clsx(styles.blockquote, className)} {...rest} /> <blockquote className={clsx(styles.blockquote, className)} {...rest} />
); );

View File

@ -3,7 +3,9 @@ import type { ComponentPropsWithoutRef } from "react";
import styles from "./CodeInline.module.css"; import styles from "./CodeInline.module.css";
const CodeInline = ({ className, ...rest }: ComponentPropsWithoutRef<"code">) => ( export type CodeInlineProps = ComponentPropsWithoutRef<"code">;
const CodeInline = ({ className, ...rest }: CodeInlineProps) => (
<code className={clsx(styles.codeInline, className)} {...rest} /> <code className={clsx(styles.codeInline, className)} {...rest} />
); );

View File

@ -22,9 +22,7 @@ const CodePen = ({
preview: `${!!preview}`, preview: `${!!preview}`,
editable: `${!!editable}`, editable: `${!!editable}`,
})}`} })}`}
scrolling="no" style={{ height: `${height}px`, width: "100%", border: "0", overflow: "hidden" }}
sandbox="allow-same-origin allow-scripts allow-popups allow-top-navigation-by-user-activation"
style={{ height: `${height}px`, width: "100%", border: "0" }}
/> />
); );
}; };

View File

@ -1,7 +1,7 @@
"use client"; "use client";
import Giscus from "@giscus/react"; import Giscus from "@giscus/react";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import type { GiscusProps } from "@giscus/react"; import type { GiscusProps } from "@giscus/react";
export type CommentsProps = { export type CommentsProps = {

View File

@ -1,7 +1,7 @@
import clsx from "clsx"; import clsx from "clsx";
import { HeartIcon } from "lucide-react"; import { HeartIcon } from "lucide-react";
import Link from "../Link"; import Link from "../Link";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import type { ComponentPropsWithoutRef } from "react"; import type { ComponentPropsWithoutRef } from "react";
import styles from "./Footer.module.css"; import styles from "./Footer.module.css";
@ -47,7 +47,7 @@ const Footer = ({ className, ...rest }: FooterProps) => {
width="1.25em" width="1.25em"
className={styles.icon} className={styles.icon}
> >
<path d="M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z"></path> <path d="M18.665 21.978C16.758 23.255 14.465 24 12 24 5.377 24 0 18.623 0 12S5.377 0 12 0s12 5.377 12 12c0 3.583-1.574 6.801-4.067 9.001L9.219 7.2H7.2v9.596h1.615V9.251l9.85 12.727Zm-3.332-8.533 1.6 2.061V7.2h-1.6v6.245Z" />
</svg> </svg>
</Link> </Link>
.{" "} .{" "}

View File

@ -12,7 +12,7 @@ const Gist = async ({ id, file }: GistProps) => {
const scriptResponse = await fetch(scriptUrl); const scriptResponse = await fetch(scriptUrl);
if (!scriptResponse.ok) { if (!scriptResponse.ok) {
console.warn(`[gist] fetching js for https://gist.github.com/${id} failed:`, scriptResponse.statusText); console.warn(`[gist] failed to fetch js:`, scriptResponse.statusText);
return ( return (
<p style={{ textAlign: "center" }}> <p style={{ textAlign: "center" }}>
@ -33,10 +33,9 @@ const Gist = async ({ id, file }: GistProps) => {
scrolling="no" scrolling="no"
id={iframeId} id={iframeId}
srcDoc={iframeHtml} srcDoc={iframeHtml}
sandbox="allow-same-origin allow-scripts allow-popups allow-top-navigation-by-user-activation"
style={{ border: "0", overflow: "hidden" }} style={{ border: "0", overflow: "hidden" }}
suppressHydrationWarning suppressHydrationWarning
></iframe> />
); );
}; };

View File

@ -2,7 +2,7 @@ import clsx from "clsx";
import Link from "../Link"; import Link from "../Link";
import Image from "../Image"; import Image from "../Image";
import Menu from "../Menu"; import Menu from "../Menu";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import type { ComponentPropsWithoutRef } from "react"; import type { ComponentPropsWithoutRef } from "react";
import styles from "./Header.module.css"; import styles from "./Header.module.css";

View File

@ -3,7 +3,9 @@ import type { ComponentPropsWithoutRef } from "react";
import styles from "./HorizontalRule.module.css"; import styles from "./HorizontalRule.module.css";
const HorizontalRule = ({ className, ...rest }: ComponentPropsWithoutRef<"hr">) => ( export type HorizontalRuleProps = ComponentPropsWithoutRef<"hr">;
const HorizontalRule = ({ className, ...rest }: HorizontalRuleProps) => (
<hr className={clsx(styles.hr, className)} {...rest} /> <hr className={clsx(styles.hr, className)} {...rest} />
); );

View File

@ -1,7 +1,7 @@
import NextLink from "next/link"; import NextLink from "next/link";
import clsx from "clsx"; import clsx from "clsx";
import objStr from "obj-str"; import objStr from "obj-str";
import config from "../../lib/config/constants"; import config from "../../lib/config";
import type { ComponentPropsWithoutRef } from "react"; import type { ComponentPropsWithoutRef } from "react";
import styles from "./Link.module.css"; import styles from "./Link.module.css";

View File

@ -79,7 +79,8 @@ export const ThemeProvider = ({ children }: PropsWithChildren) => {
id="restore-theme" id="restore-theme"
// unminified: https://gist.github.com/jakejarvis/79b0ec8506bc843023546d0d29861bf0 // unminified: https://gist.github.com/jakejarvis/79b0ec8506bc843023546d0d29861bf0
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: `(()=>{try{const e=document.documentElement,t="undefined"!=typeof Storage?window.localStorage.getItem("theme"):null,a=(t&&"dark"===t)??window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light";e.dataset.theme=a,e.style.colorScheme=a}catch(e){}})()`, __html:
"(()=>{try{const e=document.documentElement,t='undefined'!=typeof Storage?window.localStorage.getItem('theme'):null,a=(t&&'dark'===t)??window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light';e.dataset.theme=a,e.style.colorScheme=a}catch(e){}})()",
}} }}
/> />

View File

@ -1,8 +1,9 @@
const constants = { const config = {
// Site info // Site info
siteName: "Jake Jarvis", siteName: "Jake Jarvis",
siteLocale: "en-US", siteLocale: "en-US",
baseUrl: baseUrl:
// same logic as metadataBase: https://nextjs.org/docs/app/api-reference/functions/generate-metadata#default-value
process.env.NEXT_PUBLIC_VERCEL_ENV === "production" && process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL process.env.NEXT_PUBLIC_VERCEL_ENV === "production" && process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL
? `https://${process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL}` ? `https://${process.env.NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL}`
: process.env.NEXT_PUBLIC_VERCEL_ENV === "preview" && process.env.NEXT_PUBLIC_VERCEL_URL : process.env.NEXT_PUBLIC_VERCEL_ENV === "preview" && process.env.NEXT_PUBLIC_VERCEL_URL
@ -35,4 +36,4 @@ const constants = {
}, },
}; };
export default constants; export default config;

View File

@ -1,6 +1,6 @@
import { Feed } from "feed"; import { Feed } from "feed";
import { getAllPosts } from "./posts"; import { getAllPosts } from "./posts";
import config from "../config/constants"; import config from "../config";
import meJpg from "../../app/me.jpg"; import meJpg from "../../app/me.jpg";

View File

@ -5,7 +5,7 @@ import dayjsRelativeTime from "dayjs/plugin/relativeTime";
import dayjsLocalizedFormat from "dayjs/plugin/localizedFormat"; import dayjsLocalizedFormat from "dayjs/plugin/localizedFormat";
import dayjsAdvancedFormat from "dayjs/plugin/advancedFormat"; import dayjsAdvancedFormat from "dayjs/plugin/advancedFormat";
import "dayjs/locale/en"; import "dayjs/locale/en";
import config from "../config/constants"; import config from "../config";
const IsomorphicDayJs = (date?: dayjs.ConfigType): dayjs.Dayjs => { const IsomorphicDayJs = (date?: dayjs.ConfigType): dayjs.Dayjs => {
// plugins // plugins

View File

@ -3,7 +3,7 @@ import glob from "fast-glob";
import pMap from "p-map"; import pMap from "p-map";
import pMemoize from "p-memoize"; import pMemoize from "p-memoize";
import { formatDate } from "./format-date"; import { formatDate } from "./format-date";
import config from "../config/constants"; import config from "../config";
// path to directory with .mdx files, relative to project root // path to directory with .mdx files, relative to project root
const POSTS_DIR = "notes"; const POSTS_DIR = "notes";

View File

@ -14,7 +14,7 @@ import YouTube from "./components/YouTube";
import Gist from "./components/Gist"; import Gist from "./components/Gist";
import CodePen from "./components/CodePen"; import CodePen from "./components/CodePen";
export function useMDXComponents(components: MDXComponents): MDXComponents { export const useMDXComponents = (components: MDXComponents): MDXComponents => {
return { return {
...components, ...components,
@ -44,4 +44,4 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
Gist, Gist,
CodePen, CodePen,
}; };
} };

View File

@ -1,17 +1,17 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import type { NextRequest } from "next/server"; import type { NextRequest } from "next/server";
import siteConfig from "./lib/config/constants"; import siteConfig from "./lib/config";
// assign "short codes" to approved reverse proxy destinations. for example: // assign "short codes" to approved reverse proxy destinations. for example:
// ["abc", "https://jakejarvis.github.io/blah"] => /_stream/abc/123.html -> https://jakejarvis.github.io/blah/123.html // ["abc", "https://jakejarvis.github.io"] => /_stream/abc/123.html -> https://jakejarvis.github.io/123.html
const rewritePrefix = "/_stream/"; const rewritePrefix = "/_stream/";
const rewrites = new Map([ const rewrites = new Map([
// umami backend, see https://umami.is/docs/guides/running-on-vercel#proxy-umami-analytics-via-vercel // umami backend, see https://umami.is/docs/guides/running-on-vercel#proxy-umami-analytics-via-vercel
["u", process.env.NEXT_PUBLIC_UMAMI_HOST || "https://cloud.umami.is"], ["u", process.env.NEXT_PUBLIC_UMAMI_URL || "https://cloud.umami.is"],
]); ]);
export function middleware(request: NextRequest) { export const middleware = (request: NextRequest) => {
const headers = new Headers(); const headers = new Headers();
// https://gitweb.torproject.org/tor-browser-spec.git/tree/proposals/100-onion-location-header.txt // https://gitweb.torproject.org/tor-browser-spec.git/tree/proposals/100-onion-location-header.txt
@ -31,9 +31,6 @@ export function middleware(request: NextRequest) {
// search the rewrite map for the short code // search the rewrite map for the short code
const proxiedOrigin = rewrites.get(key); const proxiedOrigin = rewrites.get(key);
// TODO: remove debugging headers
headers.set("x-middleware-proxy-key", key);
// return a 400 error if a rewrite was requested but the short code isn't found // return a 400 error if a rewrite was requested but the short code isn't found
if (!proxiedOrigin) { if (!proxiedOrigin) {
return NextResponse.json( return NextResponse.json(
@ -50,7 +47,7 @@ export function middleware(request: NextRequest) {
const proxiedUrl = new URL(`${proxiedPath}${request.nextUrl.search}`, proxiedOrigin); const proxiedUrl = new URL(`${proxiedPath}${request.nextUrl.search}`, proxiedOrigin);
// TODO: remove debugging headers // TODO: remove debugging headers
headers.set("x-middleware-proxy-to", proxiedUrl.toString()); headers.set("x-middleware-rewrite", proxiedUrl.toString());
// finally do the rewriting // finally do the rewriting
return NextResponse.rewrite(proxiedUrl, { return NextResponse.rewrite(proxiedUrl, {
@ -64,7 +61,7 @@ export function middleware(request: NextRequest) {
request, request,
headers, headers,
}); });
} };
export const config = { export const config = {
// save compute time by skipping middleware for static and metadata files // save compute time by skipping middleware for static and metadata files

View File

@ -40,23 +40,13 @@ const nextConfig: NextConfig = {
], ],
}, },
], ],
rewrites: async () => ({ rewrites: async () => [
beforeFiles: [ {
{ // https://github.com/jakejarvis/tweets
// https://github.com/jakejarvis/tweets source: "/tweets/:path*",
source: "/tweets/:path*", destination: "https://tweets-khaki.vercel.app/:path*",
destination: "https://tweets-khaki.vercel.app/:path*", },
}, ],
],
afterFiles: [
{
// access security.txt, etc at both /security.txt and /.well-known/security.txt
source: "/.well-known/:slug.txt",
destination: "/:slug.txt",
},
],
fallback: [],
}),
redirects: async () => [ redirects: async () => [
{ source: "/y2k", destination: "https://y2k.pages.dev", permanent: false }, { source: "/y2k", destination: "https://y2k.pages.dev", permanent: false },
{ {

View File

@ -28,7 +28,6 @@ I've written a simple implementation below, which...
title="Dark Mode Example" title="Dark Mode Example"
loading="lazy" loading="lazy"
style={{ height: "190px", width: "100%" }} style={{ height: "190px", width: "100%" }}
sandbox="allow-same-origin allow-scripts allow-popups allow-top-navigation-by-user-activation"
></iframe> ></iframe>
A _very_ barebones example is embedded above ([view the source here](https://github.com/jakejarvis/dark-mode-example), or [open in a new window](https://jakejarvis.github.io/dark-mode-example/) if your browser is blocking the frame) and you can try it out on this site by clicking the 💡 lightbulb in the upper right corner of this page. You'll notice that the dark theme sticks when refreshing this page, navigating between other pages, or if you were to return to this example weeks from now. A _very_ barebones example is embedded above ([view the source here](https://github.com/jakejarvis/dark-mode-example), or [open in a new window](https://jakejarvis.github.io/dark-mode-example/) if your browser is blocking the frame) and you can try it out on this site by clicking the 💡 lightbulb in the upper right corner of this page. You'll notice that the dark theme sticks when refreshing this page, navigating between other pages, or if you were to return to this example weeks from now.

View File

@ -35,7 +35,7 @@
- Umami - Umami
- Giscus - Giscus
- Resend - Resend
- ...and more: https://jarv.is/uses/ - ...and more: https://jarv.is/uses
# VIEW SOURCE # VIEW SOURCE
@ -43,7 +43,7 @@
# PRIVACY POLICY # PRIVACY POLICY
https://jarv.is/privacy/ https://jarv.is/privacy
# TOR MIRROR # TOR MIRROR
@ -54,4 +54,4 @@
All content is licensed under CC-BY-4.0, an All content is licensed under CC-BY-4.0, an
open & permissive Creative Commons license: open & permissive Creative Commons license:
https://jarv.is/license/ https://jarv.is/license

View File

@ -1,24 +0,0 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Contact: https://jarv.is/contact/
Encryption: https://jarv.is/pubkey.asc
Encryption: openpgp4fpr:3bc6e5776bf379d36f6714802b0c9cf251e69a39
Preferred-Languages: en
Expires: 2025-01-01T06:00:00.000Z
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCAAdFiEEO8bld2vzedNvZxSAKwyc8lHmmjkFAmXTsxwACgkQKwyc8lHm
mjlwqA//ZyIYleG3sh5OeVpHS0mI4Tr5crR6SG08A5DEwe8ysbQqDhPx0LxodRMd
XulfZ+TIDM3TXmw7LffeiKpRRFm0ecQtQT3hKorQgzHtUuKoPdr6gPC7Lrs9H7fE
4QlAa/WM4M/yYJfW9HZnh1/2Lh5LulQ4dkGAhb5cBvSCAWYF3W0eLjUXI6xBWyzx
FEnfTxsFCBd2h1g+S/Sc99mOvwHkcMOZflsiWCvXJb8ELwcusAK4SWYUM/bTJMil
a3BzvYinjyGEGqX2u+SNp+6ZxFYpDUCq8Vl+LPUFvlYpfjqVjiK9knZBHo2YADH0
6HPP+FdMUnZTd3gGwLsUS4NOcXWqqRivL9bWOVgklsoawjOeQtelgrGXrzhtIaYo
bbUko3kpYBy9EkGKtefQ4BYAy3CTjFjfkeadm5EOXPFdEEPjQRqdfJdniHCYG+27
xmriwobd2Ht+e3f0oOOf7Ae/ZD9MvaAA5Qn5uIbebVQaOxjetKbKGwDiPSLRJ5On
zfOsJn2qY1NVjWG6IlqNHpI1vjzmNOsFFlknELg5NOFstpXvLYGX1TkqBnI2g3f4
KuGXx4gOoq2oyoZuCi2bGO0Yn6lZ1UlEnfAO2ixW2FAPe8gqzxCeyxHzgvb4vNbr
VpmZuRxsOcdCgvItZkYCaS164GYvRTPWCtC1Q3lzAZViCOgMUmk=
=XWDQ
-----END PGP SIGNATURE-----