mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-07-28 05:05:21 -04:00
chore: drizzle v1, other dep updates and cleanup
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
"order": "asc"
|
||||
},
|
||||
"sortTailwindcss": {
|
||||
"stylesheet": "app/globals.css",
|
||||
"attributes": ["className"],
|
||||
"functions": ["cn", "clsx", "cva"]
|
||||
},
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
"env": {
|
||||
"builtin": true
|
||||
},
|
||||
"options": {
|
||||
"typeAware": true,
|
||||
"typeCheck": true
|
||||
},
|
||||
"categories": {
|
||||
"correctness": "error",
|
||||
"suspicious": "warn",
|
||||
|
||||
+6
-14
@@ -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’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’m a developer based in the{" "}
|
||||
<Link
|
||||
href="https://www.youtube-nocookie.com/embed/rLwbzGyC6t4?hl=en&fs=1&showinfo=1&rel=0&iv_load_policy=3"
|
||||
title='"Boston Accent Trailer - Late Night with Seth Meyers" on YouTube'
|
||||
>
|
||||
Boston
|
||||
</Link>{" "}
|
||||
area working on some cool stuff:
|
||||
I’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
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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 [];
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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 </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"> 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>
|
||||
|
||||
@@ -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
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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`}
|
||||
|
||||
Vendored
+2
-2
@@ -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"
|
||||
|
||||
Vendored
+3
-3
@@ -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 };
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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,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",
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1747229716675,
|
||||
"tag": "0000_puzzling_sphinx",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-2
@@ -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
@@ -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
@@ -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
@@ -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> => {
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
Generated
+868
-1286
File diff suppressed because it is too large
Load Diff
+4
-2
@@ -3,5 +3,7 @@ allowBuilds:
|
||||
msw: true
|
||||
sharp: true
|
||||
unrs-resolver: true
|
||||
overrides:
|
||||
kysely: ^0.28.17
|
||||
|
||||
peerDependencyRules:
|
||||
allowAny:
|
||||
- "drizzle-orm"
|
||||
|
||||
Reference in New Issue
Block a user