├── app
├── favicon.ico
├── f
│ └── [name]
│ │ ├── [id]
│ │ ├── loading.tsx
│ │ └── page.tsx
│ │ ├── page.tsx
│ │ └── new
│ │ └── page.tsx
├── components
│ ├── welcome-toast.tsx
│ ├── search.tsx
│ ├── left-sidebar.tsx
│ ├── menu.tsx
│ ├── right-sidebar.tsx
│ ├── thread-actions.tsx
│ └── thread-list.tsx
├── layout.tsx
├── search
│ └── page.tsx
└── globals.css
├── postcss.config.mjs
├── public
├── x.svg
├── linkedin.svg
├── github.svg
└── placeholder.svg
├── .prettierrc
├── drizzle.config.ts
├── lib
├── db
│ ├── migrations
│ │ ├── meta
│ │ │ ├── _journal.json
│ │ │ └── 0000_snapshot.json
│ │ └── 0000_warm_warbird.sql
│ ├── drizzle.ts
│ ├── migrate.ts
│ ├── setup.ts
│ ├── schema.ts
│ ├── actions.ts
│ ├── seed.ts
│ └── queries.ts
└── utils.tsx
├── next.config.ts
├── components.json
├── .gitignore
├── tsconfig.json
├── package.json
├── components
└── ui
│ ├── alert.tsx
│ ├── tooltip.tsx
│ ├── button.tsx
│ └── sheet.tsx
├── README.md
└── pnpm-lock.yaml
/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/leerob/next-email-client/HEAD/app/favicon.ico
--------------------------------------------------------------------------------
/postcss.config.mjs:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | '@tailwindcss/postcss': {},
4 | },
5 | };
6 |
--------------------------------------------------------------------------------
/public/x.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": [
3 | "prettier-plugin-organize-imports",
4 | "prettier-plugin-tailwindcss"
5 | ],
6 | "semi": true,
7 | "singleQuote": true,
8 | "tailwindStylesheet": "./app/globals.css",
9 | "tailwindFunctions": ["cn", "clsx", "cva"]
10 | }
11 |
--------------------------------------------------------------------------------
/drizzle.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from 'drizzle-kit';
2 |
3 | export default {
4 | schema: './lib/db/schema.ts',
5 | out: './lib/db/migrations',
6 | dialect: 'postgresql',
7 | dbCredentials: {
8 | url: process.env.POSTGRES_URL!,
9 | },
10 | } satisfies Config;
11 |
--------------------------------------------------------------------------------
/lib/db/migrations/meta/_journal.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "7",
3 | "dialect": "postgresql",
4 | "entries": [
5 | {
6 | "idx": 0,
7 | "version": "7",
8 | "when": 1727742572262,
9 | "tag": "0000_warm_warbird",
10 | "breakpoints": true
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/app/f/[name]/[id]/loading.tsx:
--------------------------------------------------------------------------------
1 | import { LeftSidebar } from '@/app/components/left-sidebar';
2 |
3 | export default function LoadingThreadSkeleton() {
4 | return (
5 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/next.config.ts:
--------------------------------------------------------------------------------
1 | import type { NextConfig } from 'next';
2 |
3 | const nextConfig: NextConfig = {
4 | experimental: {
5 | ppr: true,
6 | dynamicIO: true,
7 | serverSourceMaps: true,
8 | },
9 | async redirects() {
10 | return [
11 | {
12 | source: '/',
13 | destination: '/f/inbox',
14 | permanent: false,
15 | },
16 | ];
17 | },
18 | };
19 |
20 | export default nextConfig;
21 |
--------------------------------------------------------------------------------
/lib/db/drizzle.ts:
--------------------------------------------------------------------------------
1 | import dotenv from 'dotenv';
2 | import { drizzle } from 'drizzle-orm/postgres-js';
3 | import postgres from 'postgres';
4 | import * as schema from './schema';
5 |
6 | dotenv.config();
7 |
8 | if (!process.env.POSTGRES_URL) {
9 | throw new Error('POSTGRES_URL environment variable is not set');
10 | }
11 |
12 | export const client = postgres(process.env.POSTGRES_URL);
13 | export const db = drizzle(client, { schema });
14 |
--------------------------------------------------------------------------------
/lib/db/migrate.ts:
--------------------------------------------------------------------------------
1 | import dotenv from 'dotenv';
2 | import { migrate } from 'drizzle-orm/postgres-js/migrator';
3 | import path from 'path';
4 | import { client, db } from './drizzle';
5 |
6 | dotenv.config();
7 |
8 | async function main() {
9 | await migrate(db, {
10 | migrationsFolder: path.join(process.cwd(), '/lib/db/migrations'),
11 | });
12 | console.log(`Migrations complete`);
13 | await client.end();
14 | }
15 |
16 | main();
17 |
--------------------------------------------------------------------------------
/public/linkedin.svg:
--------------------------------------------------------------------------------
1 |
17 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "slate",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils",
16 | "ui": "@/components/ui",
17 | "lib": "@/lib",
18 | "hooks": "@/hooks"
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # env files
29 | .env*
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 | .vscode
38 | .idea
39 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "lib": ["dom", "dom.iterable", "esnext"],
5 | "allowJs": true,
6 | "skipLibCheck": true,
7 | "strict": true,
8 | "noEmit": true,
9 | "esModuleInterop": true,
10 | "module": "esnext",
11 | "moduleResolution": "bundler",
12 | "resolveJsonModule": true,
13 | "isolatedModules": true,
14 | "jsx": "preserve",
15 | "incremental": true,
16 | "baseUrl": ".",
17 | "plugins": [
18 | {
19 | "name": "next"
20 | }
21 | ],
22 | "paths": {
23 | "@/*": ["./*"]
24 | }
25 | },
26 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27 | "exclude": ["node_modules"]
28 | }
29 |
--------------------------------------------------------------------------------
/public/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/components/welcome-toast.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { useEffect } from 'react';
4 | import { toast } from 'sonner';
5 |
6 | export function WelcomeToast() {
7 | useEffect(() => {
8 | if (!document.cookie.includes('email-toast=1')) {
9 | toast('📩 Welcome to Next.js Emails!', {
10 | duration: Infinity,
11 | onDismiss: () =>
12 | (document.cookie = 'email-toast=1; max-age=31536000; path=/'),
13 | description: (
14 |
15 | This is a demo of an email client UI with a Postgres database.{' '}
16 |
21 | Deploy your own
22 |
23 | .
24 |
25 | ),
26 | });
27 | }
28 | }, []);
29 |
30 | return null;
31 | }
32 |
--------------------------------------------------------------------------------
/app/components/search.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import Form from 'next/form';
4 | import { useSearchParams } from 'next/navigation';
5 | import { useEffect, useRef } from 'react';
6 |
7 | export function Search() {
8 | let inputRef = useRef(null);
9 | let searchParams = useSearchParams();
10 |
11 | useEffect(() => {
12 | if (inputRef.current) {
13 | inputRef.current.focus();
14 | inputRef.current.setSelectionRange(
15 | inputRef.current.value.length,
16 | inputRef.current.value.length,
17 | );
18 | }
19 | }, []);
20 |
21 | return (
22 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from 'next';
2 | import { Inter } from 'next/font/google';
3 | import { Suspense } from 'react';
4 | import { Toaster } from 'sonner';
5 | import { RightSidebar } from './components/right-sidebar';
6 | import { WelcomeToast } from './components/welcome-toast';
7 | import './globals.css';
8 |
9 | const inter = Inter({ subsets: ['latin'] });
10 |
11 | export const metadata: Metadata = {
12 | title: 'Next.js Mail',
13 | description: 'An email client template using the Next.js App Router.',
14 | };
15 |
16 | export default function RootLayout({
17 | children,
18 | }: {
19 | children: React.ReactNode;
20 | }) {
21 | return (
22 |
23 |
24 | {children}
25 | }>
26 |
27 |
28 |
29 |
30 |
31 |
32 | );
33 | }
34 |
35 | function RightSidebarSkeleton() {
36 | return (
37 |
38 | );
39 | }
40 |
--------------------------------------------------------------------------------
/lib/utils.tsx:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
8 | export function formatEmailString(
9 | userEmail: {
10 | firstName: string | null;
11 | lastName: string | null;
12 | email: string;
13 | },
14 | opts: { includeFullEmail: boolean } = { includeFullEmail: false },
15 | ) {
16 | if (userEmail.firstName && userEmail.lastName) {
17 | return `${userEmail.firstName} ${userEmail.lastName} ${
18 | opts.includeFullEmail ? `<${userEmail.email}>` : ''
19 | }`;
20 | }
21 | return userEmail.email;
22 | }
23 |
24 | export function toTitleCase(str: string) {
25 | return str.replace(/\w\S*/g, function (txt: string) {
26 | return txt.charAt(0).toUpperCase() + txt.substring(1).toLowerCase();
27 | });
28 | }
29 |
30 | export function highlightText(text: string, query: string | undefined) {
31 | if (!query) return text;
32 | const parts = text.split(new RegExp(`(${query})`, 'gi'));
33 |
34 | return parts.map((part, i) =>
35 | part.toLowerCase() === query.toLowerCase() ? (
36 |
37 | {part}
38 |
39 | ) : (
40 | part
41 | ),
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "dev": "next dev --turbo",
5 | "build": "next build",
6 | "start": "next start",
7 | "db:setup": "npx tsx lib/db/setup.ts",
8 | "db:seed": "npx tsx lib/db/seed.ts",
9 | "db:generate": "drizzle-kit generate",
10 | "db:migrate": "npx tsx lib/db/migrate.ts",
11 | "db:studio": "drizzle-kit studio"
12 | },
13 | "dependencies": {
14 | "@radix-ui/react-dialog": "^1.1.6",
15 | "@radix-ui/react-icons": "^1.3.2",
16 | "@radix-ui/react-slot": "^1.1.2",
17 | "@radix-ui/react-tooltip": "^1.1.8",
18 | "@tailwindcss/postcss": "^4.0.13",
19 | "@types/node": "^22.10.1",
20 | "@types/react": "19.0.10",
21 | "@types/react-dom": "19.0.4",
22 | "class-variance-authority": "^0.7.1",
23 | "clsx": "^2.1.1",
24 | "dotenv": "^16.4.5",
25 | "drizzle-kit": "^0.28.1",
26 | "drizzle-orm": "^0.36.4",
27 | "geist": "^1.3.1",
28 | "lucide-react": "^0.462.0",
29 | "next": "15.6.0-canary.59",
30 | "postcss": "^8.4.49",
31 | "postgres": "^3.4.5",
32 | "react": "19.0.0",
33 | "react-dom": "19.0.0",
34 | "sonner": "^1.7.0",
35 | "tailwind-merge": "^3.0.2",
36 | "tailwindcss": "^4.0.13",
37 | "tailwindcss-animate": "^1.0.7",
38 | "typescript": "^5.8.2",
39 | "zod": "^3.23.8"
40 | },
41 | "devDependencies": {
42 | "prettier-plugin-organize-imports": "^4.1.0",
43 | "prettier-plugin-tailwindcss": "^0.6.11"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/app/components/left-sidebar.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { Button } from '@/components/ui/button';
4 | import { ArrowLeft, ChevronDown, ChevronUp } from 'lucide-react';
5 | import Link from 'next/link';
6 | import { useParams } from 'next/navigation';
7 | import { Suspense } from 'react';
8 |
9 | function BackButton() {
10 | let { name } = useParams();
11 |
12 | return (
13 |
14 |
21 |
22 | );
23 | }
24 |
25 | export function LeftSidebar() {
26 | return (
27 |
28 |
29 |
30 |
31 |
38 |
45 |
46 | );
47 | }
48 |
--------------------------------------------------------------------------------
/app/f/[name]/page.tsx:
--------------------------------------------------------------------------------
1 | import { ThreadHeader, ThreadList } from '@/app/components/thread-list';
2 | import { getThreadsForFolder } from '@/lib/db/queries';
3 | import { Suspense } from 'react';
4 |
5 | export function generateStaticParams() {
6 | const folderNames = [
7 | 'inbox',
8 | 'starred',
9 | 'drafts',
10 | 'sent',
11 | 'archive',
12 | 'trash',
13 | ];
14 |
15 | return folderNames.map((name) => ({ name }));
16 | }
17 |
18 | export default function ThreadsPage({
19 | params,
20 | searchParams,
21 | }: {
22 | params: Promise<{ name: string }>;
23 | searchParams: Promise<{ q?: string; id?: string }>;
24 | }) {
25 | return (
26 |
27 | }>
28 |
29 |
30 |
31 | );
32 | }
33 |
34 | function ThreadsSkeleton({ folderName }: { folderName: string }) {
35 | return (
36 |
37 |
38 |
39 | );
40 | }
41 |
42 | async function Threads({
43 | params,
44 | searchParams,
45 | }: {
46 | params: Promise<{ name: string }>;
47 | searchParams: Promise<{ q?: string; id?: string }>;
48 | }) {
49 | let { name } = await params;
50 | let { q } = await searchParams;
51 | let threads = await getThreadsForFolder(name);
52 |
53 | return ;
54 | }
55 |
--------------------------------------------------------------------------------
/components/ui/alert.tsx:
--------------------------------------------------------------------------------
1 | import { cva, type VariantProps } from 'class-variance-authority';
2 | import * as React from 'react';
3 |
4 | import { cn } from '@/lib/utils';
5 |
6 | const alertVariants = cva(
7 | 'relative grid w-full grid-cols-[0_1fr] items-start gap-y-0.5 rounded-lg border px-4 py-3 text-sm has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] has-[>svg]:gap-x-3 [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current',
8 | {
9 | variants: {
10 | variant: {
11 | default: 'bg-background text-foreground',
12 | destructive:
13 | 'text-destructive-foreground *:data-[slot=alert-description]:text-destructive-foreground/80 [&>svg]:text-current',
14 | },
15 | },
16 | defaultVariants: {
17 | variant: 'default',
18 | },
19 | },
20 | );
21 |
22 | function Alert({
23 | className,
24 | variant,
25 | ...props
26 | }: React.ComponentProps<'div'> & VariantProps) {
27 | return (
28 |
34 | );
35 | }
36 |
37 | function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
38 | return (
39 |
47 | );
48 | }
49 |
50 | function AlertDescription({
51 | className,
52 | ...props
53 | }: React.ComponentProps<'div'>) {
54 | return (
55 |
63 | );
64 | }
65 |
66 | export { Alert, AlertDescription, AlertTitle };
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Next.js Email Client
2 |
3 | This is an email client template built with Next.js and Postgres. It's built to show off some of the features of the App Router, which enable you to build products that:
4 |
5 | - Navigate between routes in a column layout while maintaining scroll position (layouts support)
6 | - Submit forms without JavaScript enabled (progressive enhancement)
7 | - Navigate between routes extremely fast (prefetching and caching)
8 | - Retain your UI position on reload (URL state)
9 |
10 | **Demo: https://next-email-client.vercel.app**
11 |
12 | ## Tech Stack
13 |
14 | - **Framework**: [Next.js](https://nextjs.org/)
15 | - **Database**: [Postgres](https://www.postgresql.org/)
16 | - **ORM**: [Drizzle](https://orm.drizzle.team/)
17 | - **Styling**: [Tailwind CSS](https://tailwindcss.com/)
18 | - **UI Library**: [shadcn/ui](https://ui.shadcn.com/)
19 |
20 | ## Getting Started
21 |
22 | ```bash
23 | git clone https://github.com/leerob/next-email-client
24 | cd next-email-client
25 | pnpm install
26 | ```
27 |
28 | ## Running Locally
29 |
30 | Use the included setup script to create your `.env` file:
31 |
32 | ```bash
33 | pnpm db:setup
34 | ```
35 |
36 | Then, run the database migrations and seed the database with emails and folders:
37 |
38 | ```bash
39 | pnpm db:migrate
40 | pnpm db:seed
41 | ```
42 |
43 | Finally, run the Next.js development server:
44 |
45 | ```bash
46 | pnpm dev
47 | ```
48 |
49 | Open [http://localhost:3000](http://localhost:3000) in your browser to see the app in action.
50 |
51 | ## Implemented
52 |
53 | - ✅ Search for emails
54 | - ✅ Profile sidebar with user information
55 | - ✅ View all threads
56 | - ✅ View all emails in a thread
57 | - ✅ Compose view
58 | - ✅ Seed and setup script
59 | - ✅ Highlight searched text
60 | - ✅ Hook up compose view
61 | - ✅ Delete emails (move to trash)
62 | - Make side profile dynamic
63 | - Support Markdown?
64 | - Make up/down arrows work for threads
65 | - Global keyboard shortcuts
66 | - Better date formatting
67 | - Dark mode styles
68 |
--------------------------------------------------------------------------------
/components/ui/tooltip.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as TooltipPrimitive from '@radix-ui/react-tooltip';
4 | import * as React from 'react';
5 |
6 | import { cn } from '@/lib/utils';
7 |
8 | function TooltipProvider({
9 | delayDuration = 0,
10 | ...props
11 | }: React.ComponentProps) {
12 | return (
13 |
18 | );
19 | }
20 |
21 | function Tooltip({
22 | ...props
23 | }: React.ComponentProps) {
24 | return (
25 |
26 |
27 |
28 | );
29 | }
30 |
31 | function TooltipTrigger({
32 | ...props
33 | }: React.ComponentProps) {
34 | return ;
35 | }
36 |
37 | function TooltipContent({
38 | className,
39 | sideOffset = 0,
40 | children,
41 | ...props
42 | }: React.ComponentProps) {
43 | return (
44 |
45 |
54 | {children}
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
62 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import { Slot } from '@radix-ui/react-slot';
2 | import { cva, type VariantProps } from 'class-variance-authority';
3 | import * as React from 'react';
4 |
5 | import { cn } from '@/lib/utils';
6 |
7 | const buttonVariants = cva(
8 | "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
14 | destructive:
15 | 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40',
16 | outline:
17 | 'border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground',
18 | secondary:
19 | 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
20 | ghost: 'hover:bg-accent hover:text-accent-foreground',
21 | link: 'text-primary underline-offset-4 hover:underline',
22 | },
23 | size: {
24 | default: 'h-9 px-4 py-2 has-[>svg]:px-3',
25 | sm: 'h-8 gap-1.5 rounded-md px-3 text-xs has-[>svg]:px-2.5',
26 | lg: 'h-8 rounded-md px-2 has-[>svg]:px-2 sm:h-10',
27 | icon: 'size-9',
28 | },
29 | },
30 | defaultVariants: {
31 | variant: 'default',
32 | size: 'default',
33 | },
34 | },
35 | );
36 |
37 | function Button({
38 | className,
39 | variant,
40 | size,
41 | asChild = false,
42 | ...props
43 | }: React.ComponentProps<'button'> &
44 | VariantProps & {
45 | asChild?: boolean;
46 | }) {
47 | const Comp = asChild ? Slot : 'button';
48 |
49 | return (
50 |
55 | );
56 | }
57 |
58 | export { Button, buttonVariants };
59 |
--------------------------------------------------------------------------------
/app/f/[name]/[id]/page.tsx:
--------------------------------------------------------------------------------
1 | import { LeftSidebar } from '@/app/components/left-sidebar';
2 | import { ThreadActions } from '@/app/components/thread-actions';
3 | import { getEmailsForThread } from '@/lib/db/queries';
4 | import { notFound } from 'next/navigation';
5 |
6 | export default async function EmailPage({
7 | params,
8 | }: {
9 | params: Promise<{ name: string; id: string }>;
10 | }) {
11 | let id = (await params).id;
12 | let thread = await getEmailsForThread(id);
13 |
14 | if (!thread || thread.emails.length === 0) {
15 | notFound();
16 | }
17 |
18 | return (
19 |
20 |
21 |
22 |
23 |
24 |
25 | {thread.subject}
26 |
27 |
28 |
31 |
32 |
33 |
34 |
35 | {thread.emails.map((email) => (
36 |
37 |
38 |
39 | {email.sender.firstName} {email.sender.lastName} to{' '}
40 | {email.recipientId === thread.emails[0].sender.id
41 | ? 'Me'
42 | : 'All'}
43 |
44 |
45 | {new Date(email.sentDate!).toLocaleString()}
46 |
47 |
48 |
{email.body}
49 |
50 | ))}
51 |
52 |
53 |
54 |
55 | );
56 | }
57 |
--------------------------------------------------------------------------------
/app/components/menu.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Sheet,
3 | SheetContent,
4 | SheetTitle,
5 | SheetTrigger,
6 | } from '@/components/ui/sheet';
7 | import { Check, FileText, Menu, Send, Star, Trash } from 'lucide-react';
8 | import Link from 'next/link';
9 |
10 | export function NavMenu() {
11 | return (
12 |
13 |
14 |
17 |
18 |
22 | Menu
23 |
67 |
68 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/app/components/right-sidebar.tsx:
--------------------------------------------------------------------------------
1 | import { getUserProfile } from '@/lib/db/queries';
2 | import Image from 'next/image';
3 |
4 | export async function RightSidebar({ userId }: { userId: number }) {
5 | let user = await getUserProfile(userId);
6 |
7 | if (!user) {
8 | return null;
9 | }
10 |
11 | return (
12 |
13 |
14 |
{`${user.firstName} ${user.lastName}`}
15 |
16 |

21 |
22 |
{user.email}
23 |
{user.location}
24 |
25 |
26 |
{`${user.jobTitle} at ${user.company}`}
27 |
28 |
Mail
29 |
30 | {user.latestThreads.map((thread, index) => (
31 | - {thread.subject}
32 | ))}
33 |
34 |
35 |
88 |
89 |
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/public/placeholder.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/app/components/thread-actions.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import {
4 | Tooltip,
5 | TooltipContent,
6 | TooltipProvider,
7 | TooltipTrigger,
8 | } from '@/components/ui/tooltip';
9 | import { moveThreadToDone, moveThreadToTrash } from '@/lib/db/actions';
10 | import { Archive, Check, Clock } from 'lucide-react';
11 | import { useActionState } from 'react';
12 |
13 | interface ThreadActionsProps {
14 | threadId: number;
15 | }
16 |
17 | export function ThreadActions({ threadId }: ThreadActionsProps) {
18 | const initialState = {
19 | error: null,
20 | success: false,
21 | };
22 |
23 | const [doneState, doneAction, donePending] = useActionState(
24 | moveThreadToDone,
25 | initialState,
26 | );
27 | const [trashState, trashAction, trashPending] = useActionState(
28 | moveThreadToTrash,
29 | initialState,
30 | );
31 |
32 | const isProduction = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production';
33 |
34 | return (
35 |
36 |
37 |
38 |
39 |
49 |
50 | {isProduction && (
51 |
52 | Marking as done is disabled in production
53 |
54 | )}
55 |
56 |
57 |
58 |
64 |
65 |
66 | This feature is not yet implemented
67 |
68 |
69 |
70 |
71 |
81 |
82 | {isProduction && (
83 |
84 | Moving to trash is disabled in production
85 |
86 | )}
87 |
88 |
89 |
90 | );
91 | }
92 |
--------------------------------------------------------------------------------
/app/search/page.tsx:
--------------------------------------------------------------------------------
1 | import { searchThreads } from '@/lib/db/queries';
2 | import { formatEmailString, highlightText } from '@/lib/utils';
3 | import { X } from 'lucide-react';
4 | import Link from 'next/link';
5 | import { Suspense } from 'react';
6 | import { NavMenu } from '../components/menu';
7 | import { Search } from '../components/search';
8 |
9 | async function Threads({
10 | searchParams,
11 | }: {
12 | searchParams: Promise<{ q?: string; id?: string }>;
13 | }) {
14 | let q = (await searchParams).q;
15 | let threads = await searchThreads(q);
16 |
17 | return (
18 |
19 | {threads.map((thread) => {
20 | const latestEmail = thread.latestEmail;
21 | return (
22 |
26 |
29 |
30 |
31 |
32 | {highlightText(formatEmailString(latestEmail.sender), q)}
33 |
34 |
35 |
36 |
37 | {highlightText(thread.subject, q)}
38 |
39 |
40 | {highlightText(latestEmail.body, q)}
41 |
42 |
43 |
44 |
45 |
46 | {new Date(thread.lastActivityDate).toLocaleDateString()}
47 |
48 |
49 |
50 |
51 | );
52 | })}
53 |
54 | );
55 | }
56 |
57 | export default async function SearchPage({
58 | searchParams,
59 | }: {
60 | searchParams: Promise<{ q?: string; id?: string }>;
61 | }) {
62 | return (
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | );
89 | }
90 |
--------------------------------------------------------------------------------
/lib/db/setup.ts:
--------------------------------------------------------------------------------
1 | import { exec } from 'node:child_process';
2 | import { promises as fs } from 'node:fs';
3 | import path from 'node:path';
4 | import readline from 'node:readline';
5 | import { promisify } from 'node:util';
6 |
7 | const execAsync = promisify(exec);
8 |
9 | function question(query: string): Promise {
10 | const rl = readline.createInterface({
11 | input: process.stdin,
12 | output: process.stdout,
13 | });
14 |
15 | return new Promise((resolve) =>
16 | rl.question(query, (ans) => {
17 | rl.close();
18 | resolve(ans);
19 | }),
20 | );
21 | }
22 |
23 | async function getPostgresURL(): Promise {
24 | console.log('Step 1: Setting up Postgres');
25 | const dbChoice = await question(
26 | 'Do you want to use a local Postgres instance with Docker (L) or a remote Postgres instance (R)? (L/R): ',
27 | );
28 |
29 | if (dbChoice.toLowerCase() === 'l') {
30 | console.log('Setting up local Postgres instance with Docker...');
31 | await setupLocalPostgres();
32 | return 'postgres://postgres:postgres@localhost:54322/postgres';
33 | } else {
34 | console.log(
35 | 'You can find Postgres databases at: https://vercel.com/marketplace?category=databases',
36 | );
37 | return await question('Enter your POSTGRES_URL: ');
38 | }
39 | }
40 |
41 | async function setupLocalPostgres() {
42 | console.log('Checking if Docker is installed...');
43 | try {
44 | await execAsync('docker --version');
45 | console.log('Docker is installed.');
46 | } catch (error) {
47 | console.error(
48 | 'Docker is not installed. Please install Docker and try again.',
49 | );
50 | console.log(
51 | 'To install Docker, visit: https://docs.docker.com/get-docker/',
52 | );
53 | process.exit(1);
54 | }
55 |
56 | console.log('Creating docker-compose.yml file...');
57 | const dockerComposeContent = `
58 | services:
59 | postgres:
60 | image: postgres:16.4-alpine
61 | container_name: music_player_postgres
62 | environment:
63 | POSTGRES_DB: postgres
64 | POSTGRES_USER: postgres
65 | POSTGRES_PASSWORD: postgres
66 | ports:
67 | - "54322:5432"
68 | volumes:
69 | - postgres_data:/var/lib/postgresql/data
70 |
71 | volumes:
72 | postgres_data:
73 | `;
74 |
75 | await fs.writeFile(
76 | path.join(process.cwd(), 'docker-compose.yml'),
77 | dockerComposeContent,
78 | );
79 | console.log('docker-compose.yml file created.');
80 |
81 | console.log('Starting Docker container with `docker compose up -d`...');
82 | try {
83 | await execAsync('docker compose up -d');
84 | console.log('Docker container started successfully.');
85 | } catch (error) {
86 | console.error(
87 | 'Failed to start Docker container. Please check your Docker installation and try again.',
88 | );
89 | process.exit(1);
90 | }
91 | }
92 |
93 | async function writeEnvFile(envVars: Record) {
94 | console.log('Step 3: Writing environment variables to .env');
95 | const envContent = Object.entries(envVars)
96 | .map(([key, value]) => `${key}=${value}`)
97 | .join('\n');
98 |
99 | await fs.writeFile(path.join(process.cwd(), '.env'), envContent);
100 | console.log('.env file created with the necessary variables.');
101 | }
102 |
103 | async function main() {
104 | const POSTGRES_URL = await getPostgresURL();
105 |
106 | await writeEnvFile({
107 | POSTGRES_URL,
108 | });
109 |
110 | console.log('🎉 Setup completed successfully!');
111 | }
112 |
113 | main().catch(console.error);
114 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @import 'tailwindcss';
2 |
3 | @plugin "tailwindcss-animate";
4 |
5 | @custom-variant dark (&:is(.dark *));
6 |
7 | @layer base {
8 | :root {
9 | --background: hsl(0 0% 100%);
10 | --foreground: hsl(222.2 84% 4.9%);
11 | --card: hsl(0 0% 100%);
12 | --card-foreground: hsl(222.2 84% 4.9%);
13 | --popover: hsl(0 0% 100%);
14 | --popover-foreground: hsl(222.2 84% 4.9%);
15 | --primary: hsl(222.2 47.4% 11.2%);
16 | --primary-foreground: hsl(210 40% 98%);
17 | --secondary: hsl(210 40% 96.1%);
18 | --secondary-foreground: hsl(222.2 47.4% 11.2%);
19 | --muted: hsl(210 40% 96.1%);
20 | --muted-foreground: hsl(215.4 16.3% 46.9%);
21 | --accent: hsl(210 40% 96.1%);
22 | --accent-foreground: hsl(222.2 47.4% 11.2%);
23 | --destructive: hsl(0 84.2% 60.2%);
24 | --destructive-foreground: hsl(210 40% 98%);
25 | --border: hsl(214.3 31.8% 91.4%);
26 | --input: hsl(214.3 31.8% 91.4%);
27 | --ring: hsl(222.2 84% 4.9%);
28 | --chart-1: hsl(12 76% 61%);
29 | --chart-2: hsl(173 58% 39%);
30 | --chart-3: hsl(197 37% 24%);
31 | --chart-4: hsl(43 74% 66%);
32 | --chart-5: hsl(27 87% 67%);
33 | --radius: 0.5rem;
34 | }
35 |
36 | .dark {
37 | --background: hsl(222.2 84% 4.9%);
38 | --foreground: hsl(210 40% 98%);
39 | --card: hsl(222.2 84% 4.9%);
40 | --card-foreground: hsl(210 40% 98%);
41 | --popover: hsl(222.2 84% 4.9%);
42 | --popover-foreground: hsl(210 40% 98%);
43 | --primary: hsl(210 40% 98%);
44 | --primary-foreground: hsl(222.2 47.4% 11.2%);
45 | --secondary: hsl(217.2 32.6% 17.5%);
46 | --secondary-foreground: hsl(210 40% 98%);
47 | --muted: hsl(217.2 32.6% 17.5%);
48 | --muted-foreground: hsl(215 20.2% 65.1%);
49 | --accent: hsl(217.2 32.6% 17.5%);
50 | --accent-foreground: hsl(210 40% 98%);
51 | --destructive: hsl(0 62.8% 30.6%);
52 | --destructive-foreground: hsl(210 40% 98%);
53 | --border: hsl(217.2 32.6% 17.5%);
54 | --input: hsl(217.2 32.6% 17.5%);
55 | --ring: hsl(212.7 26.8% 83.9%);
56 | --chart-1: hsl(220 70% 50%);
57 | --chart-2: hsl(160 60% 45%);
58 | --chart-3: hsl(30 80% 55%);
59 | --chart-4: hsl(280 65% 60%);
60 | --chart-5: hsl(340 75% 55%);
61 | }
62 | }
63 |
64 | @theme inline {
65 | --color-background: var(--background);
66 | --color-foreground: var(--foreground);
67 | --color-card: var(--card);
68 | --color-card-foreground: var(--card-foreground);
69 | --color-popover: var(--popover);
70 | --color-popover-foreground: var(--popover-foreground);
71 | --color-primary: var(--primary);
72 | --color-primary-foreground: var(--primary-foreground);
73 | --color-secondary: var(--secondary);
74 | --color-secondary-foreground: var(--secondary-foreground);
75 | --color-muted: var(--muted);
76 | --color-muted-foreground: var(--muted-foreground);
77 | --color-accent: var(--accent);
78 | --color-accent-foreground: var(--accent-foreground);
79 | --color-destructive: var(--destructive);
80 | --color-destructive-foreground: var(--destructive-foreground);
81 | --color-border: var(--border);
82 | --color-input: var(--input);
83 | --color-ring: var(--ring);
84 | --color-chart-1: var(--chart-1);
85 | --color-chart-2: var(--chart-2);
86 | --color-chart-3: var(--chart-3);
87 | --color-chart-4: var(--chart-4);
88 | --color-chart-5: var(--chart-5);
89 | --radius-sm: calc(var(--radius) - 4px);
90 | --radius-md: calc(var(--radius) - 2px);
91 | --radius-lg: var(--radius);
92 | --radius-xl: calc(var(--radius) + 4px);
93 | }
94 |
95 | @layer base {
96 | * {
97 | @apply border-border outline-ring/50;
98 | }
99 |
100 | body {
101 | @apply bg-background text-foreground;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/lib/db/migrations/0000_warm_warbird.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE IF NOT EXISTS "emails" (
2 | "id" serial PRIMARY KEY NOT NULL,
3 | "thread_id" integer,
4 | "sender_id" integer,
5 | "recipient_id" integer,
6 | "subject" varchar(255),
7 | "body" text,
8 | "sent_date" timestamp DEFAULT now()
9 | );
10 | --> statement-breakpoint
11 | CREATE TABLE IF NOT EXISTS "folders" (
12 | "id" serial PRIMARY KEY NOT NULL,
13 | "name" varchar(50) NOT NULL
14 | );
15 | --> statement-breakpoint
16 | CREATE TABLE IF NOT EXISTS "thread_folders" (
17 | "id" serial PRIMARY KEY NOT NULL,
18 | "thread_id" integer,
19 | "folder_id" integer
20 | );
21 | --> statement-breakpoint
22 | CREATE TABLE IF NOT EXISTS "threads" (
23 | "id" serial PRIMARY KEY NOT NULL,
24 | "subject" varchar(255),
25 | "last_activity_date" timestamp DEFAULT now()
26 | );
27 | --> statement-breakpoint
28 | CREATE TABLE IF NOT EXISTS "user_folders" (
29 | "id" serial PRIMARY KEY NOT NULL,
30 | "user_id" integer,
31 | "folder_id" integer
32 | );
33 | --> statement-breakpoint
34 | CREATE TABLE IF NOT EXISTS "users" (
35 | "id" serial PRIMARY KEY NOT NULL,
36 | "first_name" varchar(50),
37 | "last_name" varchar(50),
38 | "email" varchar(255) NOT NULL,
39 | "job_title" varchar(100),
40 | "company" varchar(100),
41 | "location" varchar(100),
42 | "twitter" varchar(100),
43 | "linkedin" varchar(100),
44 | "github" varchar(100),
45 | "avatar_url" varchar(255)
46 | );
47 | --> statement-breakpoint
48 | DO $$ BEGIN
49 | ALTER TABLE "emails" ADD CONSTRAINT "emails_thread_id_threads_id_fk" FOREIGN KEY ("thread_id") REFERENCES "public"."threads"("id") ON DELETE no action ON UPDATE no action;
50 | EXCEPTION
51 | WHEN duplicate_object THEN null;
52 | END $$;
53 | --> statement-breakpoint
54 | DO $$ BEGIN
55 | ALTER TABLE "emails" ADD CONSTRAINT "emails_sender_id_users_id_fk" FOREIGN KEY ("sender_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
56 | EXCEPTION
57 | WHEN duplicate_object THEN null;
58 | END $$;
59 | --> statement-breakpoint
60 | DO $$ BEGIN
61 | ALTER TABLE "emails" ADD CONSTRAINT "emails_recipient_id_users_id_fk" FOREIGN KEY ("recipient_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
62 | EXCEPTION
63 | WHEN duplicate_object THEN null;
64 | END $$;
65 | --> statement-breakpoint
66 | DO $$ BEGIN
67 | ALTER TABLE "thread_folders" ADD CONSTRAINT "thread_folders_thread_id_threads_id_fk" FOREIGN KEY ("thread_id") REFERENCES "public"."threads"("id") ON DELETE no action ON UPDATE no action;
68 | EXCEPTION
69 | WHEN duplicate_object THEN null;
70 | END $$;
71 | --> statement-breakpoint
72 | DO $$ BEGIN
73 | ALTER TABLE "thread_folders" ADD CONSTRAINT "thread_folders_folder_id_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "public"."folders"("id") ON DELETE no action ON UPDATE no action;
74 | EXCEPTION
75 | WHEN duplicate_object THEN null;
76 | END $$;
77 | --> statement-breakpoint
78 | DO $$ BEGIN
79 | ALTER TABLE "user_folders" ADD CONSTRAINT "user_folders_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE no action ON UPDATE no action;
80 | EXCEPTION
81 | WHEN duplicate_object THEN null;
82 | END $$;
83 | --> statement-breakpoint
84 | DO $$ BEGIN
85 | ALTER TABLE "user_folders" ADD CONSTRAINT "user_folders_folder_id_folders_id_fk" FOREIGN KEY ("folder_id") REFERENCES "public"."folders"("id") ON DELETE no action ON UPDATE no action;
86 | EXCEPTION
87 | WHEN duplicate_object THEN null;
88 | END $$;
89 | --> statement-breakpoint
90 | CREATE INDEX IF NOT EXISTS "thread_id_idx" ON "emails" USING btree ("thread_id");--> statement-breakpoint
91 | CREATE INDEX IF NOT EXISTS "sender_id_idx" ON "emails" USING btree ("sender_id");--> statement-breakpoint
92 | CREATE INDEX IF NOT EXISTS "recipient_id_idx" ON "emails" USING btree ("recipient_id");--> statement-breakpoint
93 | CREATE INDEX IF NOT EXISTS "sent_date_idx" ON "emails" USING btree ("sent_date");--> statement-breakpoint
94 | CREATE UNIQUE INDEX IF NOT EXISTS "email_idx" ON "users" USING btree ("email");
--------------------------------------------------------------------------------
/lib/db/schema.ts:
--------------------------------------------------------------------------------
1 | import { relations } from 'drizzle-orm';
2 | import {
3 | index,
4 | integer,
5 | pgTable,
6 | serial,
7 | text,
8 | timestamp,
9 | uniqueIndex,
10 | varchar,
11 | } from 'drizzle-orm/pg-core';
12 |
13 | export const users = pgTable(
14 | 'users',
15 | {
16 | id: serial('id').primaryKey(),
17 | firstName: varchar('first_name', { length: 50 }),
18 | lastName: varchar('last_name', { length: 50 }),
19 | email: varchar('email', { length: 255 }).notNull(),
20 | jobTitle: varchar('job_title', { length: 100 }),
21 | company: varchar('company', { length: 100 }),
22 | location: varchar('location', { length: 100 }),
23 | twitter: varchar('twitter', { length: 100 }),
24 | linkedin: varchar('linkedin', { length: 100 }),
25 | github: varchar('github', { length: 100 }),
26 | avatarUrl: varchar('avatar_url', { length: 255 }),
27 | },
28 | (table) => {
29 | return {
30 | emailIndex: uniqueIndex('email_idx').on(table.email),
31 | };
32 | },
33 | );
34 |
35 | export const threads = pgTable('threads', {
36 | id: serial('id').primaryKey(),
37 | subject: varchar('subject', { length: 255 }),
38 | lastActivityDate: timestamp('last_activity_date').defaultNow(),
39 | });
40 |
41 | export const emails = pgTable(
42 | 'emails',
43 | {
44 | id: serial('id').primaryKey(),
45 | threadId: integer('thread_id').references(() => threads.id),
46 | senderId: integer('sender_id').references(() => users.id),
47 | recipientId: integer('recipient_id').references(() => users.id),
48 | subject: varchar('subject', { length: 255 }),
49 | body: text('body'),
50 | sentDate: timestamp('sent_date').defaultNow(),
51 | },
52 | (table) => {
53 | return {
54 | threadIdIndex: index('thread_id_idx').on(table.threadId),
55 | senderIdIndex: index('sender_id_idx').on(table.senderId),
56 | recipientIdIndex: index('recipient_id_idx').on(table.recipientId),
57 | sentDateIndex: index('sent_date_idx').on(table.sentDate),
58 | };
59 | },
60 | );
61 |
62 | export const folders = pgTable('folders', {
63 | id: serial('id').primaryKey(),
64 | name: varchar('name', { length: 50 }).notNull(),
65 | });
66 |
67 | export const userFolders = pgTable('user_folders', {
68 | id: serial('id').primaryKey(),
69 | userId: integer('user_id').references(() => users.id),
70 | folderId: integer('folder_id').references(() => folders.id),
71 | });
72 |
73 | export const threadFolders = pgTable('thread_folders', {
74 | id: serial('id').primaryKey(),
75 | threadId: integer('thread_id').references(() => threads.id),
76 | folderId: integer('folder_id').references(() => folders.id),
77 | });
78 |
79 | export const usersRelations = relations(users, ({ many }) => ({
80 | sentEmails: many(emails, { relationName: 'sender' }),
81 | receivedEmails: many(emails, { relationName: 'recipient' }),
82 | userFolders: many(userFolders),
83 | }));
84 |
85 | export const threadsRelations = relations(threads, ({ many }) => ({
86 | emails: many(emails),
87 | threadFolders: many(threadFolders),
88 | }));
89 |
90 | export const emailsRelations = relations(emails, ({ one }) => ({
91 | thread: one(threads, {
92 | fields: [emails.threadId],
93 | references: [threads.id],
94 | }),
95 | sender: one(users, {
96 | fields: [emails.senderId],
97 | references: [users.id],
98 | relationName: 'sender',
99 | }),
100 | recipient: one(users, {
101 | fields: [emails.recipientId],
102 | references: [users.id],
103 | relationName: 'recipient',
104 | }),
105 | }));
106 |
107 | export const foldersRelations = relations(folders, ({ many }) => ({
108 | userFolders: many(userFolders),
109 | threadFolders: many(threadFolders),
110 | }));
111 |
112 | export const userFoldersRelations = relations(userFolders, ({ one }) => ({
113 | user: one(users, { fields: [userFolders.userId], references: [users.id] }),
114 | folder: one(folders, {
115 | fields: [userFolders.folderId],
116 | references: [folders.id],
117 | }),
118 | }));
119 |
120 | export const threadFoldersRelations = relations(threadFolders, ({ one }) => ({
121 | thread: one(threads, {
122 | fields: [threadFolders.threadId],
123 | references: [threads.id],
124 | }),
125 | folder: one(folders, {
126 | fields: [threadFolders.folderId],
127 | references: [folders.id],
128 | }),
129 | }));
130 |
--------------------------------------------------------------------------------
/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import * as SheetPrimitive from '@radix-ui/react-dialog';
4 | import { XIcon } from 'lucide-react';
5 | import * as React from 'react';
6 |
7 | import { Button } from '@/components/ui/button';
8 | import { cn } from '@/lib/utils';
9 |
10 | function Sheet({ ...props }: React.ComponentProps) {
11 | return ;
12 | }
13 |
14 | function SheetTrigger({
15 | ...props
16 | }: React.ComponentProps) {
17 | return ;
18 | }
19 |
20 | function SheetClose({
21 | ...props
22 | }: React.ComponentProps) {
23 | return ;
24 | }
25 |
26 | function SheetPortal({
27 | ...props
28 | }: React.ComponentProps) {
29 | return ;
30 | }
31 |
32 | function SheetOverlay({
33 | className,
34 | ...props
35 | }: React.ComponentProps) {
36 | return (
37 |
45 | );
46 | }
47 |
48 | function SheetContent({
49 | className,
50 | children,
51 | side = 'right',
52 | ...props
53 | }: React.ComponentProps & {
54 | side?: 'top' | 'right' | 'bottom' | 'left';
55 | }) {
56 | return (
57 |
58 |
59 |
75 | {children}
76 |
77 |
85 |
86 |
87 |
88 | );
89 | }
90 |
91 | function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
92 | return (
93 |
98 | );
99 | }
100 |
101 | function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
102 | return (
103 |
108 | );
109 | }
110 |
111 | function SheetTitle({
112 | className,
113 | ...props
114 | }: React.ComponentProps) {
115 | return (
116 |
121 | );
122 | }
123 |
124 | function SheetDescription({
125 | className,
126 | ...props
127 | }: React.ComponentProps) {
128 | return (
129 |
134 | );
135 | }
136 |
137 | export {
138 | Sheet,
139 | SheetClose,
140 | SheetContent,
141 | SheetDescription,
142 | SheetFooter,
143 | SheetHeader,
144 | SheetTitle,
145 | SheetTrigger,
146 | };
147 |
--------------------------------------------------------------------------------
/app/components/thread-list.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { ThreadActions } from '@/app/components/thread-actions';
4 | import { emails, users } from '@/lib/db/schema';
5 | import { formatEmailString } from '@/lib/utils';
6 | import { PenSquare, Search } from 'lucide-react';
7 | import Link from 'next/link';
8 | import { useEffect, useState } from 'react';
9 | import { NavMenu } from './menu';
10 |
11 | type Email = Omit & {
12 | sender: Pick;
13 | };
14 | type User = typeof users.$inferSelect;
15 |
16 | type ThreadWithEmails = {
17 | id: number;
18 | subject: string | null;
19 | lastActivityDate: Date | null;
20 | emails: Email[];
21 | };
22 |
23 | interface ThreadListProps {
24 | folderName: string;
25 | threads: ThreadWithEmails[];
26 | searchQuery?: string;
27 | }
28 |
29 | export function ThreadHeader({
30 | folderName,
31 | count,
32 | }: {
33 | folderName: string;
34 | count?: number | undefined;
35 | }) {
36 | return (
37 |
38 |
39 |
40 |
41 | {folderName}
42 | {count}
43 |
44 |
45 |
46 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
60 | );
61 | }
62 |
63 | export function ThreadList({ folderName, threads }: ThreadListProps) {
64 | const [hoveredThread, setHoveredThread] = useState(null);
65 | const [isMobile, setIsMobile] = useState(false);
66 |
67 | useEffect(() => {
68 | const checkIsMobile = () => {
69 | setIsMobile(window.matchMedia('(hover: none)').matches);
70 | };
71 |
72 | checkIsMobile();
73 | window.addEventListener('resize', checkIsMobile);
74 |
75 | return () => {
76 | window.removeEventListener('resize', checkIsMobile);
77 | };
78 | }, []);
79 |
80 | const handleMouseEnter = (threadId: number) => {
81 | if (!isMobile) {
82 | setHoveredThread(threadId);
83 | }
84 | };
85 |
86 | const handleMouseLeave = () => {
87 | if (!isMobile) {
88 | setHoveredThread(null);
89 | }
90 | };
91 |
92 | return (
93 |
94 |
95 |
96 | {threads.map((thread) => {
97 | const latestEmail = thread.emails[0];
98 |
99 | return (
100 |
105 |
handleMouseEnter(thread.id)}
108 | onMouseLeave={handleMouseLeave}
109 | >
110 |
111 |
112 |
113 | {formatEmailString(latestEmail.sender)}
114 |
115 |
116 |
117 |
118 | {thread.subject}
119 |
120 |
121 | {latestEmail.body}
122 |
123 |
124 |
125 |
126 | {!isMobile && hoveredThread === thread.id ? (
127 |
128 | ) : (
129 |
130 | {new Date(thread.lastActivityDate!).toLocaleDateString()}
131 |
132 | )}
133 |
134 |
135 |
136 | );
137 | })}
138 |
139 |
140 | );
141 | }
142 |
--------------------------------------------------------------------------------
/lib/db/actions.ts:
--------------------------------------------------------------------------------
1 | 'use server';
2 |
3 | import { eq } from 'drizzle-orm';
4 | import { revalidatePath } from 'next/cache';
5 | import { redirect } from 'next/navigation';
6 | import { z } from 'zod';
7 | import { db } from './drizzle';
8 | import { emails, folders, threadFolders, threads, users } from './schema';
9 |
10 | const sendEmailSchema = z.object({
11 | subject: z.string().min(1, 'Subject is required'),
12 | body: z.string().min(1, 'Body is required'),
13 | recipientEmail: z.string().email('Invalid email address'),
14 | });
15 |
16 | export async function sendEmailAction(_: any, formData: FormData) {
17 | let newThread;
18 | let rawFormData = {
19 | subject: formData.get('subject'),
20 | body: formData.get('body'),
21 | recipientEmail: formData.get('recipientEmail'),
22 | };
23 |
24 | if (process.env.VERCEL_ENV === 'production') {
25 | return {
26 | error: 'Only works on localhost for now',
27 | previous: rawFormData,
28 | };
29 | }
30 |
31 | try {
32 | let validatedFields = sendEmailSchema.parse({
33 | subject: formData.get('subject'),
34 | body: formData.get('body'),
35 | recipientEmail: formData.get('recipientEmail'),
36 | });
37 |
38 | let { subject, body, recipientEmail } = validatedFields;
39 |
40 | let [recipient] = await db
41 | .select()
42 | .from(users)
43 | .where(eq(users.email, recipientEmail));
44 |
45 | if (!recipient) {
46 | [recipient] = await db
47 | .insert(users)
48 | .values({ email: recipientEmail })
49 | .returning();
50 | }
51 |
52 | let result = await db
53 | .insert(threads)
54 | .values({
55 | subject,
56 | lastActivityDate: new Date(),
57 | })
58 | .returning();
59 | newThread = result[0];
60 |
61 | await db.insert(emails).values({
62 | threadId: newThread.id,
63 | senderId: 1, // Assuming the current user's ID is 1. Replace this with the actual user ID.
64 | recipientId: recipient.id,
65 | subject,
66 | body,
67 | sentDate: new Date(),
68 | });
69 |
70 | let [sentFolder] = await db
71 | .select()
72 | .from(folders)
73 | .where(eq(folders.name, 'Sent'));
74 |
75 | await db.insert(threadFolders).values({
76 | threadId: newThread.id,
77 | folderId: sentFolder.id,
78 | });
79 | } catch (error) {
80 | if (error instanceof z.ZodError) {
81 | return { error: error.errors[0].message, previous: rawFormData };
82 | }
83 | return {
84 | error: 'Failed to send email. Please try again.',
85 | previous: rawFormData,
86 | };
87 | }
88 |
89 | revalidatePath('/', 'layout');
90 | redirect(`/f/sent/${newThread.id}`);
91 | }
92 |
93 | export async function moveThreadToDone(_: any, formData: FormData) {
94 | if (process.env.VERCEL_ENV === 'production') {
95 | return {
96 | error: 'Only works on localhost for now',
97 | };
98 | }
99 |
100 | let threadId = formData.get('threadId');
101 |
102 | if (!threadId || typeof threadId !== 'string') {
103 | return { error: 'Invalid thread ID', success: false };
104 | }
105 |
106 | try {
107 | let doneFolder = await db.query.folders.findFirst({
108 | where: eq(folders.name, 'Archive'),
109 | });
110 |
111 | if (!doneFolder) {
112 | return { error: 'Done folder not found', success: false };
113 | }
114 |
115 | let parsedThreadId = parseInt(threadId, 10);
116 |
117 | await db
118 | .delete(threadFolders)
119 | .where(eq(threadFolders.threadId, parsedThreadId));
120 |
121 | await db.insert(threadFolders).values({
122 | threadId: parsedThreadId,
123 | folderId: doneFolder.id,
124 | });
125 |
126 | revalidatePath('/f/[name]');
127 | revalidatePath('/f/[name]/[id]');
128 | return { success: true, error: null };
129 | } catch (error) {
130 | console.error('Failed to move thread to Done:', error);
131 | return { success: false, error: 'Failed to move thread to Done' };
132 | }
133 | }
134 |
135 | export async function moveThreadToTrash(_: any, formData: FormData) {
136 | if (process.env.VERCEL_ENV === 'production') {
137 | return {
138 | error: 'Only works on localhost for now',
139 | };
140 | }
141 |
142 | let threadId = formData.get('threadId');
143 |
144 | if (!threadId || typeof threadId !== 'string') {
145 | return { error: 'Invalid thread ID', success: false };
146 | }
147 |
148 | try {
149 | let trashFolder = await db.query.folders.findFirst({
150 | where: eq(folders.name, 'Trash'),
151 | });
152 |
153 | if (!trashFolder) {
154 | return { error: 'Trash folder not found', success: false };
155 | }
156 |
157 | let parsedThreadId = parseInt(threadId, 10);
158 |
159 | await db
160 | .delete(threadFolders)
161 | .where(eq(threadFolders.threadId, parsedThreadId));
162 |
163 | await db.insert(threadFolders).values({
164 | threadId: parsedThreadId,
165 | folderId: trashFolder.id,
166 | });
167 |
168 | revalidatePath('/f/[name]');
169 | revalidatePath('/f/[name]/[id]');
170 | return { success: true, error: null };
171 | } catch (error) {
172 | console.error('Failed to move thread to Trash:', error);
173 | return { success: false, error: 'Failed to move thread to Trash' };
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/lib/db/seed.ts:
--------------------------------------------------------------------------------
1 | import { db } from './drizzle';
2 | import {
3 | emails,
4 | folders,
5 | threadFolders,
6 | threads,
7 | userFolders,
8 | users,
9 | } from './schema';
10 |
11 | async function seed() {
12 | console.log('Starting seed process...');
13 | await seedUsers();
14 | await seedFolders();
15 | await seedThreadsAndEmails();
16 | console.log('Seed process completed successfully.');
17 | }
18 |
19 | async function seedUsers() {
20 | await db.insert(users).values([
21 | {
22 | firstName: 'Lee',
23 | lastName: 'Robinson',
24 | email: 'lee@leerob.com',
25 | jobTitle: 'VP of Product',
26 | company: 'Vercel',
27 | location: 'Des Moines, Iowa',
28 | avatarUrl: 'https://github.com/leerob.png',
29 | linkedin: 'https://www.linkedin.com/in/leeerob/',
30 | twitter: 'https://x.com/leerob',
31 | github: 'https://github.com/leerob',
32 | },
33 | {
34 | firstName: 'Guillermo',
35 | lastName: 'Rauch',
36 | email: 'rauchg@vercel.com',
37 | jobTitle: 'CEO',
38 | company: 'Vercel',
39 | location: 'San Francisco, California',
40 | avatarUrl: 'https://github.com/rauchg.png',
41 | },
42 | {
43 | firstName: 'Delba',
44 | lastName: 'de Oliveira',
45 | email: 'delba.oliveira@vercel.com',
46 | jobTitle: 'Staff DX Engineer',
47 | company: 'Vercel',
48 | location: 'London, UK',
49 | avatarUrl: 'https://github.com/delbaoliveira.png',
50 | },
51 | {
52 | firstName: 'Tim',
53 | lastName: 'Neutkens',
54 | email: 'tim@vercel.com',
55 | jobTitle: 'Next.js Lead',
56 | company: 'Vercel',
57 | location: 'Amsterdam, Netherlands',
58 | avatarUrl: 'https://github.com/timneutkens.png',
59 | },
60 | ]);
61 | }
62 |
63 | async function seedFolders() {
64 | await db
65 | .insert(folders)
66 | .values([
67 | { name: 'Inbox' },
68 | { name: 'Flagged' },
69 | { name: 'Sent' },
70 | { name: 'Archive' },
71 | { name: 'Spam' },
72 | { name: 'Trash' },
73 | ]);
74 |
75 | const userFolderValues = [];
76 | for (let userId = 1; userId <= 4; userId++) {
77 | for (let folderId = 1; folderId <= 6; folderId++) {
78 | userFolderValues.push({ userId, folderId });
79 | }
80 | }
81 | await db.insert(userFolders).values(userFolderValues);
82 | }
83 |
84 | async function seedThreadsAndEmails() {
85 | // Thread 1: Guillermo talking about Vercel customer feedback
86 | const thread1 = await db
87 | .insert(threads)
88 | .values({
89 | subject: 'Vercel Customer Feedback',
90 | lastActivityDate: new Date('2023-05-15T10:00:00'),
91 | })
92 | .returning();
93 |
94 | await db.insert(emails).values([
95 | {
96 | threadId: thread1[0].id,
97 | senderId: 2, // Guillermo
98 | recipientId: 1, // Lee
99 | subject: 'Vercel Customer Feedback',
100 | body: 'Met with Daniel today. He had some great feedback. After you make a change to your environment variables, he wants to immediately redeploy the application. We should make a toast that has a CTA to redeploy. Thoughts?',
101 | sentDate: new Date('2023-05-15T10:00:00'),
102 | },
103 | {
104 | threadId: thread1[0].id,
105 | senderId: 1, // Lee
106 | recipientId: 2, // Guillermo
107 | subject: 'Re: Vercel Customer Feedback',
108 | body: "Good call. I've seen this multiple times now. Let's do it.",
109 | sentDate: new Date('2023-05-15T11:30:00'),
110 | },
111 | {
112 | threadId: thread1[0].id,
113 | senderId: 2, // Guillermo
114 | recipientId: 1, // Lee
115 | subject: 'Re: Vercel Customer Feedback',
116 | body: "Amazing. Let me know when it shipped and I'll follow up.",
117 | sentDate: new Date('2023-05-15T13:45:00'),
118 | },
119 | ]);
120 |
121 | // Thread 2: Delba talking about Next.js and testing out new features
122 | const thread2 = await db
123 | .insert(threads)
124 | .values({
125 | subject: 'New Next.js RFC',
126 | lastActivityDate: new Date('2023-05-16T09:00:00'),
127 | })
128 | .returning();
129 |
130 | await db.insert(emails).values([
131 | {
132 | threadId: thread2[0].id,
133 | senderId: 3, // Delba
134 | recipientId: 1, // Lee
135 | subject: 'New Next.js RFC',
136 | body: "I'm working on the first draft of the Dynamic IO docs and examples. Do you want to take a look?",
137 | sentDate: new Date('2023-05-16T09:00:00'),
138 | },
139 | {
140 | threadId: thread2[0].id,
141 | senderId: 1, // Lee
142 | recipientId: 3, // Delba
143 | subject: 'Re: New Next.js RFC',
144 | body: "Absolutely. Let me take a look later tonight and I'll send over feedback.",
145 | sentDate: new Date('2023-05-16T10:15:00'),
146 | },
147 | {
148 | threadId: thread2[0].id,
149 | senderId: 3, // Delba
150 | recipientId: 1, // Lee
151 | subject: 'Re: New Next.js RFC',
152 | body: 'Thank you!',
153 | sentDate: new Date('2023-05-16T11:30:00'),
154 | },
155 | ]);
156 |
157 | // Thread 3: Tim with steps to test out Turbopack
158 | const thread3 = await db
159 | .insert(threads)
160 | .values({
161 | subject: 'Turbopack Testing',
162 | lastActivityDate: new Date('2023-05-17T14:00:00'),
163 | })
164 | .returning();
165 |
166 | await db.insert(emails).values([
167 | {
168 | threadId: thread3[0].id,
169 | senderId: 4, // Tim
170 | recipientId: 1, // Lee
171 | subject: 'Turbopack Testing Steps',
172 | body: `Hi Lee,
173 |
174 | Here are the steps to test out Turbopack:
175 |
176 | 1. npx create-next-app@canary
177 | 2. Select Turbopack when prompted
178 | 3. Run 'npm install' to install dependencies
179 | 4. Start the development server with 'npm run dev -- --turbo'
180 | 5. That's it!
181 |
182 | Let me know if you encounter any issues or have any questions.
183 |
184 | Best,
185 | Tim`,
186 | sentDate: new Date('2023-05-17T14:00:00'),
187 | },
188 | ]);
189 |
190 | // Add threads to folders
191 | await db.insert(threadFolders).values([
192 | { threadId: thread1[0].id, folderId: 1 }, // Inbox
193 | { threadId: thread2[0].id, folderId: 1 }, // Inbox
194 | { threadId: thread3[0].id, folderId: 1 }, // Inbox
195 | ]);
196 | }
197 |
198 | seed()
199 | .catch((error) => {
200 | console.error('Seed process failed:', error);
201 | process.exit(1);
202 | })
203 | .finally(async () => {
204 | console.log('Seed process finished. Exiting...');
205 | process.exit(0);
206 | });
207 |
--------------------------------------------------------------------------------
/app/f/[name]/new/page.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | import { LeftSidebar } from '@/app/components/left-sidebar';
4 | import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
5 | import {
6 | Tooltip,
7 | TooltipContent,
8 | TooltipProvider,
9 | TooltipTrigger,
10 | } from '@/components/ui/tooltip';
11 | import { sendEmailAction } from '@/lib/db/actions';
12 | import { ExclamationTriangleIcon } from '@radix-ui/react-icons';
13 | import { Paperclip, Trash2 } from 'lucide-react';
14 | import Link from 'next/link';
15 | import { useParams } from 'next/navigation';
16 | import { Suspense, useActionState } from 'react';
17 |
18 | function DiscardDraftLink() {
19 | let { name } = useParams();
20 |
21 | return (
22 |
23 |
24 |
25 | );
26 | }
27 |
28 | function EmailBody({ defaultValue = '' }: { defaultValue?: string }) {
29 | const handleKeyDown = (e: React.KeyboardEvent) => {
30 | if (
31 | (e.ctrlKey || e.metaKey) &&
32 | (e.key === 'Enter' || e.key === 'NumpadEnter')
33 | ) {
34 | e.preventDefault();
35 | e.currentTarget.form?.requestSubmit();
36 | }
37 | };
38 |
39 | return (
40 |
41 |
49 |
50 | );
51 | }
52 |
53 | export default function ComposePage() {
54 | let [state, formAction] = useActionState(sendEmailAction, {
55 | error: '',
56 | previous: {
57 | recipientEmail: '',
58 | subject: '',
59 | body: '',
60 | },
61 | });
62 |
63 | const isProduction = process.env.NEXT_PUBLIC_VERCEL_ENV === 'production';
64 |
65 | return (
66 |
67 |
68 |
69 |
New Message
70 | {state.error && (
71 |
72 |
73 |
74 | Error
75 | {state.error}
76 |
77 |
78 | )}
79 |
173 |
174 |
175 | );
176 | }
177 |
--------------------------------------------------------------------------------
/lib/db/queries.ts:
--------------------------------------------------------------------------------
1 | import { and, desc, eq, ilike, or, sql } from 'drizzle-orm';
2 | import { toTitleCase } from '../utils';
3 | import { db } from './drizzle';
4 | import { emails, folders, threadFolders, threads, users } from './schema';
5 |
6 | type Folder = {
7 | name: string;
8 | thread_count: number;
9 | };
10 |
11 | export async function getFoldersWithThreadCount() {
12 | 'use cache';
13 |
14 | let foldersWithCount = await db
15 | .select({
16 | name: folders.name,
17 | thread_count: sql`count(${threadFolders.threadId})`.as(
18 | 'thread_count',
19 | ),
20 | })
21 | .from(folders)
22 | .leftJoin(threadFolders, eq(folders.id, threadFolders.folderId))
23 | .groupBy(folders.name);
24 |
25 | let specialFoldersOrder = ['Inbox', 'Flagged', 'Sent'];
26 | let specialFolders = specialFoldersOrder
27 | .map((name) => foldersWithCount.find((folder) => folder.name === name))
28 | .filter(Boolean) as Folder[];
29 | let otherFolders = foldersWithCount.filter(
30 | (folder) => !specialFoldersOrder.includes(folder.name),
31 | ) as Folder[];
32 |
33 | return { specialFolders, otherFolders };
34 | }
35 |
36 | export async function getThreadsForFolder(folderName: string) {
37 | 'use cache';
38 |
39 | let originalFolderName = toTitleCase(decodeURIComponent(folderName));
40 |
41 | const threadsWithEmails = await db
42 | .select({
43 | id: threads.id,
44 | subject: threads.subject,
45 | lastActivityDate: threads.lastActivityDate,
46 | emails: sql<
47 | {
48 | id: number;
49 | senderId: number;
50 | recipientId: number;
51 | subject: string;
52 | body: string;
53 | sentDate: Date;
54 | sender: {
55 | id: number;
56 | firstName: string;
57 | lastName: string;
58 | email: string;
59 | };
60 | }[]
61 | >`json_agg(json_build_object(
62 | 'id', ${emails.id},
63 | 'senderId', ${emails.senderId},
64 | 'recipientId', ${emails.recipientId},
65 | 'subject', ${emails.subject},
66 | 'body', ${emails.body},
67 | 'sentDate', ${emails.sentDate},
68 | 'sender', json_build_object(
69 | 'id', ${users.id},
70 | 'firstName', ${users.firstName},
71 | 'lastName', ${users.lastName},
72 | 'email', ${users.email}
73 | )
74 | ) ORDER BY ${emails.sentDate} DESC)`,
75 | })
76 | .from(threads)
77 | .innerJoin(threadFolders, eq(threads.id, threadFolders.threadId))
78 | .innerJoin(folders, eq(threadFolders.folderId, folders.id))
79 | .innerJoin(emails, eq(threads.id, emails.threadId))
80 | .innerJoin(users, eq(emails.senderId, users.id))
81 | .where(eq(folders.name, originalFolderName))
82 | .groupBy(threads.id)
83 | .orderBy(desc(threads.lastActivityDate));
84 |
85 | return threadsWithEmails;
86 | }
87 |
88 | export async function searchThreads(search: string | undefined) {
89 | if (!search) {
90 | return [];
91 | }
92 |
93 | const results = await db
94 | .select({
95 | id: threads.id,
96 | subject: threads.subject,
97 | lastActivityDate: threads.lastActivityDate,
98 | folderName: folders.name,
99 | emailId: emails.id,
100 | emailSubject: emails.subject,
101 | emailBody: emails.body,
102 | emailSentDate: emails.sentDate,
103 | senderFirstName: users.firstName,
104 | senderLastName: users.lastName,
105 | senderEmail: users.email,
106 | })
107 | .from(threads)
108 | .innerJoin(emails, eq(threads.id, emails.threadId))
109 | .innerJoin(users, eq(emails.senderId, users.id))
110 | .leftJoin(threadFolders, eq(threads.id, threadFolders.threadId))
111 | .leftJoin(folders, eq(threadFolders.folderId, folders.id))
112 | .where(
113 | or(
114 | ilike(users.firstName, `%${search}%`),
115 | ilike(users.lastName, `%${search}%`),
116 | ilike(users.email, `%${search}%`),
117 | ilike(threads.subject, `%${search}%`),
118 | ilike(emails.body, `%${search}%`),
119 | ),
120 | )
121 | .orderBy(desc(threads.lastActivityDate), desc(emails.sentDate));
122 |
123 | const threadMap = new Map();
124 | for (const result of results) {
125 | if (!threadMap.has(result.id)) {
126 | threadMap.set(result.id, {
127 | id: result.id,
128 | subject: result.subject,
129 | lastActivityDate: result.lastActivityDate,
130 | folderName: result.folderName,
131 | latestEmail: {
132 | id: result.emailId,
133 | subject: result.emailSubject,
134 | body: result.emailBody,
135 | sentDate: result.emailSentDate,
136 | sender: {
137 | firstName: result.senderFirstName,
138 | lastName: result.senderLastName,
139 | email: result.senderEmail,
140 | },
141 | },
142 | });
143 | }
144 | }
145 |
146 | return Array.from(threadMap.values());
147 | }
148 |
149 | export async function getThreadInFolder(folderName: string, threadId: string) {
150 | 'use cache';
151 |
152 | let originalFolderName = toTitleCase(decodeURIComponent(folderName));
153 | let result = await db
154 | .select({
155 | id: threads.id,
156 | subject: threads.subject,
157 | lastActivityDate: threads.lastActivityDate,
158 | senderFirstName: users.firstName,
159 | senderLastName: users.lastName,
160 | senderEmail: users.email,
161 | })
162 | .from(threads)
163 | .innerJoin(threadFolders, eq(threads.id, threadFolders.threadId))
164 | .innerJoin(folders, eq(threadFolders.folderId, folders.id))
165 | .innerJoin(emails, eq(threads.id, emails.threadId))
166 | .innerJoin(users, eq(emails.senderId, users.id))
167 | .where(
168 | and(
169 | eq(folders.name, originalFolderName),
170 | eq(threads.id, parseInt(threadId)),
171 | ),
172 | );
173 |
174 | return result[0];
175 | }
176 |
177 | export async function getEmailsForThread(threadId: string) {
178 | 'use cache';
179 |
180 | const result = await db
181 | .select({
182 | id: threads.id,
183 | subject: threads.subject,
184 | emailId: emails.id,
185 | body: emails.body,
186 | sentDate: emails.sentDate,
187 | senderId: users.id,
188 | senderFirstName: users.firstName,
189 | senderLastName: users.lastName,
190 | recipientId: emails.recipientId,
191 | })
192 | .from(threads)
193 | .innerJoin(emails, eq(threads.id, emails.threadId))
194 | .innerJoin(users, eq(emails.senderId, users.id))
195 | .where(eq(threads.id, parseInt(threadId, 10)))
196 | .orderBy(emails.sentDate);
197 |
198 | if (result.length === 0) {
199 | return null;
200 | }
201 |
202 | const thread = {
203 | id: result[0].id,
204 | subject: result[0].subject,
205 | emails: result.map((row) => ({
206 | id: row.emailId,
207 | body: row.body,
208 | sentDate: row.sentDate,
209 | sender: {
210 | id: row.senderId,
211 | firstName: row.senderFirstName,
212 | lastName: row.senderLastName,
213 | },
214 | recipientId: row.recipientId,
215 | })),
216 | };
217 |
218 | return thread;
219 | }
220 |
221 | export async function getAllEmailAddresses() {
222 | 'use cache';
223 |
224 | return db
225 | .select({
226 | firstName: users.firstName,
227 | lastName: users.lastName,
228 | email: users.email,
229 | })
230 | .from(users);
231 | }
232 |
233 | export async function getUserProfile(userId: number) {
234 | 'use cache';
235 |
236 | const userInfo = await db
237 | .select({
238 | id: users.id,
239 | firstName: users.firstName,
240 | lastName: users.lastName,
241 | email: users.email,
242 | jobTitle: users.jobTitle,
243 | company: users.company,
244 | location: users.location,
245 | avatarUrl: users.avatarUrl,
246 | linkedin: users.linkedin,
247 | twitter: users.twitter,
248 | github: users.github,
249 | })
250 | .from(users)
251 | .where(eq(users.id, userId))
252 | .limit(1)
253 | .execute();
254 |
255 | if (userInfo.length === 0) {
256 | return null;
257 | }
258 |
259 | const latestThreads = await db
260 | .select({
261 | subject: threads.subject,
262 | })
263 | .from(threads)
264 | .innerJoin(emails, eq(emails.threadId, threads.id))
265 | .where(eq(emails.senderId, userId))
266 | .orderBy(desc(threads.lastActivityDate))
267 | .limit(3)
268 | .execute();
269 |
270 | return {
271 | ...userInfo[0],
272 | latestThreads,
273 | };
274 | }
275 |
--------------------------------------------------------------------------------
/lib/db/migrations/meta/0000_snapshot.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "2bb4aa26-21bc-47db-a17f-dd39c8e3048e",
3 | "prevId": "00000000-0000-0000-0000-000000000000",
4 | "version": "7",
5 | "dialect": "postgresql",
6 | "tables": {
7 | "public.emails": {
8 | "name": "emails",
9 | "schema": "",
10 | "columns": {
11 | "id": {
12 | "name": "id",
13 | "type": "serial",
14 | "primaryKey": true,
15 | "notNull": true
16 | },
17 | "thread_id": {
18 | "name": "thread_id",
19 | "type": "integer",
20 | "primaryKey": false,
21 | "notNull": false
22 | },
23 | "sender_id": {
24 | "name": "sender_id",
25 | "type": "integer",
26 | "primaryKey": false,
27 | "notNull": false
28 | },
29 | "recipient_id": {
30 | "name": "recipient_id",
31 | "type": "integer",
32 | "primaryKey": false,
33 | "notNull": false
34 | },
35 | "subject": {
36 | "name": "subject",
37 | "type": "varchar(255)",
38 | "primaryKey": false,
39 | "notNull": false
40 | },
41 | "body": {
42 | "name": "body",
43 | "type": "text",
44 | "primaryKey": false,
45 | "notNull": false
46 | },
47 | "sent_date": {
48 | "name": "sent_date",
49 | "type": "timestamp",
50 | "primaryKey": false,
51 | "notNull": false,
52 | "default": "now()"
53 | }
54 | },
55 | "indexes": {
56 | "thread_id_idx": {
57 | "name": "thread_id_idx",
58 | "columns": [
59 | {
60 | "expression": "thread_id",
61 | "isExpression": false,
62 | "asc": true,
63 | "nulls": "last"
64 | }
65 | ],
66 | "isUnique": false,
67 | "concurrently": false,
68 | "method": "btree",
69 | "with": {}
70 | },
71 | "sender_id_idx": {
72 | "name": "sender_id_idx",
73 | "columns": [
74 | {
75 | "expression": "sender_id",
76 | "isExpression": false,
77 | "asc": true,
78 | "nulls": "last"
79 | }
80 | ],
81 | "isUnique": false,
82 | "concurrently": false,
83 | "method": "btree",
84 | "with": {}
85 | },
86 | "recipient_id_idx": {
87 | "name": "recipient_id_idx",
88 | "columns": [
89 | {
90 | "expression": "recipient_id",
91 | "isExpression": false,
92 | "asc": true,
93 | "nulls": "last"
94 | }
95 | ],
96 | "isUnique": false,
97 | "concurrently": false,
98 | "method": "btree",
99 | "with": {}
100 | },
101 | "sent_date_idx": {
102 | "name": "sent_date_idx",
103 | "columns": [
104 | {
105 | "expression": "sent_date",
106 | "isExpression": false,
107 | "asc": true,
108 | "nulls": "last"
109 | }
110 | ],
111 | "isUnique": false,
112 | "concurrently": false,
113 | "method": "btree",
114 | "with": {}
115 | }
116 | },
117 | "foreignKeys": {
118 | "emails_thread_id_threads_id_fk": {
119 | "name": "emails_thread_id_threads_id_fk",
120 | "tableFrom": "emails",
121 | "tableTo": "threads",
122 | "columnsFrom": ["thread_id"],
123 | "columnsTo": ["id"],
124 | "onDelete": "no action",
125 | "onUpdate": "no action"
126 | },
127 | "emails_sender_id_users_id_fk": {
128 | "name": "emails_sender_id_users_id_fk",
129 | "tableFrom": "emails",
130 | "tableTo": "users",
131 | "columnsFrom": ["sender_id"],
132 | "columnsTo": ["id"],
133 | "onDelete": "no action",
134 | "onUpdate": "no action"
135 | },
136 | "emails_recipient_id_users_id_fk": {
137 | "name": "emails_recipient_id_users_id_fk",
138 | "tableFrom": "emails",
139 | "tableTo": "users",
140 | "columnsFrom": ["recipient_id"],
141 | "columnsTo": ["id"],
142 | "onDelete": "no action",
143 | "onUpdate": "no action"
144 | }
145 | },
146 | "compositePrimaryKeys": {},
147 | "uniqueConstraints": {}
148 | },
149 | "public.folders": {
150 | "name": "folders",
151 | "schema": "",
152 | "columns": {
153 | "id": {
154 | "name": "id",
155 | "type": "serial",
156 | "primaryKey": true,
157 | "notNull": true
158 | },
159 | "name": {
160 | "name": "name",
161 | "type": "varchar(50)",
162 | "primaryKey": false,
163 | "notNull": true
164 | }
165 | },
166 | "indexes": {},
167 | "foreignKeys": {},
168 | "compositePrimaryKeys": {},
169 | "uniqueConstraints": {}
170 | },
171 | "public.thread_folders": {
172 | "name": "thread_folders",
173 | "schema": "",
174 | "columns": {
175 | "id": {
176 | "name": "id",
177 | "type": "serial",
178 | "primaryKey": true,
179 | "notNull": true
180 | },
181 | "thread_id": {
182 | "name": "thread_id",
183 | "type": "integer",
184 | "primaryKey": false,
185 | "notNull": false
186 | },
187 | "folder_id": {
188 | "name": "folder_id",
189 | "type": "integer",
190 | "primaryKey": false,
191 | "notNull": false
192 | }
193 | },
194 | "indexes": {},
195 | "foreignKeys": {
196 | "thread_folders_thread_id_threads_id_fk": {
197 | "name": "thread_folders_thread_id_threads_id_fk",
198 | "tableFrom": "thread_folders",
199 | "tableTo": "threads",
200 | "columnsFrom": ["thread_id"],
201 | "columnsTo": ["id"],
202 | "onDelete": "no action",
203 | "onUpdate": "no action"
204 | },
205 | "thread_folders_folder_id_folders_id_fk": {
206 | "name": "thread_folders_folder_id_folders_id_fk",
207 | "tableFrom": "thread_folders",
208 | "tableTo": "folders",
209 | "columnsFrom": ["folder_id"],
210 | "columnsTo": ["id"],
211 | "onDelete": "no action",
212 | "onUpdate": "no action"
213 | }
214 | },
215 | "compositePrimaryKeys": {},
216 | "uniqueConstraints": {}
217 | },
218 | "public.threads": {
219 | "name": "threads",
220 | "schema": "",
221 | "columns": {
222 | "id": {
223 | "name": "id",
224 | "type": "serial",
225 | "primaryKey": true,
226 | "notNull": true
227 | },
228 | "subject": {
229 | "name": "subject",
230 | "type": "varchar(255)",
231 | "primaryKey": false,
232 | "notNull": false
233 | },
234 | "last_activity_date": {
235 | "name": "last_activity_date",
236 | "type": "timestamp",
237 | "primaryKey": false,
238 | "notNull": false,
239 | "default": "now()"
240 | }
241 | },
242 | "indexes": {},
243 | "foreignKeys": {},
244 | "compositePrimaryKeys": {},
245 | "uniqueConstraints": {}
246 | },
247 | "public.user_folders": {
248 | "name": "user_folders",
249 | "schema": "",
250 | "columns": {
251 | "id": {
252 | "name": "id",
253 | "type": "serial",
254 | "primaryKey": true,
255 | "notNull": true
256 | },
257 | "user_id": {
258 | "name": "user_id",
259 | "type": "integer",
260 | "primaryKey": false,
261 | "notNull": false
262 | },
263 | "folder_id": {
264 | "name": "folder_id",
265 | "type": "integer",
266 | "primaryKey": false,
267 | "notNull": false
268 | }
269 | },
270 | "indexes": {},
271 | "foreignKeys": {
272 | "user_folders_user_id_users_id_fk": {
273 | "name": "user_folders_user_id_users_id_fk",
274 | "tableFrom": "user_folders",
275 | "tableTo": "users",
276 | "columnsFrom": ["user_id"],
277 | "columnsTo": ["id"],
278 | "onDelete": "no action",
279 | "onUpdate": "no action"
280 | },
281 | "user_folders_folder_id_folders_id_fk": {
282 | "name": "user_folders_folder_id_folders_id_fk",
283 | "tableFrom": "user_folders",
284 | "tableTo": "folders",
285 | "columnsFrom": ["folder_id"],
286 | "columnsTo": ["id"],
287 | "onDelete": "no action",
288 | "onUpdate": "no action"
289 | }
290 | },
291 | "compositePrimaryKeys": {},
292 | "uniqueConstraints": {}
293 | },
294 | "public.users": {
295 | "name": "users",
296 | "schema": "",
297 | "columns": {
298 | "id": {
299 | "name": "id",
300 | "type": "serial",
301 | "primaryKey": true,
302 | "notNull": true
303 | },
304 | "first_name": {
305 | "name": "first_name",
306 | "type": "varchar(50)",
307 | "primaryKey": false,
308 | "notNull": false
309 | },
310 | "last_name": {
311 | "name": "last_name",
312 | "type": "varchar(50)",
313 | "primaryKey": false,
314 | "notNull": false
315 | },
316 | "email": {
317 | "name": "email",
318 | "type": "varchar(255)",
319 | "primaryKey": false,
320 | "notNull": true
321 | },
322 | "job_title": {
323 | "name": "job_title",
324 | "type": "varchar(100)",
325 | "primaryKey": false,
326 | "notNull": false
327 | },
328 | "company": {
329 | "name": "company",
330 | "type": "varchar(100)",
331 | "primaryKey": false,
332 | "notNull": false
333 | },
334 | "location": {
335 | "name": "location",
336 | "type": "varchar(100)",
337 | "primaryKey": false,
338 | "notNull": false
339 | },
340 | "twitter": {
341 | "name": "twitter",
342 | "type": "varchar(100)",
343 | "primaryKey": false,
344 | "notNull": false
345 | },
346 | "linkedin": {
347 | "name": "linkedin",
348 | "type": "varchar(100)",
349 | "primaryKey": false,
350 | "notNull": false
351 | },
352 | "github": {
353 | "name": "github",
354 | "type": "varchar(100)",
355 | "primaryKey": false,
356 | "notNull": false
357 | },
358 | "avatar_url": {
359 | "name": "avatar_url",
360 | "type": "varchar(255)",
361 | "primaryKey": false,
362 | "notNull": false
363 | }
364 | },
365 | "indexes": {
366 | "email_idx": {
367 | "name": "email_idx",
368 | "columns": [
369 | {
370 | "expression": "email",
371 | "isExpression": false,
372 | "asc": true,
373 | "nulls": "last"
374 | }
375 | ],
376 | "isUnique": true,
377 | "concurrently": false,
378 | "method": "btree",
379 | "with": {}
380 | }
381 | },
382 | "foreignKeys": {},
383 | "compositePrimaryKeys": {},
384 | "uniqueConstraints": {}
385 | }
386 | },
387 | "enums": {},
388 | "schemas": {},
389 | "sequences": {},
390 | "_meta": {
391 | "columns": {},
392 | "schemas": {},
393 | "tables": {}
394 | }
395 | }
396 |
--------------------------------------------------------------------------------
/pnpm-lock.yaml:
--------------------------------------------------------------------------------
1 | lockfileVersion: '9.0'
2 |
3 | settings:
4 | autoInstallPeers: true
5 | excludeLinksFromLockfile: false
6 |
7 | importers:
8 |
9 | .:
10 | dependencies:
11 | '@radix-ui/react-dialog':
12 | specifier: ^1.1.6
13 | version: 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
14 | '@radix-ui/react-icons':
15 | specifier: ^1.3.2
16 | version: 1.3.2(react@19.0.0)
17 | '@radix-ui/react-slot':
18 | specifier: ^1.1.2
19 | version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
20 | '@radix-ui/react-tooltip':
21 | specifier: ^1.1.8
22 | version: 1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
23 | '@tailwindcss/postcss':
24 | specifier: ^4.0.13
25 | version: 4.0.13
26 | '@types/node':
27 | specifier: ^22.10.1
28 | version: 22.10.1
29 | '@types/react':
30 | specifier: 19.0.10
31 | version: 19.0.10
32 | '@types/react-dom':
33 | specifier: 19.0.4
34 | version: 19.0.4(@types/react@19.0.10)
35 | class-variance-authority:
36 | specifier: ^0.7.1
37 | version: 0.7.1
38 | clsx:
39 | specifier: ^2.1.1
40 | version: 2.1.1
41 | dotenv:
42 | specifier: ^16.4.5
43 | version: 16.4.5
44 | drizzle-kit:
45 | specifier: ^0.28.1
46 | version: 0.28.1
47 | drizzle-orm:
48 | specifier: ^0.36.4
49 | version: 0.36.4(@types/react@19.0.10)(postgres@3.4.5)(react@19.0.0)
50 | geist:
51 | specifier: ^1.3.1
52 | version: 1.3.1(next@15.6.0-canary.59(react-dom@19.0.0(react@19.0.0))(react@19.0.0))
53 | lucide-react:
54 | specifier: ^0.462.0
55 | version: 0.462.0(react@19.0.0)
56 | next:
57 | specifier: 15.6.0-canary.59
58 | version: 15.6.0-canary.59(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
59 | postcss:
60 | specifier: ^8.4.49
61 | version: 8.4.49
62 | postgres:
63 | specifier: ^3.4.5
64 | version: 3.4.5
65 | react:
66 | specifier: 19.0.0
67 | version: 19.0.0
68 | react-dom:
69 | specifier: 19.0.0
70 | version: 19.0.0(react@19.0.0)
71 | sonner:
72 | specifier: ^1.7.0
73 | version: 1.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
74 | tailwind-merge:
75 | specifier: ^3.0.2
76 | version: 3.0.2
77 | tailwindcss:
78 | specifier: ^4.0.13
79 | version: 4.0.13
80 | tailwindcss-animate:
81 | specifier: ^1.0.7
82 | version: 1.0.7(tailwindcss@4.0.13)
83 | typescript:
84 | specifier: ^5.8.2
85 | version: 5.8.2
86 | zod:
87 | specifier: ^3.23.8
88 | version: 3.23.8
89 | devDependencies:
90 | prettier-plugin-organize-imports:
91 | specifier: ^4.1.0
92 | version: 4.1.0(prettier@3.5.3)(typescript@5.8.2)
93 | prettier-plugin-tailwindcss:
94 | specifier: ^0.6.11
95 | version: 0.6.11(prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.2))(prettier@3.5.3)
96 |
97 | packages:
98 |
99 | '@alloc/quick-lru@5.2.0':
100 | resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
101 | engines: {node: '>=10'}
102 |
103 | '@drizzle-team/brocli@0.10.2':
104 | resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
105 |
106 | '@emnapi/runtime@1.7.1':
107 | resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==}
108 |
109 | '@esbuild-kit/core-utils@3.3.2':
110 | resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
111 | deprecated: 'Merged into tsx: https://tsx.is'
112 |
113 | '@esbuild-kit/esm-loader@2.6.5':
114 | resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
115 | deprecated: 'Merged into tsx: https://tsx.is'
116 |
117 | '@esbuild/aix-ppc64@0.19.12':
118 | resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
119 | engines: {node: '>=12'}
120 | cpu: [ppc64]
121 | os: [aix]
122 |
123 | '@esbuild/android-arm64@0.18.20':
124 | resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
125 | engines: {node: '>=12'}
126 | cpu: [arm64]
127 | os: [android]
128 |
129 | '@esbuild/android-arm64@0.19.12':
130 | resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
131 | engines: {node: '>=12'}
132 | cpu: [arm64]
133 | os: [android]
134 |
135 | '@esbuild/android-arm@0.18.20':
136 | resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
137 | engines: {node: '>=12'}
138 | cpu: [arm]
139 | os: [android]
140 |
141 | '@esbuild/android-arm@0.19.12':
142 | resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
143 | engines: {node: '>=12'}
144 | cpu: [arm]
145 | os: [android]
146 |
147 | '@esbuild/android-x64@0.18.20':
148 | resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
149 | engines: {node: '>=12'}
150 | cpu: [x64]
151 | os: [android]
152 |
153 | '@esbuild/android-x64@0.19.12':
154 | resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
155 | engines: {node: '>=12'}
156 | cpu: [x64]
157 | os: [android]
158 |
159 | '@esbuild/darwin-arm64@0.18.20':
160 | resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
161 | engines: {node: '>=12'}
162 | cpu: [arm64]
163 | os: [darwin]
164 |
165 | '@esbuild/darwin-arm64@0.19.12':
166 | resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
167 | engines: {node: '>=12'}
168 | cpu: [arm64]
169 | os: [darwin]
170 |
171 | '@esbuild/darwin-x64@0.18.20':
172 | resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
173 | engines: {node: '>=12'}
174 | cpu: [x64]
175 | os: [darwin]
176 |
177 | '@esbuild/darwin-x64@0.19.12':
178 | resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
179 | engines: {node: '>=12'}
180 | cpu: [x64]
181 | os: [darwin]
182 |
183 | '@esbuild/freebsd-arm64@0.18.20':
184 | resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
185 | engines: {node: '>=12'}
186 | cpu: [arm64]
187 | os: [freebsd]
188 |
189 | '@esbuild/freebsd-arm64@0.19.12':
190 | resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
191 | engines: {node: '>=12'}
192 | cpu: [arm64]
193 | os: [freebsd]
194 |
195 | '@esbuild/freebsd-x64@0.18.20':
196 | resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
197 | engines: {node: '>=12'}
198 | cpu: [x64]
199 | os: [freebsd]
200 |
201 | '@esbuild/freebsd-x64@0.19.12':
202 | resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
203 | engines: {node: '>=12'}
204 | cpu: [x64]
205 | os: [freebsd]
206 |
207 | '@esbuild/linux-arm64@0.18.20':
208 | resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
209 | engines: {node: '>=12'}
210 | cpu: [arm64]
211 | os: [linux]
212 |
213 | '@esbuild/linux-arm64@0.19.12':
214 | resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
215 | engines: {node: '>=12'}
216 | cpu: [arm64]
217 | os: [linux]
218 |
219 | '@esbuild/linux-arm@0.18.20':
220 | resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
221 | engines: {node: '>=12'}
222 | cpu: [arm]
223 | os: [linux]
224 |
225 | '@esbuild/linux-arm@0.19.12':
226 | resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
227 | engines: {node: '>=12'}
228 | cpu: [arm]
229 | os: [linux]
230 |
231 | '@esbuild/linux-ia32@0.18.20':
232 | resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
233 | engines: {node: '>=12'}
234 | cpu: [ia32]
235 | os: [linux]
236 |
237 | '@esbuild/linux-ia32@0.19.12':
238 | resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
239 | engines: {node: '>=12'}
240 | cpu: [ia32]
241 | os: [linux]
242 |
243 | '@esbuild/linux-loong64@0.18.20':
244 | resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
245 | engines: {node: '>=12'}
246 | cpu: [loong64]
247 | os: [linux]
248 |
249 | '@esbuild/linux-loong64@0.19.12':
250 | resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
251 | engines: {node: '>=12'}
252 | cpu: [loong64]
253 | os: [linux]
254 |
255 | '@esbuild/linux-mips64el@0.18.20':
256 | resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
257 | engines: {node: '>=12'}
258 | cpu: [mips64el]
259 | os: [linux]
260 |
261 | '@esbuild/linux-mips64el@0.19.12':
262 | resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
263 | engines: {node: '>=12'}
264 | cpu: [mips64el]
265 | os: [linux]
266 |
267 | '@esbuild/linux-ppc64@0.18.20':
268 | resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
269 | engines: {node: '>=12'}
270 | cpu: [ppc64]
271 | os: [linux]
272 |
273 | '@esbuild/linux-ppc64@0.19.12':
274 | resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
275 | engines: {node: '>=12'}
276 | cpu: [ppc64]
277 | os: [linux]
278 |
279 | '@esbuild/linux-riscv64@0.18.20':
280 | resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
281 | engines: {node: '>=12'}
282 | cpu: [riscv64]
283 | os: [linux]
284 |
285 | '@esbuild/linux-riscv64@0.19.12':
286 | resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
287 | engines: {node: '>=12'}
288 | cpu: [riscv64]
289 | os: [linux]
290 |
291 | '@esbuild/linux-s390x@0.18.20':
292 | resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
293 | engines: {node: '>=12'}
294 | cpu: [s390x]
295 | os: [linux]
296 |
297 | '@esbuild/linux-s390x@0.19.12':
298 | resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
299 | engines: {node: '>=12'}
300 | cpu: [s390x]
301 | os: [linux]
302 |
303 | '@esbuild/linux-x64@0.18.20':
304 | resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
305 | engines: {node: '>=12'}
306 | cpu: [x64]
307 | os: [linux]
308 |
309 | '@esbuild/linux-x64@0.19.12':
310 | resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
311 | engines: {node: '>=12'}
312 | cpu: [x64]
313 | os: [linux]
314 |
315 | '@esbuild/netbsd-x64@0.18.20':
316 | resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
317 | engines: {node: '>=12'}
318 | cpu: [x64]
319 | os: [netbsd]
320 |
321 | '@esbuild/netbsd-x64@0.19.12':
322 | resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
323 | engines: {node: '>=12'}
324 | cpu: [x64]
325 | os: [netbsd]
326 |
327 | '@esbuild/openbsd-x64@0.18.20':
328 | resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
329 | engines: {node: '>=12'}
330 | cpu: [x64]
331 | os: [openbsd]
332 |
333 | '@esbuild/openbsd-x64@0.19.12':
334 | resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
335 | engines: {node: '>=12'}
336 | cpu: [x64]
337 | os: [openbsd]
338 |
339 | '@esbuild/sunos-x64@0.18.20':
340 | resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
341 | engines: {node: '>=12'}
342 | cpu: [x64]
343 | os: [sunos]
344 |
345 | '@esbuild/sunos-x64@0.19.12':
346 | resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
347 | engines: {node: '>=12'}
348 | cpu: [x64]
349 | os: [sunos]
350 |
351 | '@esbuild/win32-arm64@0.18.20':
352 | resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
353 | engines: {node: '>=12'}
354 | cpu: [arm64]
355 | os: [win32]
356 |
357 | '@esbuild/win32-arm64@0.19.12':
358 | resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
359 | engines: {node: '>=12'}
360 | cpu: [arm64]
361 | os: [win32]
362 |
363 | '@esbuild/win32-ia32@0.18.20':
364 | resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
365 | engines: {node: '>=12'}
366 | cpu: [ia32]
367 | os: [win32]
368 |
369 | '@esbuild/win32-ia32@0.19.12':
370 | resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
371 | engines: {node: '>=12'}
372 | cpu: [ia32]
373 | os: [win32]
374 |
375 | '@esbuild/win32-x64@0.18.20':
376 | resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
377 | engines: {node: '>=12'}
378 | cpu: [x64]
379 | os: [win32]
380 |
381 | '@esbuild/win32-x64@0.19.12':
382 | resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
383 | engines: {node: '>=12'}
384 | cpu: [x64]
385 | os: [win32]
386 |
387 | '@floating-ui/core@1.6.9':
388 | resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==}
389 |
390 | '@floating-ui/dom@1.6.13':
391 | resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==}
392 |
393 | '@floating-ui/react-dom@2.1.2':
394 | resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==}
395 | peerDependencies:
396 | react: '>=16.8.0'
397 | react-dom: '>=16.8.0'
398 |
399 | '@floating-ui/utils@0.2.9':
400 | resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==}
401 |
402 | '@img/colour@1.0.0':
403 | resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==}
404 | engines: {node: '>=18'}
405 |
406 | '@img/sharp-darwin-arm64@0.34.5':
407 | resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
408 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
409 | cpu: [arm64]
410 | os: [darwin]
411 |
412 | '@img/sharp-darwin-x64@0.34.5':
413 | resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
414 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
415 | cpu: [x64]
416 | os: [darwin]
417 |
418 | '@img/sharp-libvips-darwin-arm64@1.2.4':
419 | resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
420 | cpu: [arm64]
421 | os: [darwin]
422 |
423 | '@img/sharp-libvips-darwin-x64@1.2.4':
424 | resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
425 | cpu: [x64]
426 | os: [darwin]
427 |
428 | '@img/sharp-libvips-linux-arm64@1.2.4':
429 | resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
430 | cpu: [arm64]
431 | os: [linux]
432 |
433 | '@img/sharp-libvips-linux-arm@1.2.4':
434 | resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
435 | cpu: [arm]
436 | os: [linux]
437 |
438 | '@img/sharp-libvips-linux-ppc64@1.2.4':
439 | resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
440 | cpu: [ppc64]
441 | os: [linux]
442 |
443 | '@img/sharp-libvips-linux-riscv64@1.2.4':
444 | resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
445 | cpu: [riscv64]
446 | os: [linux]
447 |
448 | '@img/sharp-libvips-linux-s390x@1.2.4':
449 | resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
450 | cpu: [s390x]
451 | os: [linux]
452 |
453 | '@img/sharp-libvips-linux-x64@1.2.4':
454 | resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
455 | cpu: [x64]
456 | os: [linux]
457 |
458 | '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
459 | resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
460 | cpu: [arm64]
461 | os: [linux]
462 |
463 | '@img/sharp-libvips-linuxmusl-x64@1.2.4':
464 | resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
465 | cpu: [x64]
466 | os: [linux]
467 |
468 | '@img/sharp-linux-arm64@0.34.5':
469 | resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
470 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
471 | cpu: [arm64]
472 | os: [linux]
473 |
474 | '@img/sharp-linux-arm@0.34.5':
475 | resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
476 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
477 | cpu: [arm]
478 | os: [linux]
479 |
480 | '@img/sharp-linux-ppc64@0.34.5':
481 | resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
482 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
483 | cpu: [ppc64]
484 | os: [linux]
485 |
486 | '@img/sharp-linux-riscv64@0.34.5':
487 | resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
488 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
489 | cpu: [riscv64]
490 | os: [linux]
491 |
492 | '@img/sharp-linux-s390x@0.34.5':
493 | resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
494 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
495 | cpu: [s390x]
496 | os: [linux]
497 |
498 | '@img/sharp-linux-x64@0.34.5':
499 | resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
500 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
501 | cpu: [x64]
502 | os: [linux]
503 |
504 | '@img/sharp-linuxmusl-arm64@0.34.5':
505 | resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
506 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
507 | cpu: [arm64]
508 | os: [linux]
509 |
510 | '@img/sharp-linuxmusl-x64@0.34.5':
511 | resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
512 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
513 | cpu: [x64]
514 | os: [linux]
515 |
516 | '@img/sharp-wasm32@0.34.5':
517 | resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
518 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
519 | cpu: [wasm32]
520 |
521 | '@img/sharp-win32-arm64@0.34.5':
522 | resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
523 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
524 | cpu: [arm64]
525 | os: [win32]
526 |
527 | '@img/sharp-win32-ia32@0.34.5':
528 | resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
529 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
530 | cpu: [ia32]
531 | os: [win32]
532 |
533 | '@img/sharp-win32-x64@0.34.5':
534 | resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
535 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
536 | cpu: [x64]
537 | os: [win32]
538 |
539 | '@next/env@15.6.0-canary.59':
540 | resolution: {integrity: sha512-D5msVvBiiJrqAW3MPXP4rR7teMeCcMLCK3yTSCGM4XuuzAcO8Eb4RZtbSPS0zgvFKu9r5hv5eaTXlNmgNmZ5TQ==}
541 |
542 | '@next/swc-darwin-arm64@15.6.0-canary.58':
543 | resolution: {integrity: sha512-jGawwiKITJHOH8dSbTqfjVvCf+DesljZhtvh9LKCrO+1octLlGyDAbEQw5WEzrXCch5LWrdSqPyKft0/9LeniA==}
544 | engines: {node: '>= 10'}
545 | cpu: [arm64]
546 | os: [darwin]
547 |
548 | '@next/swc-darwin-x64@15.6.0-canary.58':
549 | resolution: {integrity: sha512-iD15Eav7Y7lQOCFvsPf9NMwo3dFrHZMX4wghrEysLuwOEJC9zM0jOSb1hdSqZwf1Odn86CIiJUvXH1UcfAm6og==}
550 | engines: {node: '>= 10'}
551 | cpu: [x64]
552 | os: [darwin]
553 |
554 | '@next/swc-linux-arm64-gnu@15.6.0-canary.58':
555 | resolution: {integrity: sha512-b8ayBG/wrycIilOFP/zU6yPQI8UVMtrQfowNaoCvG7FIuu5Fpa7MwPEGWXPyvwn2qQM5fDSsVGQOrjQ6gWLTbA==}
556 | engines: {node: '>= 10'}
557 | cpu: [arm64]
558 | os: [linux]
559 |
560 | '@next/swc-linux-arm64-musl@15.6.0-canary.58':
561 | resolution: {integrity: sha512-KMYPUdBTAITdgxRNjMKdG85bHsn3wu0KWPV2nftoov2/dVs5eFJ47w+m4upPdTgBXRAHY50OvS/nzf5mN/TXeQ==}
562 | engines: {node: '>= 10'}
563 | cpu: [arm64]
564 | os: [linux]
565 |
566 | '@next/swc-linux-x64-gnu@15.6.0-canary.58':
567 | resolution: {integrity: sha512-myknT/if7wuwss0B/1Le7ymlN0Zr/DsfGji8b+XcqeFhoy1GxQerfTlrsblZTB6EIPIex1QPRUbpIcy+N9Qfpw==}
568 | engines: {node: '>= 10'}
569 | cpu: [x64]
570 | os: [linux]
571 |
572 | '@next/swc-linux-x64-musl@15.6.0-canary.58':
573 | resolution: {integrity: sha512-3A1YLtmuot0pnZqDHV2iAfUrvQS0zp7xXUlqNb8flAJAu1Civ+2qt94l0kTfUjWHtFFUENyt2yEcXEqxuxEJfg==}
574 | engines: {node: '>= 10'}
575 | cpu: [x64]
576 | os: [linux]
577 |
578 | '@next/swc-win32-arm64-msvc@15.6.0-canary.58':
579 | resolution: {integrity: sha512-3hkMBi/Zbatqi9vwnh1zuOWQerS4CtUptn9cj4NRtVAJurzhfQBwz8RJIq/5f85XDkq0LxDrhyABZ+6RU7Un7Q==}
580 | engines: {node: '>= 10'}
581 | cpu: [arm64]
582 | os: [win32]
583 |
584 | '@next/swc-win32-x64-msvc@15.6.0-canary.58':
585 | resolution: {integrity: sha512-CFB6BzqgYJ7yJvoji0KD5nWf88JN5/iliiLn/kfzxUMvfaKmoYLrGZwRuePrAwLdBpczEsgcmuER6YuT9/pZLw==}
586 | engines: {node: '>= 10'}
587 | cpu: [x64]
588 | os: [win32]
589 |
590 | '@radix-ui/primitive@1.1.1':
591 | resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==}
592 |
593 | '@radix-ui/react-arrow@1.1.2':
594 | resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==}
595 | peerDependencies:
596 | '@types/react': '*'
597 | '@types/react-dom': '*'
598 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
599 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
600 | peerDependenciesMeta:
601 | '@types/react':
602 | optional: true
603 | '@types/react-dom':
604 | optional: true
605 |
606 | '@radix-ui/react-compose-refs@1.1.1':
607 | resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
608 | peerDependencies:
609 | '@types/react': '*'
610 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
611 | peerDependenciesMeta:
612 | '@types/react':
613 | optional: true
614 |
615 | '@radix-ui/react-context@1.1.1':
616 | resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==}
617 | peerDependencies:
618 | '@types/react': '*'
619 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
620 | peerDependenciesMeta:
621 | '@types/react':
622 | optional: true
623 |
624 | '@radix-ui/react-dialog@1.1.6':
625 | resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==}
626 | peerDependencies:
627 | '@types/react': '*'
628 | '@types/react-dom': '*'
629 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
630 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
631 | peerDependenciesMeta:
632 | '@types/react':
633 | optional: true
634 | '@types/react-dom':
635 | optional: true
636 |
637 | '@radix-ui/react-dismissable-layer@1.1.5':
638 | resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==}
639 | peerDependencies:
640 | '@types/react': '*'
641 | '@types/react-dom': '*'
642 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
643 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
644 | peerDependenciesMeta:
645 | '@types/react':
646 | optional: true
647 | '@types/react-dom':
648 | optional: true
649 |
650 | '@radix-ui/react-focus-guards@1.1.1':
651 | resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==}
652 | peerDependencies:
653 | '@types/react': '*'
654 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
655 | peerDependenciesMeta:
656 | '@types/react':
657 | optional: true
658 |
659 | '@radix-ui/react-focus-scope@1.1.2':
660 | resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==}
661 | peerDependencies:
662 | '@types/react': '*'
663 | '@types/react-dom': '*'
664 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
665 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
666 | peerDependenciesMeta:
667 | '@types/react':
668 | optional: true
669 | '@types/react-dom':
670 | optional: true
671 |
672 | '@radix-ui/react-icons@1.3.2':
673 | resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==}
674 | peerDependencies:
675 | react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc
676 |
677 | '@radix-ui/react-id@1.1.0':
678 | resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
679 | peerDependencies:
680 | '@types/react': '*'
681 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
682 | peerDependenciesMeta:
683 | '@types/react':
684 | optional: true
685 |
686 | '@radix-ui/react-popper@1.2.2':
687 | resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==}
688 | peerDependencies:
689 | '@types/react': '*'
690 | '@types/react-dom': '*'
691 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
692 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
693 | peerDependenciesMeta:
694 | '@types/react':
695 | optional: true
696 | '@types/react-dom':
697 | optional: true
698 |
699 | '@radix-ui/react-portal@1.1.4':
700 | resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==}
701 | peerDependencies:
702 | '@types/react': '*'
703 | '@types/react-dom': '*'
704 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
705 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
706 | peerDependenciesMeta:
707 | '@types/react':
708 | optional: true
709 | '@types/react-dom':
710 | optional: true
711 |
712 | '@radix-ui/react-presence@1.1.2':
713 | resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==}
714 | peerDependencies:
715 | '@types/react': '*'
716 | '@types/react-dom': '*'
717 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
718 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
719 | peerDependenciesMeta:
720 | '@types/react':
721 | optional: true
722 | '@types/react-dom':
723 | optional: true
724 |
725 | '@radix-ui/react-primitive@2.0.2':
726 | resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==}
727 | peerDependencies:
728 | '@types/react': '*'
729 | '@types/react-dom': '*'
730 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
731 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
732 | peerDependenciesMeta:
733 | '@types/react':
734 | optional: true
735 | '@types/react-dom':
736 | optional: true
737 |
738 | '@radix-ui/react-slot@1.1.2':
739 | resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==}
740 | peerDependencies:
741 | '@types/react': '*'
742 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
743 | peerDependenciesMeta:
744 | '@types/react':
745 | optional: true
746 |
747 | '@radix-ui/react-tooltip@1.1.8':
748 | resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==}
749 | peerDependencies:
750 | '@types/react': '*'
751 | '@types/react-dom': '*'
752 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
753 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
754 | peerDependenciesMeta:
755 | '@types/react':
756 | optional: true
757 | '@types/react-dom':
758 | optional: true
759 |
760 | '@radix-ui/react-use-callback-ref@1.1.0':
761 | resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==}
762 | peerDependencies:
763 | '@types/react': '*'
764 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
765 | peerDependenciesMeta:
766 | '@types/react':
767 | optional: true
768 |
769 | '@radix-ui/react-use-controllable-state@1.1.0':
770 | resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==}
771 | peerDependencies:
772 | '@types/react': '*'
773 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
774 | peerDependenciesMeta:
775 | '@types/react':
776 | optional: true
777 |
778 | '@radix-ui/react-use-escape-keydown@1.1.0':
779 | resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==}
780 | peerDependencies:
781 | '@types/react': '*'
782 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
783 | peerDependenciesMeta:
784 | '@types/react':
785 | optional: true
786 |
787 | '@radix-ui/react-use-layout-effect@1.1.0':
788 | resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==}
789 | peerDependencies:
790 | '@types/react': '*'
791 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
792 | peerDependenciesMeta:
793 | '@types/react':
794 | optional: true
795 |
796 | '@radix-ui/react-use-rect@1.1.0':
797 | resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
798 | peerDependencies:
799 | '@types/react': '*'
800 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
801 | peerDependenciesMeta:
802 | '@types/react':
803 | optional: true
804 |
805 | '@radix-ui/react-use-size@1.1.0':
806 | resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==}
807 | peerDependencies:
808 | '@types/react': '*'
809 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
810 | peerDependenciesMeta:
811 | '@types/react':
812 | optional: true
813 |
814 | '@radix-ui/react-visually-hidden@1.1.2':
815 | resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==}
816 | peerDependencies:
817 | '@types/react': '*'
818 | '@types/react-dom': '*'
819 | react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
820 | react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
821 | peerDependenciesMeta:
822 | '@types/react':
823 | optional: true
824 | '@types/react-dom':
825 | optional: true
826 |
827 | '@radix-ui/rect@1.1.0':
828 | resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==}
829 |
830 | '@swc/helpers@0.5.15':
831 | resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
832 |
833 | '@tailwindcss/node@4.0.13':
834 | resolution: {integrity: sha512-P9TmtE9Vew0vv5FwyD4bsg/dHHsIsAuUXkenuGUc5gm8fYgaxpdoxIKngCyEMEQxyCKR8PQY5V5VrrKNOx7exg==}
835 |
836 | '@tailwindcss/oxide-android-arm64@4.0.13':
837 | resolution: {integrity: sha512-+9zmwaPQ8A9ycDcdb+hRkMn6NzsmZ4YJBsW5Xqq5EdOu9xlIgmuMuJauVzDPB5BSbIWfhPdZ+le8NeRZpl1coA==}
838 | engines: {node: '>= 10'}
839 | cpu: [arm64]
840 | os: [android]
841 |
842 | '@tailwindcss/oxide-darwin-arm64@4.0.13':
843 | resolution: {integrity: sha512-Bj1QGlEJSjs/205CIRfb5/jeveOqzJ4pFMdRxu0gyiYWxBRyxsExXqaD+7162wnLP/EDKh6S1MC9E/1GwEhLtA==}
844 | engines: {node: '>= 10'}
845 | cpu: [arm64]
846 | os: [darwin]
847 |
848 | '@tailwindcss/oxide-darwin-x64@4.0.13':
849 | resolution: {integrity: sha512-lRTkxjTpMGXhLLM5GjZ0MtjPczMuhAo9j7PeSsaU6Imkm7W7RbrXfT8aP934kS7cBBV+HKN5U19Z0WWaORfb8Q==}
850 | engines: {node: '>= 10'}
851 | cpu: [x64]
852 | os: [darwin]
853 |
854 | '@tailwindcss/oxide-freebsd-x64@4.0.13':
855 | resolution: {integrity: sha512-p/YLyKhs+xFibVeAPlpMGDVMKgjChgzs12VnDFaaqRSJoOz+uJgRSKiir2tn50e7Nm4YYw35q/DRBwpDBNo1MQ==}
856 | engines: {node: '>= 10'}
857 | cpu: [x64]
858 | os: [freebsd]
859 |
860 | '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.13':
861 | resolution: {integrity: sha512-Ua/5ydE/QOTX8jHuc7M9ICWnaLi6K2MV/r+Ws2OppsOjy8tdlPbqYainJJ6Kl7ofm524K+4Fk9CQITPzeIESPw==}
862 | engines: {node: '>= 10'}
863 | cpu: [arm]
864 | os: [linux]
865 |
866 | '@tailwindcss/oxide-linux-arm64-gnu@4.0.13':
867 | resolution: {integrity: sha512-/W1+Q6tBAVgZWh/bhfOHo4n7Ryh6E7zYj4bJd9SRbkPyLtRioyK3bi6RLuDj57sa7Amk/DeomSV9iycS0xqIPA==}
868 | engines: {node: '>= 10'}
869 | cpu: [arm64]
870 | os: [linux]
871 |
872 | '@tailwindcss/oxide-linux-arm64-musl@4.0.13':
873 | resolution: {integrity: sha512-GQj6TWevNxwsYw20FdT2r2d1f7uiRsF07iFvNYxPIvIyPEV74eZ0zgFEsAH1daK1OxPy+LXdZ4grV17P5tVzhQ==}
874 | engines: {node: '>= 10'}
875 | cpu: [arm64]
876 | os: [linux]
877 |
878 | '@tailwindcss/oxide-linux-x64-gnu@4.0.13':
879 | resolution: {integrity: sha512-sQRH09faifF9w9WS6TKDWr1oLi4hoPx0EIWXZHQK/jcjarDpXGQ2DbF0KnALJCwWBxOIP/1nrmU01fZwwMzY3g==}
880 | engines: {node: '>= 10'}
881 | cpu: [x64]
882 | os: [linux]
883 |
884 | '@tailwindcss/oxide-linux-x64-musl@4.0.13':
885 | resolution: {integrity: sha512-Or1N8DIF3tP+LsloJp+UXLTIMMHMUcWXFhJLCsM4T7MzFzxkeReewRWXfk5mk137cdqVeUEH/R50xAhY1mOkTQ==}
886 | engines: {node: '>= 10'}
887 | cpu: [x64]
888 | os: [linux]
889 |
890 | '@tailwindcss/oxide-win32-arm64-msvc@4.0.13':
891 | resolution: {integrity: sha512-u2mQyqCFrr9vVTP6sfDRfGE6bhOX3/7rInehzxNhHX1HYRIx09H3sDdXzTxnZWKOjIg3qjFTCrYFUZckva5PIg==}
892 | engines: {node: '>= 10'}
893 | cpu: [arm64]
894 | os: [win32]
895 |
896 | '@tailwindcss/oxide-win32-x64-msvc@4.0.13':
897 | resolution: {integrity: sha512-sOEc4iCanp1Yqyeu9suQcEzfaUcHnqjBUgDg0ZXpjUMUwdSi37S1lu1RGoV1BYInvvGu3y3HHTmvsSfDhx2L8w==}
898 | engines: {node: '>= 10'}
899 | cpu: [x64]
900 | os: [win32]
901 |
902 | '@tailwindcss/oxide@4.0.13':
903 | resolution: {integrity: sha512-pTH3Ex5zAWC9LbS+WsYAFmkXQW3NRjmvxkKJY3NP1x0KHBWjz0Q2uGtdGMJzsa0EwoZ7wq9RTbMH1UNPceCpWw==}
904 | engines: {node: '>= 10'}
905 |
906 | '@tailwindcss/postcss@4.0.13':
907 | resolution: {integrity: sha512-zTmnPGDYb2HKClTBTBwB+lLQH+Rq4etnQXFXs2lisRyXryUnoJIBByFTljkaK9F1d7o14h6t4NJIlfbZuOHR+A==}
908 |
909 | '@types/node@22.10.1':
910 | resolution: {integrity: sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==}
911 |
912 | '@types/react-dom@19.0.4':
913 | resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==}
914 | peerDependencies:
915 | '@types/react': ^19.0.0
916 |
917 | '@types/react@19.0.10':
918 | resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==}
919 |
920 | aria-hidden@1.2.4:
921 | resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==}
922 | engines: {node: '>=10'}
923 |
924 | buffer-from@1.1.2:
925 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
926 |
927 | caniuse-lite@1.0.30001703:
928 | resolution: {integrity: sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==}
929 |
930 | class-variance-authority@0.7.1:
931 | resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==}
932 |
933 | client-only@0.0.1:
934 | resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
935 |
936 | clsx@2.1.1:
937 | resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
938 | engines: {node: '>=6'}
939 |
940 | csstype@3.1.3:
941 | resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
942 |
943 | debug@4.3.7:
944 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
945 | engines: {node: '>=6.0'}
946 | peerDependencies:
947 | supports-color: '*'
948 | peerDependenciesMeta:
949 | supports-color:
950 | optional: true
951 |
952 | detect-libc@2.0.3:
953 | resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
954 | engines: {node: '>=8'}
955 |
956 | detect-libc@2.1.2:
957 | resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
958 | engines: {node: '>=8'}
959 |
960 | detect-node-es@1.1.0:
961 | resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
962 |
963 | dotenv@16.4.5:
964 | resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
965 | engines: {node: '>=12'}
966 |
967 | drizzle-kit@0.28.1:
968 | resolution: {integrity: sha512-JimOV+ystXTWMgZkLHYHf2w3oS28hxiH1FR0dkmJLc7GHzdGJoJAQtQS5DRppnabsRZwE2U1F6CuezVBgmsBBQ==}
969 | hasBin: true
970 |
971 | drizzle-orm@0.36.4:
972 | resolution: {integrity: sha512-1OZY3PXD7BR00Gl61UUOFihslDldfH4NFRH2MbP54Yxi0G/PKn4HfO65JYZ7c16DeP3SpM3Aw+VXVG9j6CRSXA==}
973 | peerDependencies:
974 | '@aws-sdk/client-rds-data': '>=3'
975 | '@cloudflare/workers-types': '>=3'
976 | '@electric-sql/pglite': '>=0.2.0'
977 | '@libsql/client': '>=0.10.0'
978 | '@libsql/client-wasm': '>=0.10.0'
979 | '@neondatabase/serverless': '>=0.10.0'
980 | '@op-engineering/op-sqlite': '>=2'
981 | '@opentelemetry/api': ^1.4.1
982 | '@planetscale/database': '>=1'
983 | '@prisma/client': '*'
984 | '@tidbcloud/serverless': '*'
985 | '@types/better-sqlite3': '*'
986 | '@types/pg': '*'
987 | '@types/react': '>=18'
988 | '@types/sql.js': '*'
989 | '@vercel/postgres': '>=0.8.0'
990 | '@xata.io/client': '*'
991 | better-sqlite3: '>=7'
992 | bun-types: '*'
993 | expo-sqlite: '>=14.0.0'
994 | knex: '*'
995 | kysely: '*'
996 | mysql2: '>=2'
997 | pg: '>=8'
998 | postgres: '>=3'
999 | prisma: '*'
1000 | react: '>=18'
1001 | sql.js: '>=1'
1002 | sqlite3: '>=5'
1003 | peerDependenciesMeta:
1004 | '@aws-sdk/client-rds-data':
1005 | optional: true
1006 | '@cloudflare/workers-types':
1007 | optional: true
1008 | '@electric-sql/pglite':
1009 | optional: true
1010 | '@libsql/client':
1011 | optional: true
1012 | '@libsql/client-wasm':
1013 | optional: true
1014 | '@neondatabase/serverless':
1015 | optional: true
1016 | '@op-engineering/op-sqlite':
1017 | optional: true
1018 | '@opentelemetry/api':
1019 | optional: true
1020 | '@planetscale/database':
1021 | optional: true
1022 | '@prisma/client':
1023 | optional: true
1024 | '@tidbcloud/serverless':
1025 | optional: true
1026 | '@types/better-sqlite3':
1027 | optional: true
1028 | '@types/pg':
1029 | optional: true
1030 | '@types/react':
1031 | optional: true
1032 | '@types/sql.js':
1033 | optional: true
1034 | '@vercel/postgres':
1035 | optional: true
1036 | '@xata.io/client':
1037 | optional: true
1038 | better-sqlite3:
1039 | optional: true
1040 | bun-types:
1041 | optional: true
1042 | expo-sqlite:
1043 | optional: true
1044 | knex:
1045 | optional: true
1046 | kysely:
1047 | optional: true
1048 | mysql2:
1049 | optional: true
1050 | pg:
1051 | optional: true
1052 | postgres:
1053 | optional: true
1054 | prisma:
1055 | optional: true
1056 | react:
1057 | optional: true
1058 | sql.js:
1059 | optional: true
1060 | sqlite3:
1061 | optional: true
1062 |
1063 | enhanced-resolve@5.18.1:
1064 | resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==}
1065 | engines: {node: '>=10.13.0'}
1066 |
1067 | esbuild-register@3.6.0:
1068 | resolution: {integrity: sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==}
1069 | peerDependencies:
1070 | esbuild: '>=0.12 <1'
1071 |
1072 | esbuild@0.18.20:
1073 | resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
1074 | engines: {node: '>=12'}
1075 | hasBin: true
1076 |
1077 | esbuild@0.19.12:
1078 | resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
1079 | engines: {node: '>=12'}
1080 | hasBin: true
1081 |
1082 | geist@1.3.1:
1083 | resolution: {integrity: sha512-Q4gC1pBVPN+D579pBaz0TRRnGA4p9UK6elDY/xizXdFk/g4EKR5g0I+4p/Kj6gM0SajDBZ/0FvDV9ey9ud7BWw==}
1084 | peerDependencies:
1085 | next: '>=13.2.0'
1086 |
1087 | get-nonce@1.0.1:
1088 | resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
1089 | engines: {node: '>=6'}
1090 |
1091 | get-tsconfig@4.8.1:
1092 | resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==}
1093 |
1094 | graceful-fs@4.2.11:
1095 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
1096 |
1097 | jiti@2.4.2:
1098 | resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==}
1099 | hasBin: true
1100 |
1101 | lightningcss-darwin-arm64@1.29.2:
1102 | resolution: {integrity: sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==}
1103 | engines: {node: '>= 12.0.0'}
1104 | cpu: [arm64]
1105 | os: [darwin]
1106 |
1107 | lightningcss-darwin-x64@1.29.2:
1108 | resolution: {integrity: sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==}
1109 | engines: {node: '>= 12.0.0'}
1110 | cpu: [x64]
1111 | os: [darwin]
1112 |
1113 | lightningcss-freebsd-x64@1.29.2:
1114 | resolution: {integrity: sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==}
1115 | engines: {node: '>= 12.0.0'}
1116 | cpu: [x64]
1117 | os: [freebsd]
1118 |
1119 | lightningcss-linux-arm-gnueabihf@1.29.2:
1120 | resolution: {integrity: sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==}
1121 | engines: {node: '>= 12.0.0'}
1122 | cpu: [arm]
1123 | os: [linux]
1124 |
1125 | lightningcss-linux-arm64-gnu@1.29.2:
1126 | resolution: {integrity: sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==}
1127 | engines: {node: '>= 12.0.0'}
1128 | cpu: [arm64]
1129 | os: [linux]
1130 |
1131 | lightningcss-linux-arm64-musl@1.29.2:
1132 | resolution: {integrity: sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==}
1133 | engines: {node: '>= 12.0.0'}
1134 | cpu: [arm64]
1135 | os: [linux]
1136 |
1137 | lightningcss-linux-x64-gnu@1.29.2:
1138 | resolution: {integrity: sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==}
1139 | engines: {node: '>= 12.0.0'}
1140 | cpu: [x64]
1141 | os: [linux]
1142 |
1143 | lightningcss-linux-x64-musl@1.29.2:
1144 | resolution: {integrity: sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==}
1145 | engines: {node: '>= 12.0.0'}
1146 | cpu: [x64]
1147 | os: [linux]
1148 |
1149 | lightningcss-win32-arm64-msvc@1.29.2:
1150 | resolution: {integrity: sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==}
1151 | engines: {node: '>= 12.0.0'}
1152 | cpu: [arm64]
1153 | os: [win32]
1154 |
1155 | lightningcss-win32-x64-msvc@1.29.2:
1156 | resolution: {integrity: sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==}
1157 | engines: {node: '>= 12.0.0'}
1158 | cpu: [x64]
1159 | os: [win32]
1160 |
1161 | lightningcss@1.29.2:
1162 | resolution: {integrity: sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==}
1163 | engines: {node: '>= 12.0.0'}
1164 |
1165 | lucide-react@0.462.0:
1166 | resolution: {integrity: sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g==}
1167 | peerDependencies:
1168 | react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc
1169 |
1170 | ms@2.1.3:
1171 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
1172 |
1173 | nanoid@3.3.8:
1174 | resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
1175 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1176 | hasBin: true
1177 |
1178 | nanoid@3.3.9:
1179 | resolution: {integrity: sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==}
1180 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
1181 | hasBin: true
1182 |
1183 | next@15.6.0-canary.59:
1184 | resolution: {integrity: sha512-FOKHnHEVqtbMj2eQfgRgEkzEds/hqjz5JRBjLhT7UgPtvs36Vzc7RU9mh8lOZt/TONS7jVs+5ilv194QA/wdnQ==}
1185 | engines: {node: '>=20.9.0'}
1186 | hasBin: true
1187 | peerDependencies:
1188 | '@opentelemetry/api': ^1.1.0
1189 | '@playwright/test': ^1.51.1
1190 | babel-plugin-react-compiler: '*'
1191 | react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
1192 | react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
1193 | sass: ^1.3.0
1194 | peerDependenciesMeta:
1195 | '@opentelemetry/api':
1196 | optional: true
1197 | '@playwright/test':
1198 | optional: true
1199 | babel-plugin-react-compiler:
1200 | optional: true
1201 | sass:
1202 | optional: true
1203 |
1204 | picocolors@1.1.1:
1205 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
1206 |
1207 | postcss@8.4.31:
1208 | resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
1209 | engines: {node: ^10 || ^12 || >=14}
1210 |
1211 | postcss@8.4.49:
1212 | resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
1213 | engines: {node: ^10 || ^12 || >=14}
1214 |
1215 | postgres@3.4.5:
1216 | resolution: {integrity: sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==}
1217 | engines: {node: '>=12'}
1218 |
1219 | prettier-plugin-organize-imports@4.1.0:
1220 | resolution: {integrity: sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==}
1221 | peerDependencies:
1222 | prettier: '>=2.0'
1223 | typescript: '>=2.9'
1224 | vue-tsc: ^2.1.0
1225 | peerDependenciesMeta:
1226 | vue-tsc:
1227 | optional: true
1228 |
1229 | prettier-plugin-tailwindcss@0.6.11:
1230 | resolution: {integrity: sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==}
1231 | engines: {node: '>=14.21.3'}
1232 | peerDependencies:
1233 | '@ianvs/prettier-plugin-sort-imports': '*'
1234 | '@prettier/plugin-pug': '*'
1235 | '@shopify/prettier-plugin-liquid': '*'
1236 | '@trivago/prettier-plugin-sort-imports': '*'
1237 | '@zackad/prettier-plugin-twig': '*'
1238 | prettier: ^3.0
1239 | prettier-plugin-astro: '*'
1240 | prettier-plugin-css-order: '*'
1241 | prettier-plugin-import-sort: '*'
1242 | prettier-plugin-jsdoc: '*'
1243 | prettier-plugin-marko: '*'
1244 | prettier-plugin-multiline-arrays: '*'
1245 | prettier-plugin-organize-attributes: '*'
1246 | prettier-plugin-organize-imports: '*'
1247 | prettier-plugin-sort-imports: '*'
1248 | prettier-plugin-style-order: '*'
1249 | prettier-plugin-svelte: '*'
1250 | peerDependenciesMeta:
1251 | '@ianvs/prettier-plugin-sort-imports':
1252 | optional: true
1253 | '@prettier/plugin-pug':
1254 | optional: true
1255 | '@shopify/prettier-plugin-liquid':
1256 | optional: true
1257 | '@trivago/prettier-plugin-sort-imports':
1258 | optional: true
1259 | '@zackad/prettier-plugin-twig':
1260 | optional: true
1261 | prettier-plugin-astro:
1262 | optional: true
1263 | prettier-plugin-css-order:
1264 | optional: true
1265 | prettier-plugin-import-sort:
1266 | optional: true
1267 | prettier-plugin-jsdoc:
1268 | optional: true
1269 | prettier-plugin-marko:
1270 | optional: true
1271 | prettier-plugin-multiline-arrays:
1272 | optional: true
1273 | prettier-plugin-organize-attributes:
1274 | optional: true
1275 | prettier-plugin-organize-imports:
1276 | optional: true
1277 | prettier-plugin-sort-imports:
1278 | optional: true
1279 | prettier-plugin-style-order:
1280 | optional: true
1281 | prettier-plugin-svelte:
1282 | optional: true
1283 |
1284 | prettier@3.5.3:
1285 | resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==}
1286 | engines: {node: '>=14'}
1287 | hasBin: true
1288 |
1289 | react-dom@19.0.0:
1290 | resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==}
1291 | peerDependencies:
1292 | react: ^19.0.0
1293 |
1294 | react-remove-scroll-bar@2.3.8:
1295 | resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
1296 | engines: {node: '>=10'}
1297 | peerDependencies:
1298 | '@types/react': '*'
1299 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
1300 | peerDependenciesMeta:
1301 | '@types/react':
1302 | optional: true
1303 |
1304 | react-remove-scroll@2.6.3:
1305 | resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==}
1306 | engines: {node: '>=10'}
1307 | peerDependencies:
1308 | '@types/react': '*'
1309 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1310 | peerDependenciesMeta:
1311 | '@types/react':
1312 | optional: true
1313 |
1314 | react-style-singleton@2.2.3:
1315 | resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
1316 | engines: {node: '>=10'}
1317 | peerDependencies:
1318 | '@types/react': '*'
1319 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1320 | peerDependenciesMeta:
1321 | '@types/react':
1322 | optional: true
1323 |
1324 | react@19.0.0:
1325 | resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==}
1326 | engines: {node: '>=0.10.0'}
1327 |
1328 | resolve-pkg-maps@1.0.0:
1329 | resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
1330 |
1331 | scheduler@0.25.0:
1332 | resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==}
1333 |
1334 | semver@7.7.3:
1335 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
1336 | engines: {node: '>=10'}
1337 | hasBin: true
1338 |
1339 | sharp@0.34.5:
1340 | resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
1341 | engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
1342 |
1343 | sonner@1.7.0:
1344 | resolution: {integrity: sha512-W6dH7m5MujEPyug3lpI2l3TC3Pp1+LTgK0Efg+IHDrBbtEjyCmCHHo6yfNBOsf1tFZ6zf+jceWwB38baC8yO9g==}
1345 | peerDependencies:
1346 | react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1347 | react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1348 |
1349 | source-map-js@1.2.1:
1350 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
1351 | engines: {node: '>=0.10.0'}
1352 |
1353 | source-map-support@0.5.21:
1354 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
1355 |
1356 | source-map@0.6.1:
1357 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
1358 | engines: {node: '>=0.10.0'}
1359 |
1360 | styled-jsx@5.1.6:
1361 | resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
1362 | engines: {node: '>= 12.0.0'}
1363 | peerDependencies:
1364 | '@babel/core': '*'
1365 | babel-plugin-macros: '*'
1366 | react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
1367 | peerDependenciesMeta:
1368 | '@babel/core':
1369 | optional: true
1370 | babel-plugin-macros:
1371 | optional: true
1372 |
1373 | tailwind-merge@3.0.2:
1374 | resolution: {integrity: sha512-l7z+OYZ7mu3DTqrL88RiKrKIqO3NcpEO8V/Od04bNpvk0kiIFndGEoqfuzvj4yuhRkHKjRkII2z+KS2HfPcSxw==}
1375 |
1376 | tailwindcss-animate@1.0.7:
1377 | resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
1378 | peerDependencies:
1379 | tailwindcss: '>=3.0.0 || insiders'
1380 |
1381 | tailwindcss@4.0.13:
1382 | resolution: {integrity: sha512-gbvFrB0fOsTv/OugXWi2PtflJ4S6/ctu6Mmn3bCftmLY/6xRsQVEJPgIIpABwpZ52DpONkCA3bEj5b54MHxF2Q==}
1383 |
1384 | tapable@2.2.1:
1385 | resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
1386 | engines: {node: '>=6'}
1387 |
1388 | tslib@2.8.1:
1389 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
1390 |
1391 | typescript@5.8.2:
1392 | resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==}
1393 | engines: {node: '>=14.17'}
1394 | hasBin: true
1395 |
1396 | undici-types@6.20.0:
1397 | resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==}
1398 |
1399 | use-callback-ref@1.3.3:
1400 | resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
1401 | engines: {node: '>=10'}
1402 | peerDependencies:
1403 | '@types/react': '*'
1404 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1405 | peerDependenciesMeta:
1406 | '@types/react':
1407 | optional: true
1408 |
1409 | use-sidecar@1.1.3:
1410 | resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==}
1411 | engines: {node: '>=10'}
1412 | peerDependencies:
1413 | '@types/react': '*'
1414 | react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
1415 | peerDependenciesMeta:
1416 | '@types/react':
1417 | optional: true
1418 |
1419 | zod@3.23.8:
1420 | resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==}
1421 |
1422 | snapshots:
1423 |
1424 | '@alloc/quick-lru@5.2.0': {}
1425 |
1426 | '@drizzle-team/brocli@0.10.2': {}
1427 |
1428 | '@emnapi/runtime@1.7.1':
1429 | dependencies:
1430 | tslib: 2.8.1
1431 | optional: true
1432 |
1433 | '@esbuild-kit/core-utils@3.3.2':
1434 | dependencies:
1435 | esbuild: 0.18.20
1436 | source-map-support: 0.5.21
1437 |
1438 | '@esbuild-kit/esm-loader@2.6.5':
1439 | dependencies:
1440 | '@esbuild-kit/core-utils': 3.3.2
1441 | get-tsconfig: 4.8.1
1442 |
1443 | '@esbuild/aix-ppc64@0.19.12':
1444 | optional: true
1445 |
1446 | '@esbuild/android-arm64@0.18.20':
1447 | optional: true
1448 |
1449 | '@esbuild/android-arm64@0.19.12':
1450 | optional: true
1451 |
1452 | '@esbuild/android-arm@0.18.20':
1453 | optional: true
1454 |
1455 | '@esbuild/android-arm@0.19.12':
1456 | optional: true
1457 |
1458 | '@esbuild/android-x64@0.18.20':
1459 | optional: true
1460 |
1461 | '@esbuild/android-x64@0.19.12':
1462 | optional: true
1463 |
1464 | '@esbuild/darwin-arm64@0.18.20':
1465 | optional: true
1466 |
1467 | '@esbuild/darwin-arm64@0.19.12':
1468 | optional: true
1469 |
1470 | '@esbuild/darwin-x64@0.18.20':
1471 | optional: true
1472 |
1473 | '@esbuild/darwin-x64@0.19.12':
1474 | optional: true
1475 |
1476 | '@esbuild/freebsd-arm64@0.18.20':
1477 | optional: true
1478 |
1479 | '@esbuild/freebsd-arm64@0.19.12':
1480 | optional: true
1481 |
1482 | '@esbuild/freebsd-x64@0.18.20':
1483 | optional: true
1484 |
1485 | '@esbuild/freebsd-x64@0.19.12':
1486 | optional: true
1487 |
1488 | '@esbuild/linux-arm64@0.18.20':
1489 | optional: true
1490 |
1491 | '@esbuild/linux-arm64@0.19.12':
1492 | optional: true
1493 |
1494 | '@esbuild/linux-arm@0.18.20':
1495 | optional: true
1496 |
1497 | '@esbuild/linux-arm@0.19.12':
1498 | optional: true
1499 |
1500 | '@esbuild/linux-ia32@0.18.20':
1501 | optional: true
1502 |
1503 | '@esbuild/linux-ia32@0.19.12':
1504 | optional: true
1505 |
1506 | '@esbuild/linux-loong64@0.18.20':
1507 | optional: true
1508 |
1509 | '@esbuild/linux-loong64@0.19.12':
1510 | optional: true
1511 |
1512 | '@esbuild/linux-mips64el@0.18.20':
1513 | optional: true
1514 |
1515 | '@esbuild/linux-mips64el@0.19.12':
1516 | optional: true
1517 |
1518 | '@esbuild/linux-ppc64@0.18.20':
1519 | optional: true
1520 |
1521 | '@esbuild/linux-ppc64@0.19.12':
1522 | optional: true
1523 |
1524 | '@esbuild/linux-riscv64@0.18.20':
1525 | optional: true
1526 |
1527 | '@esbuild/linux-riscv64@0.19.12':
1528 | optional: true
1529 |
1530 | '@esbuild/linux-s390x@0.18.20':
1531 | optional: true
1532 |
1533 | '@esbuild/linux-s390x@0.19.12':
1534 | optional: true
1535 |
1536 | '@esbuild/linux-x64@0.18.20':
1537 | optional: true
1538 |
1539 | '@esbuild/linux-x64@0.19.12':
1540 | optional: true
1541 |
1542 | '@esbuild/netbsd-x64@0.18.20':
1543 | optional: true
1544 |
1545 | '@esbuild/netbsd-x64@0.19.12':
1546 | optional: true
1547 |
1548 | '@esbuild/openbsd-x64@0.18.20':
1549 | optional: true
1550 |
1551 | '@esbuild/openbsd-x64@0.19.12':
1552 | optional: true
1553 |
1554 | '@esbuild/sunos-x64@0.18.20':
1555 | optional: true
1556 |
1557 | '@esbuild/sunos-x64@0.19.12':
1558 | optional: true
1559 |
1560 | '@esbuild/win32-arm64@0.18.20':
1561 | optional: true
1562 |
1563 | '@esbuild/win32-arm64@0.19.12':
1564 | optional: true
1565 |
1566 | '@esbuild/win32-ia32@0.18.20':
1567 | optional: true
1568 |
1569 | '@esbuild/win32-ia32@0.19.12':
1570 | optional: true
1571 |
1572 | '@esbuild/win32-x64@0.18.20':
1573 | optional: true
1574 |
1575 | '@esbuild/win32-x64@0.19.12':
1576 | optional: true
1577 |
1578 | '@floating-ui/core@1.6.9':
1579 | dependencies:
1580 | '@floating-ui/utils': 0.2.9
1581 |
1582 | '@floating-ui/dom@1.6.13':
1583 | dependencies:
1584 | '@floating-ui/core': 1.6.9
1585 | '@floating-ui/utils': 0.2.9
1586 |
1587 | '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1588 | dependencies:
1589 | '@floating-ui/dom': 1.6.13
1590 | react: 19.0.0
1591 | react-dom: 19.0.0(react@19.0.0)
1592 |
1593 | '@floating-ui/utils@0.2.9': {}
1594 |
1595 | '@img/colour@1.0.0':
1596 | optional: true
1597 |
1598 | '@img/sharp-darwin-arm64@0.34.5':
1599 | optionalDependencies:
1600 | '@img/sharp-libvips-darwin-arm64': 1.2.4
1601 | optional: true
1602 |
1603 | '@img/sharp-darwin-x64@0.34.5':
1604 | optionalDependencies:
1605 | '@img/sharp-libvips-darwin-x64': 1.2.4
1606 | optional: true
1607 |
1608 | '@img/sharp-libvips-darwin-arm64@1.2.4':
1609 | optional: true
1610 |
1611 | '@img/sharp-libvips-darwin-x64@1.2.4':
1612 | optional: true
1613 |
1614 | '@img/sharp-libvips-linux-arm64@1.2.4':
1615 | optional: true
1616 |
1617 | '@img/sharp-libvips-linux-arm@1.2.4':
1618 | optional: true
1619 |
1620 | '@img/sharp-libvips-linux-ppc64@1.2.4':
1621 | optional: true
1622 |
1623 | '@img/sharp-libvips-linux-riscv64@1.2.4':
1624 | optional: true
1625 |
1626 | '@img/sharp-libvips-linux-s390x@1.2.4':
1627 | optional: true
1628 |
1629 | '@img/sharp-libvips-linux-x64@1.2.4':
1630 | optional: true
1631 |
1632 | '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
1633 | optional: true
1634 |
1635 | '@img/sharp-libvips-linuxmusl-x64@1.2.4':
1636 | optional: true
1637 |
1638 | '@img/sharp-linux-arm64@0.34.5':
1639 | optionalDependencies:
1640 | '@img/sharp-libvips-linux-arm64': 1.2.4
1641 | optional: true
1642 |
1643 | '@img/sharp-linux-arm@0.34.5':
1644 | optionalDependencies:
1645 | '@img/sharp-libvips-linux-arm': 1.2.4
1646 | optional: true
1647 |
1648 | '@img/sharp-linux-ppc64@0.34.5':
1649 | optionalDependencies:
1650 | '@img/sharp-libvips-linux-ppc64': 1.2.4
1651 | optional: true
1652 |
1653 | '@img/sharp-linux-riscv64@0.34.5':
1654 | optionalDependencies:
1655 | '@img/sharp-libvips-linux-riscv64': 1.2.4
1656 | optional: true
1657 |
1658 | '@img/sharp-linux-s390x@0.34.5':
1659 | optionalDependencies:
1660 | '@img/sharp-libvips-linux-s390x': 1.2.4
1661 | optional: true
1662 |
1663 | '@img/sharp-linux-x64@0.34.5':
1664 | optionalDependencies:
1665 | '@img/sharp-libvips-linux-x64': 1.2.4
1666 | optional: true
1667 |
1668 | '@img/sharp-linuxmusl-arm64@0.34.5':
1669 | optionalDependencies:
1670 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
1671 | optional: true
1672 |
1673 | '@img/sharp-linuxmusl-x64@0.34.5':
1674 | optionalDependencies:
1675 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4
1676 | optional: true
1677 |
1678 | '@img/sharp-wasm32@0.34.5':
1679 | dependencies:
1680 | '@emnapi/runtime': 1.7.1
1681 | optional: true
1682 |
1683 | '@img/sharp-win32-arm64@0.34.5':
1684 | optional: true
1685 |
1686 | '@img/sharp-win32-ia32@0.34.5':
1687 | optional: true
1688 |
1689 | '@img/sharp-win32-x64@0.34.5':
1690 | optional: true
1691 |
1692 | '@next/env@15.6.0-canary.59': {}
1693 |
1694 | '@next/swc-darwin-arm64@15.6.0-canary.58':
1695 | optional: true
1696 |
1697 | '@next/swc-darwin-x64@15.6.0-canary.58':
1698 | optional: true
1699 |
1700 | '@next/swc-linux-arm64-gnu@15.6.0-canary.58':
1701 | optional: true
1702 |
1703 | '@next/swc-linux-arm64-musl@15.6.0-canary.58':
1704 | optional: true
1705 |
1706 | '@next/swc-linux-x64-gnu@15.6.0-canary.58':
1707 | optional: true
1708 |
1709 | '@next/swc-linux-x64-musl@15.6.0-canary.58':
1710 | optional: true
1711 |
1712 | '@next/swc-win32-arm64-msvc@15.6.0-canary.58':
1713 | optional: true
1714 |
1715 | '@next/swc-win32-x64-msvc@15.6.0-canary.58':
1716 | optional: true
1717 |
1718 | '@radix-ui/primitive@1.1.1': {}
1719 |
1720 | '@radix-ui/react-arrow@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1721 | dependencies:
1722 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1723 | react: 19.0.0
1724 | react-dom: 19.0.0(react@19.0.0)
1725 | optionalDependencies:
1726 | '@types/react': 19.0.10
1727 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1728 |
1729 | '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.10)(react@19.0.0)':
1730 | dependencies:
1731 | react: 19.0.0
1732 | optionalDependencies:
1733 | '@types/react': 19.0.10
1734 |
1735 | '@radix-ui/react-context@1.1.1(@types/react@19.0.10)(react@19.0.0)':
1736 | dependencies:
1737 | react: 19.0.0
1738 | optionalDependencies:
1739 | '@types/react': 19.0.10
1740 |
1741 | '@radix-ui/react-dialog@1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1742 | dependencies:
1743 | '@radix-ui/primitive': 1.1.1
1744 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1745 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1746 | '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1747 | '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1748 | '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1749 | '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1750 | '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1751 | '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1752 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1753 | '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
1754 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1755 | aria-hidden: 1.2.4
1756 | react: 19.0.0
1757 | react-dom: 19.0.0(react@19.0.0)
1758 | react-remove-scroll: 2.6.3(@types/react@19.0.10)(react@19.0.0)
1759 | optionalDependencies:
1760 | '@types/react': 19.0.10
1761 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1762 |
1763 | '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1764 | dependencies:
1765 | '@radix-ui/primitive': 1.1.1
1766 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1767 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1768 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1769 | '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1770 | react: 19.0.0
1771 | react-dom: 19.0.0(react@19.0.0)
1772 | optionalDependencies:
1773 | '@types/react': 19.0.10
1774 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1775 |
1776 | '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.10)(react@19.0.0)':
1777 | dependencies:
1778 | react: 19.0.0
1779 | optionalDependencies:
1780 | '@types/react': 19.0.10
1781 |
1782 | '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1783 | dependencies:
1784 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1785 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1786 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1787 | react: 19.0.0
1788 | react-dom: 19.0.0(react@19.0.0)
1789 | optionalDependencies:
1790 | '@types/react': 19.0.10
1791 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1792 |
1793 | '@radix-ui/react-icons@1.3.2(react@19.0.0)':
1794 | dependencies:
1795 | react: 19.0.0
1796 |
1797 | '@radix-ui/react-id@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1798 | dependencies:
1799 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1800 | react: 19.0.0
1801 | optionalDependencies:
1802 | '@types/react': 19.0.10
1803 |
1804 | '@radix-ui/react-popper@1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1805 | dependencies:
1806 | '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1807 | '@radix-ui/react-arrow': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1808 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1809 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1810 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1811 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1812 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1813 | '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1814 | '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1815 | '@radix-ui/rect': 1.1.0
1816 | react: 19.0.0
1817 | react-dom: 19.0.0(react@19.0.0)
1818 | optionalDependencies:
1819 | '@types/react': 19.0.10
1820 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1821 |
1822 | '@radix-ui/react-portal@1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1823 | dependencies:
1824 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1825 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1826 | react: 19.0.0
1827 | react-dom: 19.0.0(react@19.0.0)
1828 | optionalDependencies:
1829 | '@types/react': 19.0.10
1830 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1831 |
1832 | '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1833 | dependencies:
1834 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1835 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1836 | react: 19.0.0
1837 | react-dom: 19.0.0(react@19.0.0)
1838 | optionalDependencies:
1839 | '@types/react': 19.0.10
1840 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1841 |
1842 | '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1843 | dependencies:
1844 | '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
1845 | react: 19.0.0
1846 | react-dom: 19.0.0(react@19.0.0)
1847 | optionalDependencies:
1848 | '@types/react': 19.0.10
1849 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1850 |
1851 | '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)':
1852 | dependencies:
1853 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1854 | react: 19.0.0
1855 | optionalDependencies:
1856 | '@types/react': 19.0.10
1857 |
1858 | '@radix-ui/react-tooltip@1.1.8(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1859 | dependencies:
1860 | '@radix-ui/primitive': 1.1.1
1861 | '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1862 | '@radix-ui/react-context': 1.1.1(@types/react@19.0.10)(react@19.0.0)
1863 | '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1864 | '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1865 | '@radix-ui/react-popper': 1.2.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1866 | '@radix-ui/react-portal': 1.1.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1867 | '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1868 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1869 | '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0)
1870 | '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1871 | '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1872 | react: 19.0.0
1873 | react-dom: 19.0.0(react@19.0.0)
1874 | optionalDependencies:
1875 | '@types/react': 19.0.10
1876 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1877 |
1878 | '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1879 | dependencies:
1880 | react: 19.0.0
1881 | optionalDependencies:
1882 | '@types/react': 19.0.10
1883 |
1884 | '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1885 | dependencies:
1886 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1887 | react: 19.0.0
1888 | optionalDependencies:
1889 | '@types/react': 19.0.10
1890 |
1891 | '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1892 | dependencies:
1893 | '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1894 | react: 19.0.0
1895 | optionalDependencies:
1896 | '@types/react': 19.0.10
1897 |
1898 | '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1899 | dependencies:
1900 | react: 19.0.0
1901 | optionalDependencies:
1902 | '@types/react': 19.0.10
1903 |
1904 | '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1905 | dependencies:
1906 | '@radix-ui/rect': 1.1.0
1907 | react: 19.0.0
1908 | optionalDependencies:
1909 | '@types/react': 19.0.10
1910 |
1911 | '@radix-ui/react-use-size@1.1.0(@types/react@19.0.10)(react@19.0.0)':
1912 | dependencies:
1913 | '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.10)(react@19.0.0)
1914 | react: 19.0.0
1915 | optionalDependencies:
1916 | '@types/react': 19.0.10
1917 |
1918 | '@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
1919 | dependencies:
1920 | '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
1921 | react: 19.0.0
1922 | react-dom: 19.0.0(react@19.0.0)
1923 | optionalDependencies:
1924 | '@types/react': 19.0.10
1925 | '@types/react-dom': 19.0.4(@types/react@19.0.10)
1926 |
1927 | '@radix-ui/rect@1.1.0': {}
1928 |
1929 | '@swc/helpers@0.5.15':
1930 | dependencies:
1931 | tslib: 2.8.1
1932 |
1933 | '@tailwindcss/node@4.0.13':
1934 | dependencies:
1935 | enhanced-resolve: 5.18.1
1936 | jiti: 2.4.2
1937 | tailwindcss: 4.0.13
1938 |
1939 | '@tailwindcss/oxide-android-arm64@4.0.13':
1940 | optional: true
1941 |
1942 | '@tailwindcss/oxide-darwin-arm64@4.0.13':
1943 | optional: true
1944 |
1945 | '@tailwindcss/oxide-darwin-x64@4.0.13':
1946 | optional: true
1947 |
1948 | '@tailwindcss/oxide-freebsd-x64@4.0.13':
1949 | optional: true
1950 |
1951 | '@tailwindcss/oxide-linux-arm-gnueabihf@4.0.13':
1952 | optional: true
1953 |
1954 | '@tailwindcss/oxide-linux-arm64-gnu@4.0.13':
1955 | optional: true
1956 |
1957 | '@tailwindcss/oxide-linux-arm64-musl@4.0.13':
1958 | optional: true
1959 |
1960 | '@tailwindcss/oxide-linux-x64-gnu@4.0.13':
1961 | optional: true
1962 |
1963 | '@tailwindcss/oxide-linux-x64-musl@4.0.13':
1964 | optional: true
1965 |
1966 | '@tailwindcss/oxide-win32-arm64-msvc@4.0.13':
1967 | optional: true
1968 |
1969 | '@tailwindcss/oxide-win32-x64-msvc@4.0.13':
1970 | optional: true
1971 |
1972 | '@tailwindcss/oxide@4.0.13':
1973 | optionalDependencies:
1974 | '@tailwindcss/oxide-android-arm64': 4.0.13
1975 | '@tailwindcss/oxide-darwin-arm64': 4.0.13
1976 | '@tailwindcss/oxide-darwin-x64': 4.0.13
1977 | '@tailwindcss/oxide-freebsd-x64': 4.0.13
1978 | '@tailwindcss/oxide-linux-arm-gnueabihf': 4.0.13
1979 | '@tailwindcss/oxide-linux-arm64-gnu': 4.0.13
1980 | '@tailwindcss/oxide-linux-arm64-musl': 4.0.13
1981 | '@tailwindcss/oxide-linux-x64-gnu': 4.0.13
1982 | '@tailwindcss/oxide-linux-x64-musl': 4.0.13
1983 | '@tailwindcss/oxide-win32-arm64-msvc': 4.0.13
1984 | '@tailwindcss/oxide-win32-x64-msvc': 4.0.13
1985 |
1986 | '@tailwindcss/postcss@4.0.13':
1987 | dependencies:
1988 | '@alloc/quick-lru': 5.2.0
1989 | '@tailwindcss/node': 4.0.13
1990 | '@tailwindcss/oxide': 4.0.13
1991 | lightningcss: 1.29.2
1992 | postcss: 8.4.49
1993 | tailwindcss: 4.0.13
1994 |
1995 | '@types/node@22.10.1':
1996 | dependencies:
1997 | undici-types: 6.20.0
1998 |
1999 | '@types/react-dom@19.0.4(@types/react@19.0.10)':
2000 | dependencies:
2001 | '@types/react': 19.0.10
2002 |
2003 | '@types/react@19.0.10':
2004 | dependencies:
2005 | csstype: 3.1.3
2006 |
2007 | aria-hidden@1.2.4:
2008 | dependencies:
2009 | tslib: 2.8.1
2010 |
2011 | buffer-from@1.1.2: {}
2012 |
2013 | caniuse-lite@1.0.30001703: {}
2014 |
2015 | class-variance-authority@0.7.1:
2016 | dependencies:
2017 | clsx: 2.1.1
2018 |
2019 | client-only@0.0.1: {}
2020 |
2021 | clsx@2.1.1: {}
2022 |
2023 | csstype@3.1.3: {}
2024 |
2025 | debug@4.3.7:
2026 | dependencies:
2027 | ms: 2.1.3
2028 |
2029 | detect-libc@2.0.3: {}
2030 |
2031 | detect-libc@2.1.2:
2032 | optional: true
2033 |
2034 | detect-node-es@1.1.0: {}
2035 |
2036 | dotenv@16.4.5: {}
2037 |
2038 | drizzle-kit@0.28.1:
2039 | dependencies:
2040 | '@drizzle-team/brocli': 0.10.2
2041 | '@esbuild-kit/esm-loader': 2.6.5
2042 | esbuild: 0.19.12
2043 | esbuild-register: 3.6.0(esbuild@0.19.12)
2044 | transitivePeerDependencies:
2045 | - supports-color
2046 |
2047 | drizzle-orm@0.36.4(@types/react@19.0.10)(postgres@3.4.5)(react@19.0.0):
2048 | optionalDependencies:
2049 | '@types/react': 19.0.10
2050 | postgres: 3.4.5
2051 | react: 19.0.0
2052 |
2053 | enhanced-resolve@5.18.1:
2054 | dependencies:
2055 | graceful-fs: 4.2.11
2056 | tapable: 2.2.1
2057 |
2058 | esbuild-register@3.6.0(esbuild@0.19.12):
2059 | dependencies:
2060 | debug: 4.3.7
2061 | esbuild: 0.19.12
2062 | transitivePeerDependencies:
2063 | - supports-color
2064 |
2065 | esbuild@0.18.20:
2066 | optionalDependencies:
2067 | '@esbuild/android-arm': 0.18.20
2068 | '@esbuild/android-arm64': 0.18.20
2069 | '@esbuild/android-x64': 0.18.20
2070 | '@esbuild/darwin-arm64': 0.18.20
2071 | '@esbuild/darwin-x64': 0.18.20
2072 | '@esbuild/freebsd-arm64': 0.18.20
2073 | '@esbuild/freebsd-x64': 0.18.20
2074 | '@esbuild/linux-arm': 0.18.20
2075 | '@esbuild/linux-arm64': 0.18.20
2076 | '@esbuild/linux-ia32': 0.18.20
2077 | '@esbuild/linux-loong64': 0.18.20
2078 | '@esbuild/linux-mips64el': 0.18.20
2079 | '@esbuild/linux-ppc64': 0.18.20
2080 | '@esbuild/linux-riscv64': 0.18.20
2081 | '@esbuild/linux-s390x': 0.18.20
2082 | '@esbuild/linux-x64': 0.18.20
2083 | '@esbuild/netbsd-x64': 0.18.20
2084 | '@esbuild/openbsd-x64': 0.18.20
2085 | '@esbuild/sunos-x64': 0.18.20
2086 | '@esbuild/win32-arm64': 0.18.20
2087 | '@esbuild/win32-ia32': 0.18.20
2088 | '@esbuild/win32-x64': 0.18.20
2089 |
2090 | esbuild@0.19.12:
2091 | optionalDependencies:
2092 | '@esbuild/aix-ppc64': 0.19.12
2093 | '@esbuild/android-arm': 0.19.12
2094 | '@esbuild/android-arm64': 0.19.12
2095 | '@esbuild/android-x64': 0.19.12
2096 | '@esbuild/darwin-arm64': 0.19.12
2097 | '@esbuild/darwin-x64': 0.19.12
2098 | '@esbuild/freebsd-arm64': 0.19.12
2099 | '@esbuild/freebsd-x64': 0.19.12
2100 | '@esbuild/linux-arm': 0.19.12
2101 | '@esbuild/linux-arm64': 0.19.12
2102 | '@esbuild/linux-ia32': 0.19.12
2103 | '@esbuild/linux-loong64': 0.19.12
2104 | '@esbuild/linux-mips64el': 0.19.12
2105 | '@esbuild/linux-ppc64': 0.19.12
2106 | '@esbuild/linux-riscv64': 0.19.12
2107 | '@esbuild/linux-s390x': 0.19.12
2108 | '@esbuild/linux-x64': 0.19.12
2109 | '@esbuild/netbsd-x64': 0.19.12
2110 | '@esbuild/openbsd-x64': 0.19.12
2111 | '@esbuild/sunos-x64': 0.19.12
2112 | '@esbuild/win32-arm64': 0.19.12
2113 | '@esbuild/win32-ia32': 0.19.12
2114 | '@esbuild/win32-x64': 0.19.12
2115 |
2116 | geist@1.3.1(next@15.6.0-canary.59(react-dom@19.0.0(react@19.0.0))(react@19.0.0)):
2117 | dependencies:
2118 | next: 15.6.0-canary.59(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
2119 |
2120 | get-nonce@1.0.1: {}
2121 |
2122 | get-tsconfig@4.8.1:
2123 | dependencies:
2124 | resolve-pkg-maps: 1.0.0
2125 |
2126 | graceful-fs@4.2.11: {}
2127 |
2128 | jiti@2.4.2: {}
2129 |
2130 | lightningcss-darwin-arm64@1.29.2:
2131 | optional: true
2132 |
2133 | lightningcss-darwin-x64@1.29.2:
2134 | optional: true
2135 |
2136 | lightningcss-freebsd-x64@1.29.2:
2137 | optional: true
2138 |
2139 | lightningcss-linux-arm-gnueabihf@1.29.2:
2140 | optional: true
2141 |
2142 | lightningcss-linux-arm64-gnu@1.29.2:
2143 | optional: true
2144 |
2145 | lightningcss-linux-arm64-musl@1.29.2:
2146 | optional: true
2147 |
2148 | lightningcss-linux-x64-gnu@1.29.2:
2149 | optional: true
2150 |
2151 | lightningcss-linux-x64-musl@1.29.2:
2152 | optional: true
2153 |
2154 | lightningcss-win32-arm64-msvc@1.29.2:
2155 | optional: true
2156 |
2157 | lightningcss-win32-x64-msvc@1.29.2:
2158 | optional: true
2159 |
2160 | lightningcss@1.29.2:
2161 | dependencies:
2162 | detect-libc: 2.0.3
2163 | optionalDependencies:
2164 | lightningcss-darwin-arm64: 1.29.2
2165 | lightningcss-darwin-x64: 1.29.2
2166 | lightningcss-freebsd-x64: 1.29.2
2167 | lightningcss-linux-arm-gnueabihf: 1.29.2
2168 | lightningcss-linux-arm64-gnu: 1.29.2
2169 | lightningcss-linux-arm64-musl: 1.29.2
2170 | lightningcss-linux-x64-gnu: 1.29.2
2171 | lightningcss-linux-x64-musl: 1.29.2
2172 | lightningcss-win32-arm64-msvc: 1.29.2
2173 | lightningcss-win32-x64-msvc: 1.29.2
2174 |
2175 | lucide-react@0.462.0(react@19.0.0):
2176 | dependencies:
2177 | react: 19.0.0
2178 |
2179 | ms@2.1.3: {}
2180 |
2181 | nanoid@3.3.8: {}
2182 |
2183 | nanoid@3.3.9: {}
2184 |
2185 | next@15.6.0-canary.59(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
2186 | dependencies:
2187 | '@next/env': 15.6.0-canary.59
2188 | '@swc/helpers': 0.5.15
2189 | caniuse-lite: 1.0.30001703
2190 | postcss: 8.4.31
2191 | react: 19.0.0
2192 | react-dom: 19.0.0(react@19.0.0)
2193 | styled-jsx: 5.1.6(react@19.0.0)
2194 | optionalDependencies:
2195 | '@next/swc-darwin-arm64': 15.6.0-canary.58
2196 | '@next/swc-darwin-x64': 15.6.0-canary.58
2197 | '@next/swc-linux-arm64-gnu': 15.6.0-canary.58
2198 | '@next/swc-linux-arm64-musl': 15.6.0-canary.58
2199 | '@next/swc-linux-x64-gnu': 15.6.0-canary.58
2200 | '@next/swc-linux-x64-musl': 15.6.0-canary.58
2201 | '@next/swc-win32-arm64-msvc': 15.6.0-canary.58
2202 | '@next/swc-win32-x64-msvc': 15.6.0-canary.58
2203 | sharp: 0.34.5
2204 | transitivePeerDependencies:
2205 | - '@babel/core'
2206 | - babel-plugin-macros
2207 |
2208 | picocolors@1.1.1: {}
2209 |
2210 | postcss@8.4.31:
2211 | dependencies:
2212 | nanoid: 3.3.9
2213 | picocolors: 1.1.1
2214 | source-map-js: 1.2.1
2215 |
2216 | postcss@8.4.49:
2217 | dependencies:
2218 | nanoid: 3.3.8
2219 | picocolors: 1.1.1
2220 | source-map-js: 1.2.1
2221 |
2222 | postgres@3.4.5: {}
2223 |
2224 | prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.2):
2225 | dependencies:
2226 | prettier: 3.5.3
2227 | typescript: 5.8.2
2228 |
2229 | prettier-plugin-tailwindcss@0.6.11(prettier-plugin-organize-imports@4.1.0(prettier@3.5.3)(typescript@5.8.2))(prettier@3.5.3):
2230 | dependencies:
2231 | prettier: 3.5.3
2232 | optionalDependencies:
2233 | prettier-plugin-organize-imports: 4.1.0(prettier@3.5.3)(typescript@5.8.2)
2234 |
2235 | prettier@3.5.3: {}
2236 |
2237 | react-dom@19.0.0(react@19.0.0):
2238 | dependencies:
2239 | react: 19.0.0
2240 | scheduler: 0.25.0
2241 |
2242 | react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0):
2243 | dependencies:
2244 | react: 19.0.0
2245 | react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
2246 | tslib: 2.8.1
2247 | optionalDependencies:
2248 | '@types/react': 19.0.10
2249 |
2250 | react-remove-scroll@2.6.3(@types/react@19.0.10)(react@19.0.0):
2251 | dependencies:
2252 | react: 19.0.0
2253 | react-remove-scroll-bar: 2.3.8(@types/react@19.0.10)(react@19.0.0)
2254 | react-style-singleton: 2.2.3(@types/react@19.0.10)(react@19.0.0)
2255 | tslib: 2.8.1
2256 | use-callback-ref: 1.3.3(@types/react@19.0.10)(react@19.0.0)
2257 | use-sidecar: 1.1.3(@types/react@19.0.10)(react@19.0.0)
2258 | optionalDependencies:
2259 | '@types/react': 19.0.10
2260 |
2261 | react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0):
2262 | dependencies:
2263 | get-nonce: 1.0.1
2264 | react: 19.0.0
2265 | tslib: 2.8.1
2266 | optionalDependencies:
2267 | '@types/react': 19.0.10
2268 |
2269 | react@19.0.0: {}
2270 |
2271 | resolve-pkg-maps@1.0.0: {}
2272 |
2273 | scheduler@0.25.0: {}
2274 |
2275 | semver@7.7.3:
2276 | optional: true
2277 |
2278 | sharp@0.34.5:
2279 | dependencies:
2280 | '@img/colour': 1.0.0
2281 | detect-libc: 2.1.2
2282 | semver: 7.7.3
2283 | optionalDependencies:
2284 | '@img/sharp-darwin-arm64': 0.34.5
2285 | '@img/sharp-darwin-x64': 0.34.5
2286 | '@img/sharp-libvips-darwin-arm64': 1.2.4
2287 | '@img/sharp-libvips-darwin-x64': 1.2.4
2288 | '@img/sharp-libvips-linux-arm': 1.2.4
2289 | '@img/sharp-libvips-linux-arm64': 1.2.4
2290 | '@img/sharp-libvips-linux-ppc64': 1.2.4
2291 | '@img/sharp-libvips-linux-riscv64': 1.2.4
2292 | '@img/sharp-libvips-linux-s390x': 1.2.4
2293 | '@img/sharp-libvips-linux-x64': 1.2.4
2294 | '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
2295 | '@img/sharp-libvips-linuxmusl-x64': 1.2.4
2296 | '@img/sharp-linux-arm': 0.34.5
2297 | '@img/sharp-linux-arm64': 0.34.5
2298 | '@img/sharp-linux-ppc64': 0.34.5
2299 | '@img/sharp-linux-riscv64': 0.34.5
2300 | '@img/sharp-linux-s390x': 0.34.5
2301 | '@img/sharp-linux-x64': 0.34.5
2302 | '@img/sharp-linuxmusl-arm64': 0.34.5
2303 | '@img/sharp-linuxmusl-x64': 0.34.5
2304 | '@img/sharp-wasm32': 0.34.5
2305 | '@img/sharp-win32-arm64': 0.34.5
2306 | '@img/sharp-win32-ia32': 0.34.5
2307 | '@img/sharp-win32-x64': 0.34.5
2308 | optional: true
2309 |
2310 | sonner@1.7.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
2311 | dependencies:
2312 | react: 19.0.0
2313 | react-dom: 19.0.0(react@19.0.0)
2314 |
2315 | source-map-js@1.2.1: {}
2316 |
2317 | source-map-support@0.5.21:
2318 | dependencies:
2319 | buffer-from: 1.1.2
2320 | source-map: 0.6.1
2321 |
2322 | source-map@0.6.1: {}
2323 |
2324 | styled-jsx@5.1.6(react@19.0.0):
2325 | dependencies:
2326 | client-only: 0.0.1
2327 | react: 19.0.0
2328 |
2329 | tailwind-merge@3.0.2: {}
2330 |
2331 | tailwindcss-animate@1.0.7(tailwindcss@4.0.13):
2332 | dependencies:
2333 | tailwindcss: 4.0.13
2334 |
2335 | tailwindcss@4.0.13: {}
2336 |
2337 | tapable@2.2.1: {}
2338 |
2339 | tslib@2.8.1: {}
2340 |
2341 | typescript@5.8.2: {}
2342 |
2343 | undici-types@6.20.0: {}
2344 |
2345 | use-callback-ref@1.3.3(@types/react@19.0.10)(react@19.0.0):
2346 | dependencies:
2347 | react: 19.0.0
2348 | tslib: 2.8.1
2349 | optionalDependencies:
2350 | '@types/react': 19.0.10
2351 |
2352 | use-sidecar@1.1.3(@types/react@19.0.10)(react@19.0.0):
2353 | dependencies:
2354 | detect-node-es: 1.1.0
2355 | react: 19.0.0
2356 | tslib: 2.8.1
2357 | optionalDependencies:
2358 | '@types/react': 19.0.10
2359 |
2360 | zod@3.23.8: {}
2361 |
--------------------------------------------------------------------------------