chore: drizzle v1, other dep updates and cleanup

This commit is contained in:
2026-07-22 18:54:59 -04:00
parent dcddf6e3ec
commit 087512a87d
55 changed files with 2782 additions and 1971 deletions
+1
View File
@@ -14,6 +14,7 @@
"order": "asc"
},
"sortTailwindcss": {
"stylesheet": "app/globals.css",
"attributes": ["className"],
"functions": ["cn", "clsx", "cva"]
},
+4
View File
@@ -4,6 +4,10 @@
"env": {
"builtin": true
},
"options": {
"typeAware": true,
"typeCheck": true
},
"categories": {
"correctness": "error",
"suspicious": "warn",
+6 -14
View File
@@ -1,6 +1,5 @@
import { IconArrowUpRight } from "@tabler/icons-react";
import Image, { type StaticImageData } from "next/image";
import Link from "next/link";
import { FadeTransition } from "@/components/page-transition";
@@ -61,21 +60,14 @@ const Page = () => (
<FadeTransition>
<h1 className="text-lg font-medium tracking-tight">
Hi there! I&rsquo;m Jake.{" "}
<span className="motion-safe:animate-wave ml-0.5 inline-block origin-[65%_80%] text-[1.2rem]">
<span className="ml-0.5 inline-block origin-[65%_80%] text-[1.2rem] motion-safe:animate-wave">
👋
</span>
</h1>
<div className="markdown">
<p className="text-sm leading-normal tracking-[-0.0125em]">
I&rsquo;m a developer based in the{" "}
<Link
href="https://www.youtube-nocookie.com/embed/rLwbzGyC6t4?hl=en&amp;fs=1&amp;showinfo=1&amp;rel=0&amp;iv_load_policy=3"
title='"Boston Accent Trailer - Late Night with Seth Meyers" on YouTube'
>
Boston
</Link>{" "}
area working on some cool stuff:
I&rsquo;m a developer based in the Boston area working on some cool stuff:
</p>
</div>
@@ -96,16 +88,16 @@ const Page = () => (
width={64}
height={64}
decoding="async"
className="ring-border size-6 shrink-0 rounded-[26%] ring-1"
className="size-6 shrink-0 rounded-[26%] ring-1 ring-border"
/>
<span className="text-primary text-sm font-medium tracking-[-0.0125em] group-hover:underline group-hover:underline-offset-4">
<span className="text-sm font-medium tracking-[-0.0125em] text-primary group-hover:underline group-hover:underline-offset-4">
{project.name}
</span>
</div>
<span className="text-muted-foreground ml-9 text-xs text-pretty sm:ml-auto">
<span className="ml-9 text-xs text-pretty text-muted-foreground sm:ml-auto">
{project.tagline}
<IconArrowUpRight
className="group-hover:text-primary ml-1 inline size-3.5 shrink-0 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5"
className="ml-1 inline size-3.5 shrink-0 transition-transform group-hover:translate-x-0.5 group-hover:-translate-y-0.5 group-hover:text-primary"
aria-hidden="true"
/>
</span>
+1 -1
View File
@@ -341,5 +341,5 @@
/* Prose */
.markdown {
@apply prose prose-neutral dark:prose-invert prose-sm prose-hr:border-foreground/20 prose-headings:font-semibold prose-headings:text-primary prose-headings:tracking-tight prose-p:text-foreground/90 prose-strong:text-primary prose-li:text-foreground/80 prose-a:text-primary prose-a:font-medium prose-a:underline prose-a:underline-offset-4 prose-blockquote:[&_p]:text-foreground/75 prose-blockquote:*:before:content-none prose-blockquote:*:after:content-none prose-code:bg-muted prose-code:text-foreground prose-code:px-1 prose-code:py-0.5 prose-code:rounded-sm prose-code:text-[0.9em] prose-code:before:content-none prose-code:after:content-none max-w-none [&_table]:!border-[color:var(--border)] [&_td]:!border-[color:var(--border)] [&_th]:!border-[color:var(--border)];
@apply prose prose-sm max-w-none prose-neutral dark:prose-invert prose-headings:font-semibold prose-headings:tracking-tight prose-headings:text-primary prose-p:text-foreground/90 prose-a:font-medium prose-a:text-primary prose-a:underline prose-a:underline-offset-4 prose-blockquote:*:before:content-none prose-blockquote:*:after:content-none prose-strong:text-primary prose-code:rounded-sm prose-code:bg-muted prose-code:px-1 prose-code:py-0.5 prose-code:text-[0.9em] prose-code:text-foreground prose-code:before:content-none prose-code:after:content-none prose-li:text-foreground/80 prose-hr:border-foreground/20 prose-blockquote:[&_p]:text-foreground/75 [&_table]:!border-[color:var(--border)] [&_td]:!border-[color:var(--border)] [&_th]:!border-[color:var(--border)];
}
+1 -1
View File
@@ -60,7 +60,7 @@ const RootLayout = ({ children }: Readonly<{ children: React.ReactNode }>) => (
/>
</head>
<body className="bg-background text-foreground font-sans antialiased">
<body className="bg-background font-sans text-foreground antialiased">
<Providers>
<div className="mx-auto w-full max-w-[720px] px-5">
<Header />
+3 -3
View File
@@ -28,8 +28,8 @@ const Page = () => (
item={{
"@context": "https://schema.org",
"@type": "VideoObject",
name: metadata.title as string,
description: metadata.description as string,
name: 'Facebook App on "The Lab with Leo Laporte"',
description: "Powncer app featured in Leo Laporte's TechTV show.",
contentUrl:
"https://ijyxfbpcm3itvdly.public.blob.vercel-storage.com/leo-uoCXHS9gViyRnQhr8CEGXFvj4VGh5Y.webm",
thumbnailUrl: `${process.env.NEXT_PUBLIC_BASE_URL}${thumbnail.src}`,
@@ -50,7 +50,7 @@ const Page = () => (
poster={thumbnail.src}
/>
<p className="text-muted-foreground mx-4 mt-5 mb-0 text-center text-sm leading-relaxed">
<p className="mx-4 mt-5 mb-0 text-center text-sm leading-relaxed text-muted-foreground">
Video is property of{" "}
<a
href="https://web.archive.org/web/20070511004304/www.g4techtv.ca"
+1 -1
View File
@@ -271,7 +271,7 @@ const OpenGraphImage = async ({ params }: { params: Promise<{ slug: string }> })
);
} catch (error) {
console.error("[/notes/[slug]/opengraph-image] error generating open graph image:", error);
notFound();
return notFound();
}
};
+3 -3
View File
@@ -104,7 +104,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
}}
/>
<div className="text-foreground/70 flex flex-wrap items-center gap-x-4 gap-y-2.5 text-[13px] tracking-wide">
<div className="flex flex-wrap items-center gap-x-4 gap-y-2.5 text-[13px] tracking-wide text-foreground/70">
<Link
href={`/${POSTS_DIR}/${post.slug}`}
className={
@@ -128,7 +128,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
<span
key={tag}
title={tag}
className="before:text-foreground/40 mx-px lowercase before:pr-0.5 before:content-['#'] first-of-type:ml-0 last-of-type:mr-0"
className="mx-px lowercase before:pr-0.5 before:text-foreground/40 before:content-['#'] first-of-type:ml-0 last-of-type:mr-0"
>
{tag}
</span>
@@ -180,7 +180,7 @@ const Page = async ({ params }: { params: Promise<{ slug: string }> }) => {
<section id="comments" className="isolate my-8 w-full border-t-2 pt-8">
<div className="mx-auto w-full max-w-3xl space-y-6">
{post.noComments ? (
<div className="bg-muted/40 flex justify-center rounded-lg px-6 py-12">
<div className="flex justify-center rounded-lg bg-muted/40 px-6 py-12">
<p className="text-center text-lg font-medium">Comments are closed.</p>
</div>
) : (
+1 -1
View File
@@ -64,7 +64,7 @@ const PostsList = async () => {
<ul className="space-y-4">
{yearPosts.map(({ slug, dateISO, dateTitle, dateDisplay, title, htmlTitle }) => (
<li className="flex text-sm leading-relaxed" key={slug}>
<span className="text-muted-foreground w-18 shrink-0 md:w-22">
<span className="w-18 shrink-0 text-muted-foreground md:w-22">
<time dateTime={dateISO} title={dateTitle} suppressHydrationWarning>
{dateDisplay}
</time>
+3 -1
View File
@@ -123,7 +123,9 @@ export const getRepos = async (): Promise<Repository[] | undefined> => {
},
);
return user.repositories.edges?.map((edge) => edge?.node as Repository);
return user.repositories.edges
?.map((edge) => edge?.node)
.filter((node): node is Repository => node != null);
} catch (error) {
console.error("[server/github] Failed to fetch repositories:", error);
return [];
+8 -8
View File
@@ -51,7 +51,7 @@ const Page = async () => {
<ActivityCalendar data={contributions} noun="contribution" />
</div>
) : (
<p className="text-muted-foreground my-4 text-center">
<p className="my-4 text-center text-muted-foreground">
Unable to load contribution data at this time.
</p>
)}
@@ -73,7 +73,7 @@ const Page = async () => {
{repos.map((repo) => (
<div
key={repo?.name}
className="border-ring/30 h-fit space-y-1.5 rounded-2xl border-1 px-4 py-3 shadow-xs"
className="h-fit space-y-1.5 rounded-2xl border-1 border-ring/30 px-4 py-3 shadow-xs"
>
<a
href={repo?.url}
@@ -85,14 +85,14 @@ const Page = async () => {
</a>
{repo?.description && (
<p className="text-foreground/85 text-xs leading-relaxed text-pretty">
<p className="text-xs leading-relaxed text-pretty text-foreground/85">
{repo?.description}
</p>
)}
<div className="flex flex-wrap gap-x-4 text-[11px] leading-loose whitespace-nowrap">
{repo?.primaryLanguage && (
<div className="text-muted-foreground inline-flex flex-nowrap items-center gap-1.5">
<div className="inline-flex flex-nowrap items-center gap-1.5 text-muted-foreground">
{repo?.primaryLanguage.color && (
<span
className="inline-block size-3 rounded-full bg-[var(--language-color)]"
@@ -111,7 +111,7 @@ const Page = async () => {
title={`${Intl.NumberFormat(process.env.NEXT_PUBLIC_SITE_LOCALE).format(repo?.stargazerCount)} ${repo?.stargazerCount === 1 ? "star" : "stars"}`}
target="_blank"
rel="noopener noreferrer"
className="text-muted-foreground hover:text-primary inline-flex flex-nowrap items-center gap-1.5 hover:no-underline"
className="inline-flex flex-nowrap items-center gap-1.5 text-muted-foreground hover:text-primary hover:no-underline"
>
<IconStar className="inline-block size-3.5 shrink-0" aria-hidden="true" />
<span>
@@ -128,7 +128,7 @@ const Page = async () => {
target="_blank"
rel="noopener noreferrer"
title={`${Intl.NumberFormat(process.env.NEXT_PUBLIC_SITE_LOCALE).format(repo?.forkCount)} ${repo?.forkCount === 1 ? "fork" : "forks"}`}
className="text-muted-foreground hover:text-primary inline-flex flex-nowrap items-center gap-1.5 hover:no-underline"
className="inline-flex flex-nowrap items-center gap-1.5 text-muted-foreground hover:text-primary hover:no-underline"
>
<IconGitFork className="inline-block size-3.5 shrink-0" aria-hidden="true" />
<span>
@@ -139,7 +139,7 @@ const Page = async () => {
</a>
)}
<div className="text-muted-foreground whitespace-nowrap">
<div className="whitespace-nowrap text-muted-foreground">
<Suspense fallback={null}>
<span>
Updated <RelativeTime date={repo?.pushedAt} />
@@ -151,7 +151,7 @@ const Page = async () => {
))}
</div>
) : (
<p className="text-muted-foreground my-4 text-center">
<p className="my-4 text-center text-muted-foreground">
Unable to load repository data at this time.
</p>
)}
+8 -10
View File
@@ -1,4 +1,5 @@
import { cacheLife } from "next/cache";
import { isValidElement } from "react";
import { codeToHtml } from "shiki";
import { CopyButton } from "@/components/copy-button";
@@ -12,10 +13,8 @@ const getTextContent = (node: React.ReactNode): string => {
if (node == null || typeof node === "boolean") return "";
if (typeof node === "string" || typeof node === "number") return String(node);
if (Array.isArray(node)) return node.map(getTextContent).join("");
if (typeof node === "object" && "props" in node) {
return getTextContent(
(node as React.ReactElement<{ children?: React.ReactNode }>).props.children,
);
if (isValidElement<{ children?: React.ReactNode }>(node)) {
return getTextContent(node.props.children);
}
return "";
};
@@ -41,7 +40,7 @@ const CodeBlock = async ({
...props
}: CodeBlockProps) => {
// Escape hatch for non-code pre blocks
if (!children || typeof children !== "object" || !("props" in children)) {
if (!isValidElement<{ children?: React.ReactNode; className?: string }>(children)) {
return (
<pre className={className} {...props}>
{children}
@@ -49,9 +48,8 @@ const CodeBlock = async ({
);
}
const codeProps = children.props as React.ComponentProps<"code">;
const codeString = getTextContent(codeProps.children).trim();
const lang = codeProps.className?.split("language-")[1] ?? "text";
const codeString = getTextContent(children.props.children).trim();
const lang = children.props.className?.split("language-")[1] ?? "text";
const html = await renderCode(codeString, lang);
@@ -66,12 +64,12 @@ const CodeBlock = async ({
data-lang={lang}
data-line-numbers={showLineNumbers || undefined}
className={cn(
"bg-code text-code-foreground overflow-x-auto overflow-y-hidden rounded-xl text-[13px] leading-normal outline-none",
"overflow-x-auto overflow-y-hidden rounded-xl bg-code text-[13px] leading-normal text-code-foreground outline-none",
"[&_span]:!bg-transparent [&_span[style*='color']]:dark:!text-(--shiki-dark)",
"[&_pre]:m-0 [&_pre]:rounded-xl [&_pre]:!bg-transparent",
"[&_code]:white-space-pre [&_code]:grid [&_code]:min-w-full [&_code]:px-4 [&_code]:py-3.5 [&_code]:[counter-reset:line]",
"[&_.line]:min-h-1lh [&_.line]:inline-block [&_.line]:w-full [&_.line]:py-0.5",
"data-[line-numbers]:[&_.line]:before:text-code-number data-[line-numbers]:[&_.line]:before:mr-6 data-[line-numbers]:[&_.line]:before:inline-block data-[line-numbers]:[&_.line]:before:w-5 data-[line-numbers]:[&_.line]:before:text-right data-[line-numbers]:[&_.line]:before:content-[counter(line)] data-[line-numbers]:[&_.line]:before:[counter-increment:line]",
"data-[line-numbers]:[&_.line]:before:mr-6 data-[line-numbers]:[&_.line]:before:inline-block data-[line-numbers]:[&_.line]:before:w-5 data-[line-numbers]:[&_.line]:before:text-right data-[line-numbers]:[&_.line]:before:text-code-number data-[line-numbers]:[&_.line]:before:content-[counter(line)] data-[line-numbers]:[&_.line]:before:[counter-increment:line]",
className,
)}
dangerouslySetInnerHTML={{ __html: html }}
+4 -4
View File
@@ -133,7 +133,7 @@ const SubmitButton = ({
// Markdown help popover (only shown for new comments)
const MarkdownHelp = () => (
<p className="text-muted-foreground text-[0.8rem] leading-relaxed">
<p className="text-[0.8rem] leading-relaxed text-muted-foreground">
<IconMarkdown className="mr-1.5 inline-block size-4 align-text-top" />
<span className="max-md:hidden">Basic&nbsp;</span>
<Popover>
@@ -141,7 +141,7 @@ const MarkdownHelp = () => (
render={
<button
type="button"
className="text-primary decoration-primary/40 cursor-pointer font-semibold no-underline decoration-2 underline-offset-4 hover:underline"
className="cursor-pointer font-semibold text-primary no-underline decoration-primary/40 decoration-2 underline-offset-4 hover:underline"
>
<span>Markdown</span>
<span className="max-md:hidden">&nbsp;syntax</span>
@@ -154,7 +154,7 @@ const MarkdownHelp = () => (
Examples:
</p>
<ul className="[&>li::marker]:text-muted-foreground my-2 list-inside list-disc pl-1 text-sm [&>li]:my-1.5 [&>li]:pl-1 [&>li]:text-nowrap [&>li::marker]:font-normal">
<ul className="my-2 list-inside list-disc pl-1 text-sm [&>li]:my-1.5 [&>li]:pl-1 [&>li]:text-nowrap [&>li::marker]:font-normal [&>li::marker]:text-muted-foreground">
<li>
<span className="font-bold">**bold**</span>
</li>
@@ -169,7 +169,7 @@ const MarkdownHelp = () => (
](https://jarv.is)
</li>
<li>
<span className="bg-muted rounded-sm px-[0.3rem] py-[0.2rem] font-mono text-sm font-medium">
<span className="rounded-sm bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-medium">
`code`
</span>
</li>
+3 -3
View File
@@ -36,7 +36,7 @@ const CommentSingle = ({ comment }: { comment: CommentWithUser }) => {
</a>
<Link
href={`#${divId}`}
className="text-muted-foreground text-xs leading-none hover:no-underline"
className="text-xs leading-none text-muted-foreground hover:no-underline"
>
<RelativeTime date={comment.createdAt} />
</Link>
@@ -46,8 +46,8 @@ const CommentSingle = ({ comment }: { comment: CommentWithUser }) => {
className={cn(
"isolate max-w-none text-[0.875rem] leading-relaxed",
"[&_p]:my-5 [&_p]:first:mt-0 [&_p]:last:mb-0",
"[&_a]:text-primary [&_a]:decoration-primary/40 [&_a]:no-underline [&_a]:decoration-2 [&_a]:underline-offset-4 [&_a]:hover:underline",
"[&_code]:bg-muted [&_code]:rounded-sm [&_code]:px-[0.3rem] [&_code]:py-[0.2rem] [&_code]:font-medium",
"[&_a]:text-primary [&_a]:no-underline [&_a]:decoration-primary/40 [&_a]:decoration-2 [&_a]:underline-offset-4 [&_a]:hover:underline",
"[&_code]:rounded-sm [&_code]:bg-muted [&_code]:px-[0.3rem] [&_code]:py-[0.2rem] [&_code]:font-medium",
"group-has-data-[intent=edit]:hidden", // hides the rendered comment when its own edit form is active
)}
>
+2 -2
View File
@@ -33,7 +33,7 @@ const Comments = async ({ slug }: { slug: string }) => {
{session ? (
<NewCommentForm slug={slug} />
) : (
<div className="bg-muted/40 flex flex-col items-center justify-center gap-y-4 rounded-lg p-6">
<div className="flex flex-col items-center justify-center gap-y-4 rounded-lg bg-muted/40 p-6">
<p className="text-center font-medium">Join the discussion by signing in:</p>
<SignIn callbackPath={`/${slug}#comments`} />
</div>
@@ -51,7 +51,7 @@ const Comments = async ({ slug }: { slug: string }) => {
))}
</div>
) : (
<div className="text-foreground/80 py-8 text-center text-lg font-medium tracking-tight">
<div className="py-8 text-center text-lg font-medium tracking-tight text-foreground/80">
Be the first to comment!
</div>
)}
+18 -13
View File
@@ -1,7 +1,6 @@
"use client";
import { IconCheck, IconClipboardCheck, IconCopy } from "@tabler/icons-react";
import copy from "copy-to-clipboard";
import * as React from "react";
import { toast } from "sonner";
@@ -28,21 +27,27 @@ function CopyButton({
[],
);
const handleCopy = () => {
const handleCopy = async () => {
if (hasCopied) return;
copy(value);
setHasCopied(true);
toast.success("Copied!", {
icon: <IconClipboardCheck className="text-foreground/85 size-4" aria-hidden="true" />,
duration: 2000,
id: "copy-button-toast-success",
});
try {
await navigator.clipboard.writeText(value);
setHasCopied(true);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
toast.success("Copied!", {
icon: <IconClipboardCheck className="size-4 text-foreground/85" aria-hidden="true" />,
duration: 2000,
id: "copy-button-toast-success",
});
} catch (error) {
console.error("failed to copy:", error);
} finally {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => setHasCopied(false), 2000);
}
timeoutRef.current = setTimeout(() => setHasCopied(false), 2000);
};
return (
@@ -52,7 +57,7 @@ function CopyButton({
size="icon"
variant={variant}
className={cn(
"text-muted-foreground bg-code hover:bg-accent dark:hover:bg-accent absolute top-3 right-2 z-10 size-7.5 hover:opacity-100 focus-visible:opacity-100",
"absolute top-3 right-2 z-10 size-7.5 bg-code text-muted-foreground hover:bg-accent hover:opacity-100 focus-visible:opacity-100 dark:hover:bg-accent",
hasCopied ? "cursor-default" : "cursor-pointer",
className,
)}
+8 -7
View File
@@ -6,11 +6,13 @@ import { ReactCompareSlider, ReactCompareSliderImage } from "react-compare-slide
import { cn } from "@/lib/utils";
type ImageChildProps = Parameters<typeof getImageProps>[0];
const ImageDiff = ({
children,
className,
}: {
children: React.ReactElement[];
children: React.ReactElement<ImageChildProps>[];
className?: string;
}) => {
// Extract the two image children
@@ -20,19 +22,18 @@ const ImageDiff = ({
return null;
}
const [before, after] = children;
// Get the original image source to extract dimensions for aspect ratio
const firstChildProps = children[0].props as Parameters<typeof getImageProps>[0];
const imageSrc = firstChildProps.src;
const imageSrc = before.props.src;
const aspectRatio =
typeof imageSrc === "object" && "width" in imageSrc && "height" in imageSrc
? imageSrc.width / imageSrc.height
: 16 / 9;
// Extract image props, stripping out MDX className (margins, etc.) that would break slider layout
const beforeImageProps = getImageProps(firstChildProps).props;
const afterImageProps = getImageProps(
children[1].props as Parameters<typeof getImageProps>[0],
).props;
const beforeImageProps = getImageProps(before.props).props;
const afterImageProps = getImageProps(after.props).props;
return (
<ReactCompareSlider
+1 -1
View File
@@ -5,7 +5,7 @@ import siteConfig from "@/lib/config/site";
const Footer = () => (
<footer
style={{ viewTransitionName: "persistent-footer" }}
className="text-muted-foreground border-border mt-8 w-full border-t py-6 text-xs leading-loose"
className="mt-8 w-full border-t border-border py-6 text-xs leading-loose text-muted-foreground"
>
All content is licensed under{" "}
<Link href="/license" className="underline underline-offset-2">
+5 -5
View File
@@ -243,7 +243,7 @@ const ContactPopover = () => (
href={link.href}
target={link.external ? "_blank" : undefined}
rel={`me${link.external ? " noopener noreferrer" : ""}`}
className="hover:bg-muted focus-visible:border-ring focus-visible:ring-ring/50 flex items-center gap-2 rounded-md px-1.5 py-1.5 no-underline transition-colors outline-none focus-visible:ring-3"
className="flex items-center gap-2 rounded-md px-1.5 py-1.5 no-underline transition-colors outline-none hover:bg-muted focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
>
<Tooltip disableHoverablePopup>
<TooltipTrigger>
@@ -255,7 +255,7 @@ const ContactPopover = () => (
{link.value}
</span>
{link.external ? (
<IconExternalLink className="text-muted-foreground/70 size-3.5" aria-hidden="true" />
<IconExternalLink className="size-3.5 text-muted-foreground/70" aria-hidden="true" />
) : null}
</a>
))}
@@ -292,7 +292,7 @@ const Header = ({ className }: { className?: string }) => {
"bg-background/0 backdrop-blur-none",
"data-[scrolled=true]:bg-background/80",
"data-[scrolled=true]:backdrop-blur-md",
"data-[scrolled=true]:border-border/70 data-[scrolled=true]:border-b",
"data-[scrolled=true]:border-b data-[scrolled=true]:border-border/70",
className,
)}
>
@@ -304,14 +304,14 @@ const Header = ({ className }: { className?: string }) => {
transitionTypes={pathname === "/" ? undefined : ["nav-lateral"]}
aria-label={siteConfig.name}
className={cn(
"hover:text-foreground/85 flex shrink-0 items-center",
"flex shrink-0 items-center hover:text-foreground/85",
"gap-2.5 pr-2 hover:no-underline",
)}
>
<Image
src={avatarImg}
alt={`Photo of ${siteConfig.name}`}
className="border-ring/30 size-7 rounded-full border"
className="size-7 rounded-full border border-ring/30"
width={40}
height={40}
quality={75}
+1 -1
View File
@@ -39,7 +39,7 @@ const Menu = () => {
nativeButton={false}
aria-label={item.text}
data-current={isCurrent || undefined}
className="data-current:bg-accent/60 data-current:text-accent-foreground px-2.5 py-3.5 text-sm leading-none"
className="px-2.5 py-3.5 text-sm leading-none data-current:bg-accent/60 data-current:text-accent-foreground"
render={<Link href={item.href} transitionTypes={transitionTypes} />}
>
{item.text}
+1 -1
View File
@@ -19,7 +19,7 @@ const PageTitle = ({
>
<Link
href={canonical}
className="text-foreground before:text-muted-foreground no-underline before:tracking-wider before:content-['./']"
className="text-foreground no-underline before:tracking-wider before:text-muted-foreground before:content-['./']"
>
{children}
</Link>
+1 -1
View File
@@ -20,7 +20,7 @@ const Marquee = ({
.map((_, i) => (
<div
key={i}
className="motion-safe:animate-marquee flex shrink-0 flex-row justify-around [gap:var(--gap)]"
className="flex shrink-0 flex-row justify-around [gap:var(--gap)] motion-safe:animate-marquee"
>
{children}
</div>
+2 -2
View File
@@ -72,7 +72,7 @@ const PostStats = ({ slug }: { slug: string }) => {
{viewCount > 0 && (
<Badge
variant="secondary"
className="text-foreground/80 gap-[5px] text-[11px] tabular-nums"
className="gap-[5px] text-[11px] text-foreground/80 tabular-nums"
>
<IconEye className="text-foreground/65" aria-hidden="true" />
{numberFormatter.format(viewCount)}
@@ -82,7 +82,7 @@ const PostStats = ({ slug }: { slug: string }) => {
{commentCount > 0 && (
<Badge
variant="secondary"
className="text-foreground/80 gap-[5px] text-[11px] tabular-nums"
className="gap-[5px] text-[11px] text-foreground/80 tabular-nums"
render={
<Link
href={`/${slug}#comments`}
+2 -2
View File
@@ -20,8 +20,8 @@ const CodePen = ({
<iframe
src={`https://codepen.io/${username}/embed/${id}/?${new URLSearchParams({
"default-tab": `${defaultTab},result`,
preview: `${!!preview}`,
editable: `${!!editable}`,
preview: `${preview}`,
editable: `${editable}`,
})}`}
title={title}
sandbox="allow-scripts allow-popups allow-forms"
+3 -3
View File
@@ -7,8 +7,8 @@ import YouTubeEmbed from "react-lite-youtube-embed";
const YouTube = ({
title = "YouTube video",
...rest
}: React.ComponentProps<typeof YouTubeEmbed>) => (
<YouTubeEmbed cookie={false} containerElement="div" title={title} {...rest} />
);
}: Omit<React.ComponentProps<typeof YouTubeEmbed>, "title"> & {
title?: string;
}) => <YouTubeEmbed cookie={false} containerElement="div" title={title} {...rest} />;
export { YouTube };
+5 -5
View File
@@ -23,7 +23,7 @@ function AlertDialogOverlay({ className, ...props }: AlertDialogPrimitive.Backdr
<AlertDialogPrimitive.Backdrop
data-slot="alert-dialog-overlay"
className={cn(
"data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs",
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className,
)}
{...props}
@@ -45,7 +45,7 @@ function AlertDialogContent({
data-slot="alert-dialog-content"
data-size={size}
className={cn(
"group/alert-dialog-content bg-popover text-popover-foreground ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl p-4 ring-1 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm",
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
@@ -72,7 +72,7 @@ function AlertDialogFooter({ className, ...props }: React.ComponentProps<"div">)
<div
data-slot="alert-dialog-footer"
className={cn(
"bg-muted/50 -mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className,
)}
{...props}
@@ -85,7 +85,7 @@ function AlertDialogMedia({ className, ...props }: React.ComponentProps<"div">)
<div
data-slot="alert-dialog-media"
className={cn(
"bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-md sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
"mb-2 inline-flex size-10 items-center justify-center rounded-md bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6",
className,
)}
{...props}
@@ -117,7 +117,7 @@ function AlertDialogDescription({
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn(
"text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3",
"text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className,
)}
{...props}
+5 -5
View File
@@ -17,7 +17,7 @@ function Avatar({
data-slot="avatar"
data-size={size}
className={cn(
"group/avatar after:border-border relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
"group/avatar relative flex size-8 shrink-0 rounded-full select-none after:absolute after:inset-0 after:rounded-full after:border after:border-border after:mix-blend-darken data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
className,
)}
{...props}
@@ -40,7 +40,7 @@ function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props)
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
"bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
"flex size-full items-center justify-center rounded-full bg-muted text-sm text-muted-foreground group-data-[size=sm]/avatar:text-xs",
className,
)}
{...props}
@@ -53,7 +53,7 @@ function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) {
<span
data-slot="avatar-badge"
className={cn(
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
@@ -69,7 +69,7 @@ function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="avatar-group"
className={cn(
"group/avatar-group *:data-[slot=avatar]:ring-background flex -space-x-2 *:data-[slot=avatar]:ring-2",
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
className,
)}
{...props}
@@ -82,7 +82,7 @@ function AvatarGroupCount({ className, ...props }: React.ComponentProps<"div">)
<div
data-slot="avatar-group-count"
className={cn(
"bg-muted text-muted-foreground ring-background relative flex size-8 shrink-0 items-center justify-center rounded-full text-sm ring-2 group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
className,
)}
{...props}
+1 -1
View File
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const badgeVariants = cva(
"group/badge focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:ring-[3px] has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:pointer-events-none [&>svg]:size-3!",
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
{
variants: {
variant: {
+1 -1
View File
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
const buttonVariants = cva(
"group/button focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-50 aria-invalid:ring-3 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
+4 -4
View File
@@ -28,7 +28,7 @@ function DialogOverlay({ className, ...props }: DialogPrimitive.Backdrop.Props)
<DialogPrimitive.Backdrop
data-slot="dialog-overlay"
className={cn(
"data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs",
"fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className,
)}
{...props}
@@ -50,7 +50,7 @@ function DialogContent({
<DialogPrimitive.Popup
data-slot="dialog-content"
className={cn(
"bg-popover text-popover-foreground ring-foreground/10 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl p-4 text-sm ring-1 duration-100 outline-none sm:max-w-sm",
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
@@ -88,7 +88,7 @@ function DialogFooter({
<div
data-slot="dialog-footer"
className={cn(
"bg-muted/50 -mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t p-4 sm:flex-row sm:justify-end",
"-mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t bg-muted/50 p-4 sm:flex-row sm:justify-end",
className,
)}
{...props}
@@ -116,7 +116,7 @@ function DialogDescription({ className, ...props }: DialogPrimitive.Description.
<DialogPrimitive.Description
data-slot="dialog-description"
className={cn(
"text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3",
"text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className,
)}
{...props}
+9 -9
View File
@@ -39,7 +39,7 @@ function DropdownMenuContent({
<MenuPrimitive.Popup
data-slot="dropdown-menu-content"
className={cn(
"bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 shadow-md ring-1 duration-100 outline-none data-closed:overflow-hidden",
"z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
@@ -65,7 +65,7 @@ function DropdownMenuLabel({
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"text-muted-foreground px-1.5 py-1 text-xs font-medium data-inset:pl-7",
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
className,
)}
{...props}
@@ -88,7 +88,7 @@ function DropdownMenuItem({
data-inset={inset}
data-variant={variant}
className={cn(
"group/dropdown-menu-item focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:*:[svg]:text-destructive relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-7 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
className,
)}
{...props}
@@ -113,7 +113,7 @@ function DropdownMenuSubTrigger({
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none data-inset:pl-7 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
@@ -136,7 +136,7 @@ function DropdownMenuSubContent({
<DropdownMenuContent
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 w-auto min-w-[96px] rounded-lg p-1 shadow-lg ring-1 duration-100",
"w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
align={align}
@@ -162,7 +162,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-7 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
checked={checked}
@@ -198,7 +198,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-7 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
@@ -220,7 +220,7 @@ function DropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.
return (
<MenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
className={cn("-mx-1 my-1 h-px bg-border", className)}
{...props}
/>
);
@@ -231,7 +231,7 @@ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"spa
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground ml-auto text-xs tracking-widest",
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
className,
)}
{...props}
+6 -6
View File
@@ -51,7 +51,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
);
}
const fieldVariants = cva("group/field data-[invalid=true]:text-destructive flex w-full gap-2", {
const fieldVariants = cva("group/field flex w-full gap-2 data-[invalid=true]:text-destructive", {
variants: {
orientation: {
vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
@@ -98,7 +98,7 @@ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>)
<Label
data-slot="field-label"
className={cn(
"group/field-label peer/field-label has-data-checked:border-primary/30 has-data-checked:bg-primary/5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5",
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
className,
)}
@@ -125,9 +125,9 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
<p
data-slot="field-description"
className={cn(
"text-muted-foreground text-left text-sm leading-normal font-normal group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
"text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
"last:mt-0 nth-last-2:-mt-1",
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
"[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className,
)}
{...props}
@@ -155,7 +155,7 @@ function FieldSeparator({
<Separator className="absolute inset-0 top-1/2" />
{children && (
<span
className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
data-slot="field-separator-content"
>
{children}
@@ -203,7 +203,7 @@ function FieldError({
<div
role="alert"
data-slot="field-error"
className={cn("text-destructive text-sm font-normal", className)}
className={cn("text-sm font-normal text-destructive", className)}
{...props}
>
{content}
+1 -1
View File
@@ -9,7 +9,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type}
data-slot="input"
className={cn(
"border-input file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 h-8 w-full min-w-0 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-3 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className,
)}
{...props}
+1 -1
View File
@@ -34,7 +34,7 @@ function PopoverContent({
<PopoverPrimitive.Popup
data-slot="popover-content"
className={cn(
"bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 outline-hidden duration-100",
"z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
+8 -8
View File
@@ -41,14 +41,14 @@ function SelectTrigger({
data-slot="select-trigger"
data-size={size}
className={cn(
"border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex w-fit items-center justify-between gap-1.5 rounded-lg border bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap transition-colors outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-8 data-[size=sm]:h-7 data-[size=sm]:rounded-[min(var(--radius-md),10px)] *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
>
{children}
<SelectPrimitive.Icon
render={<IconChevronDown className="text-muted-foreground pointer-events-none size-4" />}
render={<IconChevronDown className="pointer-events-none size-4 text-muted-foreground" />}
/>
</SelectPrimitive.Trigger>
);
@@ -82,7 +82,7 @@ function SelectContent({
data-slot="select-content"
data-align-trigger={alignItemWithTrigger}
className={cn(
"bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg shadow-md ring-1 duration-100 data-[align-trigger=true]:animate-none",
"relative isolate z-50 max-h-(--available-height) w-(--anchor-width) min-w-36 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
@@ -100,7 +100,7 @@ function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props)
return (
<SelectPrimitive.GroupLabel
data-slot="select-label"
className={cn("text-muted-foreground px-1.5 py-1 text-xs", className)}
className={cn("px-1.5 py-1 text-xs text-muted-foreground", className)}
{...props}
/>
);
@@ -111,7 +111,7 @@ function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Prop
<SelectPrimitive.Item
data-slot="select-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className,
)}
{...props}
@@ -134,7 +134,7 @@ function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Prop
return (
<SelectPrimitive.Separator
data-slot="select-separator"
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
className={cn("pointer-events-none -mx-1 my-1 h-px bg-border", className)}
{...props}
/>
);
@@ -148,7 +148,7 @@ function SelectScrollUpButton({
<SelectPrimitive.ScrollUpArrow
data-slot="select-scroll-up-button"
className={cn(
"bg-popover top-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
"top-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
@@ -166,7 +166,7 @@ function SelectScrollDownButton({
<SelectPrimitive.ScrollDownArrow
data-slot="select-scroll-down-button"
className={cn(
"bg-popover bottom-0 z-10 flex w-full cursor-default items-center justify-center py-1 [&_svg:not([class*='size-'])]:size-4",
"bottom-0 z-10 flex w-full cursor-default items-center justify-center bg-popover py-1 [&_svg:not([class*='size-'])]:size-4",
className,
)}
{...props}
+1 -1
View File
@@ -10,7 +10,7 @@ function Separator({ className, orientation = "horizontal", ...props }: Separato
data-slot="separator"
orientation={orientation}
className={cn(
"bg-border shrink-0 data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
className,
)}
{...props}
+1 -1
View File
@@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="skeleton"
className={cn("bg-muted animate-pulse rounded-md", className)}
className={cn("animate-pulse rounded-md bg-muted", className)}
{...props}
/>
);
+13 -9
View File
@@ -13,10 +13,21 @@ import { Spinner } from "@/components/ui/spinner";
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
const toasterTheme: ToasterProps["theme"] =
theme === "dark" || theme === "light" || theme === "system" ? theme : "system";
const style: React.CSSProperties & {
[key: `--${string}`]: string;
} = {
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
"--border-radius": "var(--radius)",
};
return (
<Sonner
theme={theme as ToasterProps["theme"]}
theme={toasterTheme}
className="toaster group"
icons={{
success: <IconCircleCheck className="size-4" />,
@@ -25,14 +36,7 @@ const Toaster = ({ ...props }: ToasterProps) => {
error: <IconCircleX className="size-4" />,
loading: <Spinner className="size-4" />,
}}
style={
{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
"--border-radius": "var(--radius)",
} as React.CSSProperties
}
style={style}
toastOptions={{
classNames: {
toast: "cn-toast",
+1 -1
View File
@@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
<textarea
data-slot="textarea"
className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 disabled:bg-input/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 flex field-sizing-content min-h-16 w-full rounded-lg border bg-transparent px-2.5 py-2 text-base transition-colors outline-none focus-visible:ring-3 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:ring-3 md:text-sm",
"flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className,
)}
{...props}
+2 -2
View File
@@ -38,13 +38,13 @@ function TooltipContent({
<TooltipPrimitive.Popup
data-slot="tooltip-content"
className={cn(
"bg-foreground text-background data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm",
"z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className,
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" />
</TooltipPrimitive.Popup>
</TooltipPrimitive.Positioner>
</TooltipPrimitive.Portal>
@@ -0,0 +1,773 @@
{
"id": "d126927d-35cf-4b6f-ab97-3872b8db26a7",
"prevIds": [
"00000000-0000-0000-0000-000000000000"
],
"version": "8",
"dialect": "postgres",
"ddl": [
{
"isRlsEnabled": false,
"name": "account",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "account_pkey",
"schema": "public",
"table": "account",
"entityType": "pks"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "account_id",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "provider_id",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "access_token",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "refresh_token",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id_token",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "access_token_expires_at",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "refresh_token_expires_at",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "scope",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "password",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "created_at",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"schema": "public",
"table": "account",
"entityType": "columns"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "account_user_id_user_id_fk",
"schema": "public",
"table": "account",
"entityType": "fks"
},
{
"isRlsEnabled": false,
"name": "comment",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "comment_pkey",
"schema": "public",
"table": "comment",
"entityType": "pks"
},
{
"type": "uuid",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "gen_random_uuid()",
"generated": null,
"identity": null,
"name": "id",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "content",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "page_slug",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "uuid",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "parent_id",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "updated_at",
"schema": "public",
"table": "comment",
"entityType": "columns"
},
{
"nameExplicit": false,
"columns": [
"page_slug"
],
"schemaTo": "public",
"tableTo": "page",
"columnsTo": [
"slug"
],
"onUpdate": "NO ACTION",
"onDelete": "NO ACTION",
"name": "comment_page_slug_page_slug_fk",
"schema": "public",
"table": "comment",
"entityType": "fks"
},
{
"nameExplicit": false,
"columns": [
"parent_id"
],
"schemaTo": "public",
"tableTo": "comment",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "comment_parent_id_comment_id_fk",
"schema": "public",
"table": "comment",
"entityType": "fks"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "comment_user_id_user_id_fk",
"schema": "public",
"table": "comment",
"entityType": "fks"
},
{
"isRlsEnabled": false,
"name": "page",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"slug"
],
"nameExplicit": false,
"name": "page_pkey",
"schema": "public",
"table": "page",
"entityType": "pks"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "slug",
"schema": "public",
"table": "page",
"entityType": "columns"
},
{
"type": "integer",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "1",
"generated": null,
"identity": null,
"name": "views",
"schema": "public",
"table": "page",
"entityType": "columns"
},
{
"isRlsEnabled": false,
"name": "session",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "session_pkey",
"schema": "public",
"table": "session",
"entityType": "pks"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "expires_at",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "token",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "created_at",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "ip_address",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_agent",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"schema": "public",
"table": "session",
"entityType": "columns"
},
{
"nameExplicit": false,
"columns": [
"token"
],
"nullsNotDistinct": false,
"name": "session_token_unique",
"schema": "public",
"table": "session",
"entityType": "uniques"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "session_user_id_user_id_fk",
"schema": "public",
"table": "session",
"entityType": "fks"
},
{
"isRlsEnabled": false,
"name": "user",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "user_pkey",
"schema": "public",
"table": "user",
"entityType": "pks"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "name",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "email",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "boolean",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "email_verified",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "image",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "created_at",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"schema": "public",
"table": "user",
"entityType": "columns"
},
{
"nameExplicit": false,
"columns": [
"email"
],
"nullsNotDistinct": false,
"name": "user_email_unique",
"schema": "public",
"table": "user",
"entityType": "uniques"
},
{
"isRlsEnabled": false,
"name": "verification",
"schema": "public",
"entityType": "tables"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "verification_pkey",
"schema": "public",
"table": "verification",
"entityType": "pks"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"schema": "public",
"table": "verification",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "identifier",
"schema": "public",
"table": "verification",
"entityType": "columns"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "value",
"schema": "public",
"table": "verification",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "expires_at",
"schema": "public",
"table": "verification",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "created_at",
"schema": "public",
"table": "verification",
"entityType": "columns"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"schema": "public",
"table": "verification",
"entityType": "columns"
}
],
"renames": []
}
@@ -0,0 +1,12 @@
ALTER TABLE "account" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "session" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "user" ALTER COLUMN "email_verified" SET DEFAULT false;--> statement-breakpoint
ALTER TABLE "user" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "user" ALTER COLUMN "updated_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "verification" ALTER COLUMN "created_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "verification" ALTER COLUMN "created_at" SET NOT NULL;--> statement-breakpoint
ALTER TABLE "verification" ALTER COLUMN "updated_at" SET DEFAULT now();--> statement-breakpoint
ALTER TABLE "verification" ALTER COLUMN "updated_at" SET NOT NULL;--> statement-breakpoint
CREATE INDEX "account_userId_idx" ON "account" ("user_id");--> statement-breakpoint
CREATE INDEX "session_userId_idx" ON "session" ("user_id");--> statement-breakpoint
CREATE INDEX "verification_identifier_idx" ON "verification" ("identifier");
@@ -0,0 +1,836 @@
{
"version": "8",
"dialect": "postgres",
"id": "e59d8dc1-96e4-4979-95ac-dd6f5039d87c",
"prevIds": [
"d126927d-35cf-4b6f-ab97-3872b8db26a7"
],
"ddl": [
{
"isRlsEnabled": false,
"name": "account",
"entityType": "tables",
"schema": "public"
},
{
"isRlsEnabled": false,
"name": "comment",
"entityType": "tables",
"schema": "public"
},
{
"isRlsEnabled": false,
"name": "page",
"entityType": "tables",
"schema": "public"
},
{
"isRlsEnabled": false,
"name": "session",
"entityType": "tables",
"schema": "public"
},
{
"isRlsEnabled": false,
"name": "user",
"entityType": "tables",
"schema": "public"
},
{
"isRlsEnabled": false,
"name": "verification",
"entityType": "tables",
"schema": "public"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "account_id",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "provider_id",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "access_token",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "refresh_token",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id_token",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "access_token_expires_at",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "refresh_token_expires_at",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "scope",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "password",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"entityType": "columns",
"schema": "public",
"table": "account"
},
{
"type": "uuid",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "gen_random_uuid()",
"generated": null,
"identity": null,
"name": "id",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "content",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "page_slug",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "uuid",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "parent_id",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "updated_at",
"entityType": "columns",
"schema": "public",
"table": "comment"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "slug",
"entityType": "columns",
"schema": "public",
"table": "page"
},
{
"type": "integer",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "1",
"generated": null,
"identity": null,
"name": "views",
"entityType": "columns",
"schema": "public",
"table": "page"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "expires_at",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "token",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "updated_at",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "ip_address",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_agent",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "user_id",
"entityType": "columns",
"schema": "public",
"table": "session"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "name",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "email",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "boolean",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "false",
"generated": null,
"identity": null,
"name": "email_verified",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "text",
"typeSchema": null,
"notNull": false,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "image",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "updated_at",
"entityType": "columns",
"schema": "public",
"table": "user"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "id",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "identifier",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"type": "text",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "value",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": null,
"generated": null,
"identity": null,
"name": "expires_at",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "created_at",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"type": "timestamp",
"typeSchema": null,
"notNull": true,
"dimensions": 0,
"default": "now()",
"generated": null,
"identity": null,
"name": "updated_at",
"entityType": "columns",
"schema": "public",
"table": "verification"
},
{
"nameExplicit": true,
"columns": [
{
"value": "user_id",
"isExpression": false,
"asc": true,
"nullsFirst": false,
"opclass": null
}
],
"isUnique": false,
"where": null,
"with": "",
"method": "btree",
"concurrently": false,
"name": "account_userId_idx",
"entityType": "indexes",
"schema": "public",
"table": "account"
},
{
"nameExplicit": true,
"columns": [
{
"value": "user_id",
"isExpression": false,
"asc": true,
"nullsFirst": false,
"opclass": null
}
],
"isUnique": false,
"where": null,
"with": "",
"method": "btree",
"concurrently": false,
"name": "session_userId_idx",
"entityType": "indexes",
"schema": "public",
"table": "session"
},
{
"nameExplicit": true,
"columns": [
{
"value": "identifier",
"isExpression": false,
"asc": true,
"nullsFirst": false,
"opclass": null
}
],
"isUnique": false,
"where": null,
"with": "",
"method": "btree",
"concurrently": false,
"name": "verification_identifier_idx",
"entityType": "indexes",
"schema": "public",
"table": "verification"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "account_user_id_user_id_fk",
"entityType": "fks",
"schema": "public",
"table": "account"
},
{
"nameExplicit": false,
"columns": [
"page_slug"
],
"schemaTo": "public",
"tableTo": "page",
"columnsTo": [
"slug"
],
"onUpdate": "NO ACTION",
"onDelete": "NO ACTION",
"name": "comment_page_slug_page_slug_fk",
"entityType": "fks",
"schema": "public",
"table": "comment"
},
{
"nameExplicit": false,
"columns": [
"parent_id"
],
"schemaTo": "public",
"tableTo": "comment",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "comment_parent_id_comment_id_fk",
"entityType": "fks",
"schema": "public",
"table": "comment"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "comment_user_id_user_id_fk",
"entityType": "fks",
"schema": "public",
"table": "comment"
},
{
"nameExplicit": false,
"columns": [
"user_id"
],
"schemaTo": "public",
"tableTo": "user",
"columnsTo": [
"id"
],
"onUpdate": "NO ACTION",
"onDelete": "CASCADE",
"name": "session_user_id_user_id_fk",
"entityType": "fks",
"schema": "public",
"table": "session"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "account_pkey",
"schema": "public",
"table": "account",
"entityType": "pks"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "comment_pkey",
"schema": "public",
"table": "comment",
"entityType": "pks"
},
{
"columns": [
"slug"
],
"nameExplicit": false,
"name": "page_pkey",
"schema": "public",
"table": "page",
"entityType": "pks"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "session_pkey",
"schema": "public",
"table": "session",
"entityType": "pks"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "user_pkey",
"schema": "public",
"table": "user",
"entityType": "pks"
},
{
"columns": [
"id"
],
"nameExplicit": false,
"name": "verification_pkey",
"schema": "public",
"table": "verification",
"entityType": "pks"
},
{
"nameExplicit": false,
"columns": [
"token"
],
"nullsNotDistinct": false,
"name": "session_token_unique",
"schema": "public",
"table": "session",
"entityType": "uniques"
},
{
"nameExplicit": false,
"columns": [
"email"
],
"nullsNotDistinct": false,
"name": "user_email_unique",
"schema": "public",
"table": "user",
"entityType": "uniques"
}
],
"renames": []
}
-443
View File
@@ -1,443 +0,0 @@
{
"id": "d126927d-35cf-4b6f-ab97-3872b8db26a7",
"prevId": "00000000-0000-0000-0000-000000000000",
"version": "7",
"dialect": "postgresql",
"tables": {
"public.account": {
"name": "account",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"account_id": {
"name": "account_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"provider_id": {
"name": "provider_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"access_token": {
"name": "access_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"refresh_token": {
"name": "refresh_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"id_token": {
"name": "id_token",
"type": "text",
"primaryKey": false,
"notNull": false
},
"access_token_expires_at": {
"name": "access_token_expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"refresh_token_expires_at": {
"name": "refresh_token_expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"scope": {
"name": "scope",
"type": "text",
"primaryKey": false,
"notNull": false
},
"password": {
"name": "password",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"account_user_id_user_id_fk": {
"name": "account_user_id_user_id_fk",
"tableFrom": "account",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.comment": {
"name": "comment",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "uuid",
"primaryKey": true,
"notNull": true,
"default": "gen_random_uuid()"
},
"content": {
"name": "content",
"type": "text",
"primaryKey": false,
"notNull": true
},
"page_slug": {
"name": "page_slug",
"type": "text",
"primaryKey": false,
"notNull": true
},
"parent_id": {
"name": "parent_id",
"type": "uuid",
"primaryKey": false,
"notNull": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true,
"default": "now()"
}
},
"indexes": {},
"foreignKeys": {
"comment_page_slug_page_slug_fk": {
"name": "comment_page_slug_page_slug_fk",
"tableFrom": "comment",
"tableTo": "page",
"columnsFrom": [
"page_slug"
],
"columnsTo": [
"slug"
],
"onDelete": "no action",
"onUpdate": "no action"
},
"comment_parent_id_comment_id_fk": {
"name": "comment_parent_id_comment_id_fk",
"tableFrom": "comment",
"tableTo": "comment",
"columnsFrom": [
"parent_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
},
"comment_user_id_user_id_fk": {
"name": "comment_user_id_user_id_fk",
"tableFrom": "comment",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.page": {
"name": "page",
"schema": "",
"columns": {
"slug": {
"name": "slug",
"type": "text",
"primaryKey": true,
"notNull": true
},
"views": {
"name": "views",
"type": "integer",
"primaryKey": false,
"notNull": true,
"default": 1
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.session": {
"name": "session",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"token": {
"name": "token",
"type": "text",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"ip_address": {
"name": "ip_address",
"type": "text",
"primaryKey": false,
"notNull": false
},
"user_agent": {
"name": "user_agent",
"type": "text",
"primaryKey": false,
"notNull": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {
"session_user_id_user_id_fk": {
"name": "session_user_id_user_id_fk",
"tableFrom": "session",
"tableTo": "user",
"columnsFrom": [
"user_id"
],
"columnsTo": [
"id"
],
"onDelete": "cascade",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"session_token_unique": {
"name": "session_token_unique",
"nullsNotDistinct": false,
"columns": [
"token"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.user": {
"name": "user",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"name": {
"name": "name",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email": {
"name": "email",
"type": "text",
"primaryKey": false,
"notNull": true
},
"email_verified": {
"name": "email_verified",
"type": "boolean",
"primaryKey": false,
"notNull": true
},
"image": {
"name": "image",
"type": "text",
"primaryKey": false,
"notNull": false
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {
"user_email_unique": {
"name": "user_email_unique",
"nullsNotDistinct": false,
"columns": [
"email"
]
}
},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
},
"public.verification": {
"name": "verification",
"schema": "",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true
},
"identifier": {
"name": "identifier",
"type": "text",
"primaryKey": false,
"notNull": true
},
"value": {
"name": "value",
"type": "text",
"primaryKey": false,
"notNull": true
},
"expires_at": {
"name": "expires_at",
"type": "timestamp",
"primaryKey": false,
"notNull": true
},
"created_at": {
"name": "created_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
},
"updated_at": {
"name": "updated_at",
"type": "timestamp",
"primaryKey": false,
"notNull": false
}
},
"indexes": {},
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {},
"policies": {},
"checkConstraints": {},
"isRLSEnabled": false
}
},
"enums": {},
"schemas": {},
"sequences": {},
"roles": {},
"policies": {},
"views": {},
"_meta": {
"columns": {},
"schemas": {},
"tables": {}
}
}
-13
View File
@@ -1,13 +0,0 @@
{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1747229716675,
"tag": "0000_puzzling_sphinx",
"breakpoints": true
}
]
}
+3 -2
View File
@@ -1,5 +1,5 @@
import { type BetterAuthOptions, betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { drizzleAdapter } from "@better-auth/drizzle-adapter/relations-v2";
import { type BetterAuthOptions, betterAuth } from "better-auth/minimal";
import { nextCookies } from "better-auth/next-js";
import { db } from "@/lib/db";
@@ -11,6 +11,7 @@ export const auth = betterAuth({
provider: "pg",
schema,
}),
experimental: { joins: true },
plugins: [nextCookies()],
socialProviders: {
github: {
+5 -3
View File
@@ -2,7 +2,7 @@ import { attachDatabasePool } from "@vercel/functions";
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "@/lib/db/schema";
import { authRelations } from "./schema";
// Create explicit pool instance for better connection management
const pool = new Pool({
@@ -18,5 +18,7 @@ try {
// ignore
}
// Pass pool to Drizzle with schema
export const db = drizzle(pool, { schema });
export const db = drizzle({
client: pool,
relations: { ...authRelations },
});
+89 -40
View File
@@ -1,3 +1,4 @@
import { defineRelationsPart } from "drizzle-orm";
import {
type AnyPgColumn,
boolean,
@@ -6,57 +7,105 @@ import {
text,
timestamp,
uuid,
index,
} from "drizzle-orm/pg-core";
export const user = pgTable("user", {
id: text("id").primaryKey(),
name: text("name").notNull(),
email: text("email").notNull().unique(),
emailVerified: boolean("email_verified").notNull(),
emailVerified: boolean("email_verified").default(false).notNull(),
image: text("image"),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.defaultNow()
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
});
export const session = pgTable("session", {
id: text("id").primaryKey(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
});
export const session = pgTable(
"session",
{
id: text("id").primaryKey(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
},
(table) => [index("session_userId_idx").on(table.userId)],
);
export const account = pgTable("account", {
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: timestamp("access_token_expires_at"),
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
scope: text("scope"),
password: text("password"),
createdAt: timestamp("created_at").notNull(),
updatedAt: timestamp("updated_at").notNull(),
});
export const account = pgTable(
"account",
{
id: text("id").primaryKey(),
accountId: text("account_id").notNull(),
providerId: text("provider_id").notNull(),
userId: text("user_id")
.notNull()
.references(() => user.id, { onDelete: "cascade" }),
accessToken: text("access_token"),
refreshToken: text("refresh_token"),
idToken: text("id_token"),
accessTokenExpiresAt: timestamp("access_token_expires_at"),
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
scope: text("scope"),
password: text("password"),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
},
(table) => [index("account_userId_idx").on(table.userId)],
);
export const verification = pgTable("verification", {
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at"),
updatedAt: timestamp("updated_at"),
});
export const verification = pgTable(
"verification",
{
id: text("id").primaryKey(),
identifier: text("identifier").notNull(),
value: text("value").notNull(),
expiresAt: timestamp("expires_at").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.defaultNow()
.$onUpdate(() => /* @__PURE__ */ new Date())
.notNull(),
},
(table) => [index("verification_identifier_idx").on(table.identifier)],
);
export const authRelations = defineRelationsPart({ user, session, account, verification }, (r) => ({
user: {
sessions: r.many.session({
from: r.user.id,
to: r.session.userId,
}),
accounts: r.many.account({
from: r.user.id,
to: r.account.userId,
}),
},
session: {
user: r.one.user({
from: r.session.userId,
to: r.user.id,
}),
},
account: {
user: r.one.user({
from: r.account.userId,
to: r.user.id,
}),
},
}));
export const page = pgTable("page", {
slug: text("slug").primaryKey(),
+12 -11
View File
@@ -36,16 +36,17 @@ export const getPost = async (slug: string): Promise<Post | undefined> => {
return allPosts.find((post) => post.slug === slug);
};
export const getFrontMatter: {
/**
* Returns the front matter of ALL posts, sorted reverse chronologically.
*/
(): Promise<FrontMatter[]>;
/**
* Returns the front matter of a given slug, or undefined if the slug does not exist.
*/
(slug: string): Promise<FrontMatter | undefined>;
} = (async (slug?: string) => {
/**
* Returns the front matter of ALL posts, sorted reverse chronologically.
*/
export async function getFrontMatter(): Promise<FrontMatter[]>;
/**
* Returns the front matter of a given slug, or undefined if the slug does not exist.
*/
export async function getFrontMatter(slug: string): Promise<FrontMatter | undefined>;
export async function getFrontMatter(
slug?: string,
): Promise<FrontMatter[] | FrontMatter | undefined> {
"use cache";
const toFrontMatter = (post: Post): FrontMatter => ({
@@ -70,7 +71,7 @@ export const getFrontMatter: {
}
throw new Error("getFrontMatter() called with invalid argument.");
}) as typeof getFrontMatter;
}
/** Returns the sanitized HTML content of a post for RSS feeds. */
export const getContent = async (slug: string): Promise<string | undefined> => {
+3 -3
View File
@@ -51,7 +51,7 @@ export const getCommentCount = async (slug: string): Promise<number> => {
.from(schema.comment)
.where(eq(schema.comment.pageSlug, slug));
return Number(result[0]?.count ?? 0);
return result[0]?.count ?? 0;
} catch (error) {
console.error("[server/comments] error fetching comment count:", error);
return 0;
@@ -76,7 +76,7 @@ export const getCommentCountsForSlugs = async (
const map: Record<string, number> = Object.fromEntries(slugs.map((s) => [s, 0]));
for (const row of rows) {
map[row.pageSlug] = Number(row.count ?? 0);
map[row.pageSlug] = row.count ?? 0;
}
return map;
} catch (error) {
@@ -100,7 +100,7 @@ export const getAllCommentCounts = async (): Promise<Record<string, number>> =>
const map: Record<string, number> = {};
for (const row of rows) {
map[row.pageSlug] = Number(row.count ?? 0);
map[row.pageSlug] = row.count ?? 0;
}
return map;
} catch (error) {
+1 -1
View File
@@ -46,7 +46,7 @@ export const useMDXComponents = (components: MDXComponents): MDXComponents => {
height={imageHeight}
className={cn(
"mx-auto my-8 block h-auto max-w-full rounded-sm",
"[&+em]:text-muted-foreground [&+em]:-mt-4 [&+em]:block [&+em]:text-center [&+em]:text-[0.875em] [&+em]:leading-normal [&+em]:font-medium [&+em]:not-italic",
"[&+em]:-mt-4 [&+em]:block [&+em]:text-center [&+em]:text-[0.875em] [&+em]:leading-normal [&+em]:font-medium [&+em]:text-muted-foreground [&+em]:not-italic",
className,
)}
{...rest}
+26 -24
View File
@@ -9,6 +9,7 @@
"url": "https://github.com/jakejarvis"
},
"repository": "jakejarvis/jarv.is",
"type": "module",
"scripts": {
"dev": "next dev -H 0.0.0.0",
"build": "next build",
@@ -23,33 +24,33 @@
},
"dependencies": {
"@base-ui/react": "^1.6.0",
"@fontsource/inter": "^5.2.8",
"@better-auth/drizzle-adapter": "^1.7.0-rc.1",
"@fontsource/inter": "^5.3.0",
"@mdx-js/loader": "^3.1.1",
"@mdx-js/react": "^3.1.1",
"@next/mdx": "16.2.9",
"@number-flow/react": "^0.6.1",
"@next/mdx": "16.2.11",
"@number-flow/react": "^0.6.2",
"@octokit/graphql": "^9.0.3",
"@octokit/graphql-schema": "^15.26.1",
"@tabler/icons-react": "^3.44.0",
"@tabler/icons-react": "^3.45.0",
"@vercel/analytics": "^2.0.1",
"@vercel/functions": "^3.7.4",
"@vercel/functions": "^3.7.5",
"@vercel/speed-insights": "^2.0.0",
"better-auth": "^1.6.22",
"better-auth": "^1.7.0-rc.1",
"cheerio": "^1.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"copy-to-clipboard": "^4.0.2",
"drizzle-orm": "^0.45.2",
"drizzle-orm": "^1.0.0-rc.4",
"fast-glob": "^3.3.3",
"feed": "^5.2.1",
"feed": "^6.0.0",
"html-entities": "^2.6.0",
"next": "16.2.9",
"next": "16.2.11",
"next-themes": "^0.4.6",
"pg": "^8.22.0",
"react": "19.2.7",
"react-activity-calendar": "^3.2.0",
"react": "19.2.8",
"react-activity-calendar": "^3.2.1",
"react-compare-slider": "^4.0.0",
"react-dom": "19.2.7",
"react-dom": "19.2.8",
"react-lite-youtube-embed": "~3.6.0",
"react-markdown": "^10.1.0",
"react-schemaorg": "^2.0.1",
@@ -72,8 +73,8 @@
"remark-smartypants": "^3.0.2",
"remark-strip-mdx-imports-exports": "^1.0.1",
"server-only": "0.0.1",
"shadcn": "^4.12.0",
"shiki": "^4.3.0",
"shadcn": "^4.13.1",
"shiki": "^4.3.1",
"sonner": "^2.0.7",
"tailwind-merge": "^3.6.0",
"unified": "^11.0.5",
@@ -83,28 +84,29 @@
"@content-collections/cli": "^0.1.9",
"@content-collections/core": "^0.15.2",
"@content-collections/next": "^0.2.11",
"@tailwindcss/postcss": "^4.3.2",
"@tailwindcss/postcss": "^4.3.3",
"@tailwindcss/typography": "^0.5.20",
"@types/mdx": "^2.0.14",
"@types/node": "^26.0.1",
"@types/node": "^26.1.1",
"@types/pg": "^8.20.0",
"@types/react": "19.2.17",
"@types/react-dom": "19.2.3",
"babel-plugin-react-compiler": "1.0.0",
"dotenv": "^17.4.2",
"drizzle-kit": "^0.31.10",
"oxfmt": "^0.56.0",
"oxlint": "^1.71.0",
"postcss": "^8.5.16",
"drizzle-kit": "^1.0.0-rc.4",
"oxfmt": "^0.59.0",
"oxlint": "^1.74.0",
"oxlint-tsgolint": "^7.0.2001",
"postcss": "^8.5.21",
"schema-dts": "^2.0.0",
"tailwindcss": "^4.3.2",
"tailwindcss": "^4.3.3",
"tw-animate-css": "^1.4.0",
"typescript": "6.0.3"
"typescript": "^6.0.3"
},
"engines": {
"node": ">=24.x"
},
"packageManager": "pnpm@11.9.0",
"packageManager": "pnpm@11.16.0",
"cacheDirectories": [
"node_modules",
".next/cache"
+868 -1286
View File
File diff suppressed because it is too large Load Diff
+4 -2
View File
@@ -3,5 +3,7 @@ allowBuilds:
msw: true
sharp: true
unrs-resolver: true
overrides:
kysely: ^0.28.17
peerDependencyRules:
allowAny:
- "drizzle-orm"