diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..ac5e5ce
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,2 @@
+OPENAI_API_KEY=XXXXXXXX
+NEXTAUTH_SECRET=XXXXXXXX
diff --git a/.eslintrc.json b/.eslintrc.json
index ef7fd95..6ec5479 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,5 +1,25 @@
{
"$schema": "https://json.schemastore.org/eslintrc",
"root": true,
- "extends": ["next/core-web-vitals", "prettier"]
+ "extends": [
+ "next/core-web-vitals",
+ "prettier",
+ "plugin:tailwindcss/recommended"
+ ],
+ "plugins": ["tailwindcss"],
+ "rules": {
+ "tailwindcss/no-custom-classname": "off"
+ },
+ "settings": {
+ "tailwindcss": {
+ "callees": ["cn", "cva"],
+ "config": "tailwind.config.js"
+ }
+ },
+ "overrides": [
+ {
+ "files": ["*.ts", "*.tsx"],
+ "parser": "@typescript-eslint/parser"
+ }
+ ]
}
diff --git a/.gitignore b/.gitignore
index d8de1d3..83d560e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,3 +35,4 @@ yarn-error.log*
.contentlayer
.env
.vercel
+.vscode
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 99438eb..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "typescript.tsdk": "../../node_modules/.pnpm/typescript@4.9.5/node_modules/typescript/lib",
- "typescript.enablePromptUseWorkspaceTsdk": true
-}
diff --git a/README.md b/README.md
index a9b83db..247c999 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,45 @@
# Next.js AI Chatbot
-A an AI-powered Chatbot built with Next.js built by Vercel Labs.
+A Next.js 13 and App Router-ready AI chatbot template featuring:
+
+- [Next.js](https://nextjs.org) App Router
+- React Server Components (RSCs), Suspense, and Server Actions
+- [Vercel AI SDK](https://sdk.vercel.ai/docs) for streaming chat UI
+- Support for OpenAI (default), Anthropic, HuggingFace, or custom AI chat models and/or LangChain
+- Edge runtime-ready
+- [Shadcn UI](https://ui.shadcn.com)
+ - Styling with [Tailwind CSS](https://tailwindcss.com)
+ - [Radix UI](https://radix-ui.com) for headless component primitives
+- Chat History, rate limiting, and session storage with [Vercel KV](https://vercel.com/storage/kv)
+- [Next Auth](https://github.com/nextauthjs/next-auth) for authentication
+
+## Model Providers
+
+This template ships with OpenAI `gpt-3.5-turbo` as the default. However, thanks to the [Vercel AI SDK](https://sdk.vercel.ai/docs), switching LLM providers to [Anthropic](https://anthropic.com), [HuggingFace](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code.
+
+## Running locally
+
+You will need to use the environment variables [defined in `.env.example`](.env.example) to run Next.js AI Chatbot. It's recommended you use [Vercel Environment Variables](https://vercel.com/docs/concepts/projects/environment-variables) for this, but a `.env` file is all that is necessary.
+
+> Note: You should not commit your `.env` file or it will expose secrets that will allow others to control access to your various OpenAI and authentication provider accounts.
+
+1. Install Vercel CLI: `npm i -g vercel`
+2. Link local instance with Vercel and GitHub accounts (creates `.vercel` directory): `vercel link`
+3. Download your environment variables: `vercel env pull`
+
+```bash
+pnpm install
+pnpm dev
+```
+
+Your app should now be running on [localhost:3000](http://localhost:3000/).
+
+## Authors
+
+This library is created by [Vercel](https://vercel.com) and [Next.js](https://nextjs.org) team members, with contributions from:
+
+- Jared Palmer ([@jaredpalmer](https://twitter.com/jaredpalmer)) - [Vercel](https://vercel.com)
+- Shu Ding ([@shuding\_](https://twitter.com/shuding_)) - [Vercel](https://vercel.com)
+- Shadcn ([@shadcn](https://twitter.com/shadcn)) - [Contractor](https://shadcn.com)
+
+[Contributors](https://github.com/vercel-labs/ai-chatbot/graphs/contributors)
diff --git a/app/actions.ts b/app/actions.ts
index aa4c98f..3d40e88 100644
--- a/app/actions.ts
+++ b/app/actions.ts
@@ -1,7 +1,44 @@
'use server'
-import { kv } from '@vercel/kv'
import { revalidatePath } from 'next/cache'
+import { kv } from '@vercel/kv'
+
+import { type Chat } from '@/lib/types'
+
+export async function getChats(userId?: string | null) {
+ if (!userId) {
+ return []
+ }
+
+ try {
+ const pipeline = kv.pipeline()
+ const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1)
+
+ for (const chat of chats) {
+ pipeline.hgetall(chat)
+ }
+
+ const results = await pipeline.exec()
+
+ return results as Chat[]
+ } catch (error) {
+ return []
+ }
+}
+
+export async function getChat(id: string, userId: string) {
+ const chat = await kv.hgetall(`chat:${id}`)
+
+ if (!chat) {
+ throw new Error('Not found')
+ }
+
+ if (userId && chat.userId !== userId) {
+ throw new Error('Unauthorized')
+ }
+
+ return chat
+}
export async function removeChat({
id,
diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts
index 883210b..4463a8e 100644
--- a/app/api/auth/[...nextauth]/route.ts
+++ b/app/api/auth/[...nextauth]/route.ts
@@ -1,2 +1,2 @@
export { GET, POST } from '@/auth'
-export const runtime = 'edge'
+// export const runtime = 'edge'
diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts
index 710194d..515d947 100644
--- a/app/api/chat/route.ts
+++ b/app/api/chat/route.ts
@@ -1,10 +1,11 @@
import { auth } from '@/auth'
import { kv } from '@vercel/kv'
-import { nanoid } from '@/lib/utils'
import { OpenAIStream, StreamingTextResponse } from 'ai-connector'
import { Configuration, OpenAIApi } from 'openai-edge'
-export const runtime = 'edge'
+import { nanoid } from '@/lib/utils'
+
+// export const runtime = 'edge'
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY
diff --git a/app/chat-list.tsx b/app/chat-list.tsx
deleted file mode 100644
index 7675690..0000000
--- a/app/chat-list.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-'use client'
-
-import { type Message } from 'ai-connector'
-
-import { ChatMessage } from './chat-message'
-import { NextChatLogo } from '@/components/ui/nextchat-logo'
-import { Plus } from 'lucide-react'
-import { EmptyScreen } from './empty'
-
-export interface ChatList {
- messages: Message[]
-}
-
-export function ChatList({ messages }: ChatList) {
- return (
-
-
-
-
-
- Next.js Chatbot
-
-
-
-
-
- {messages.length > 0 ? (
-
- {messages.map(message => (
-
- ))}
-
- ) : (
-
- )}
-
-
- )
-}
diff --git a/app/chat-message.tsx b/app/chat-message.tsx
deleted file mode 100644
index dab5d07..0000000
--- a/app/chat-message.tsx
+++ /dev/null
@@ -1,119 +0,0 @@
-import CodeBlock from '@/components/ui/codeblock'
-import { MemoizedReactMarkdown } from '@/components/markdown'
-import remarkGfm from 'remark-gfm'
-import remarkMath from 'remark-math'
-import { cn } from '@/lib/utils'
-import { fontMessage } from '@/lib/fonts'
-import { Message } from 'ai-connector'
-export interface ChatMessageProps {
- message: Message
-}
-
-export function ChatMessage(props: ChatMessageProps) {
- return (
-
-
-
- {props.message.role === 'user' ? (
-
- ) : (
-
-
-
- )}
-
- ▍
-
- )
- }
-
- children[0] = (children[0] as string).replace('`▍`', '▍')
- }
-
- const match = /language-(\w+)/.exec(className || '')
-
- return !inline ? (
-
- ) : (
-
- {children}
-
- )
- },
- table({ children }) {
- return (
-
- )
- },
- th({ children }) {
- return (
-
- {children}
-
- )
- },
- td({ children }) {
- return (
-
- {children}
-
- )
- }
- }}
- >
- {props.message.content}
-
-
-
-
- )
-}
-
-ChatMessage.displayName = 'ChatMessage'
-
-export function IconOpenAI(props: JSX.IntrinsicElements['svg']) {
- return (
-
- OpenAI icon
-
-
- )
-}
diff --git a/app/chat.tsx b/app/chat.tsx
deleted file mode 100644
index d328db2..0000000
--- a/app/chat.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-'use client'
-
-import { useRouter } from 'next/navigation'
-import { Prompt } from './prompt'
-import { useChat, type Message } from 'ai-connector'
-import { ChatList } from './chat-list'
-
-export interface ChatProps {
- // create?: (input: string) => Chat | undefined;
- initialMessages?: Message[]
- id?: string
-}
-
-export function Chat({
- id,
- // create,
- initialMessages
-}: ChatProps) {
- const router = useRouter()
-
- const { isLoading, messages, reload, append } = useChat({
- initialMessages: initialMessages as any[],
- id
- // onCreate: (id: string) => {
- // router.push(`/chat/${id}`);
- // },
- })
-
- return (
-
-
-
-
-
-
{
- append({
- content: value,
- role: 'user'
- })
- }}
- onRefresh={messages.length ? reload : undefined}
- isLoading={isLoading}
- />
-
-
- )
-}
-
-Chat.displayName = 'Chat'
diff --git a/app/chat/[id]/page.tsx b/app/chat/[id]/page.tsx
index 61094d6..728ef16 100644
--- a/app/chat/[id]/page.tsx
+++ b/app/chat/[id]/page.tsx
@@ -1,15 +1,12 @@
-import { Sidebar } from '@/app/sidebar'
-
-import { auth } from '@/auth'
import { type Metadata } from 'next'
+import { auth } from '@/auth'
-import { Chat } from '@/app/chat'
-import { type Chat as ChatType } from '@/lib/types'
-import { kv } from '@vercel/kv'
-import { Message } from 'ai-connector'
+import { Chat } from '@/components/chat'
+import { getChat } from '@/app/actions'
-export const runtime = 'edge'
+// export const runtime = 'edge'
export const preferredRegion = 'home'
+
export interface ChatPageProps {
params: {
id: string
@@ -30,27 +27,5 @@ export default async function ChatPage({ params }: ChatPageProps) {
const session = await auth()
const chat = await getChat(params.id, session?.user?.email ?? '')
- return (
-
- )
-}
-
-ChatPage.displayName = 'ChatPage'
-
-async function getChat(id: string, userId: string) {
- const chat = await kv.hgetall(`chat:${id}`)
- if (!chat) {
- throw new Error('Not found')
- }
-
- if (userId && chat.userId !== userId) {
- throw new Error('Unauthorized')
- }
-
- return chat
+ return
}
diff --git a/app/empty.tsx b/app/empty.tsx
deleted file mode 100644
index 0deaf75..0000000
--- a/app/empty.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import { cn } from '@/lib/utils'
-import { ExternalLink } from './external-link'
-import { fontMessage } from '@/lib/fonts'
-
-function ExampleBubble({ children }: { children?: React.ReactNode }) {
- return (
-
- )
-}
-
-export function EmptyScreen() {
- return (
-
-
-
-
-
Welcome to Next.js Chatbot!
-
- This is an open source AI chatbot app built with{' '}
- Next.js and{' '}
-
- Vercel KV
-
- . You can start a conversation here or try the following examples:
-
-
-
-
- Explain technical concepts
- Summarize article
- Get assistance
- Draft an email
-
-
-
- )
-}
diff --git a/app/globals.css b/app/globals.css
index 69f3827..9beeb2c 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -3,17 +3,76 @@
@tailwind utilities;
@layer base {
- #__next,
- #root {
- height: 100%;
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 240 10% 3.9%;
+
+ --muted: 240 4.8% 95.9%;
+ --muted-foreground: 240 3.8% 46.1%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 240 10% 3.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 240 10% 3.9%;
+
+ --border: 240 5.9% 90%;
+ --input: 240 5.9% 90%;
+
+ --primary: 240 5.9% 10%;
+ --primary-foreground: 0 0% 98%;
+
+ --secondary: 240 4.8% 95.9%;
+ --secondary-foreground: 240 5.9% 10%;
+
+ --accent: 240 4.8% 95.9%;
+ --accent-foreground: ;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 0 0% 98%;
+
+ --ring: 240 5% 64.9%;
+
+ --radius: 0.5rem;
}
- body,
- html {
- height: 100%;
+ .dark {
+ --background: 240 10% 3.9%;
+ --foreground: 0 0% 98%;
+
+ --muted: 240 3.7% 15.9%;
+ --muted-foreground: 240 5% 64.9%;
+
+ --popover: 240 10% 3.9%;
+ --popover-foreground: 0 0% 98%;
+
+ --card: 240 10% 3.9%;
+ --card-foreground: 0 0% 98%;
+
+ --border: 240 3.7% 15.9%;
+ --input: 240 3.7% 15.9%;
+
+ --primary: 0 0% 98%;
+ --primary-foreground: 240 5.9% 10%;
+
+ --secondary: 240 3.7% 15.9%;
+ --secondary-foreground: 0 0% 98%;
+
+ --accent: 240 3.7% 15.9%;
+ --accent-foreground: ;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 0 85.7% 97.3%;
+
+ --ring: 240 3.7% 15.9%;
}
}
-pre:has(div.codeblock) {
- padding: 0;
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
}
diff --git a/app/layout.tsx b/app/layout.tsx
index b9e8d26..4d366d0 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,9 +1,11 @@
-import './globals.css'
import { Metadata } from 'next'
-import { ThemeProvider } from '@/components/theme-provider'
+import '@/app/globals.css'
import { fontMono, fontSans } from '@/lib/fonts'
import { cn } from '@/lib/utils'
+import { Header } from '@/components/header'
+import { TailwindIndicator } from '@/components/tailwind-indicator'
+import { ThemeProvider } from '@/components/theme-provider'
export const metadata: Metadata = {
title: {
@@ -28,22 +30,24 @@ interface RootLayoutProps {
export default function RootLayout({ children }: RootLayoutProps) {
return (
- <>
-
-
-
-
- {children}
- {/* */}
-
-
-
- >
+
+
+
+
+
+ {/* @ts-ignore */}
+
+ {children}
+
+
+
+
+
)
}
diff --git a/app/page.tsx b/app/page.tsx
index 0804ebc..ee1ff4c 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,18 +1,8 @@
-import { Chat } from './chat'
-import { Sidebar } from './sidebar'
-import { auth } from '@/auth'
+import { Chat } from '@/components/chat'
-export const runtime = 'edge'
+// export const runtime = 'edge'
export const preferredRegion = 'home'
export default async function IndexPage() {
- const session = await auth()
- return (
-
- )
+ return
}
diff --git a/app/prompt.tsx b/app/prompt.tsx
deleted file mode 100644
index 0c03997..0000000
--- a/app/prompt.tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-'use client'
-
-import { CornerDownLeft, RefreshCcw, StopCircle } from 'lucide-react'
-import { useState } from 'react'
-import Textarea from 'react-textarea-autosize'
-
-import { Button } from '@/components/ui/button'
-import { fontMessage } from '@/lib/fonts'
-import { useCmdEnterSubmit } from '@/lib/hooks/use-command-enter-submit'
-import { cn } from '@/lib/utils'
-
-export interface PromptProps {
- onSubmit: (value: string) => void
- onRefresh?: () => void
- onAbort?: () => void
- isLoading: boolean
-}
-
-export function Prompt({
- onSubmit,
- onRefresh,
- onAbort,
- isLoading
-}: PromptProps) {
- const [input, setInput] = useState('')
- const { formRef, onKeyDown } = useCmdEnterSubmit()
- return (
-
- )
-}
-
-Prompt.displayName = 'Prompt'
diff --git a/app/sidebar-item.tsx b/app/sidebar-item.tsx
deleted file mode 100644
index f0598e6..0000000
--- a/app/sidebar-item.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-'use client'
-
-import { cn } from '@/lib/utils'
-import Link from 'next/link'
-import { usePathname, useRouter } from 'next/navigation'
-import { MessageCircleIcon, Trash2Icon, Loader2Icon } from 'lucide-react'
-import { removeChat } from './actions'
-import { useTransition } from 'react'
-
-export function SidebarItem({
- title,
- href,
- id,
- userId
-}: {
- title: string
- href: string
- id: string
- userId: string
-}) {
- const pathname = usePathname()
- const router = useRouter()
- const active = pathname === href
- const [isPending, startTransition] = useTransition()
-
- if (!id) return null
-
- return (
-
-
-
- {title}
-
- {
- e.preventDefault()
- startTransition(() => {
- removeChat({ id, userId, path: href }).then(() => {
- router.push('/')
- })
- })
- }}
- >
- {isPending ? (
-
- ) : (
-
- )}
-
-
- )
-}
diff --git a/app/sidebar.tsx b/app/sidebar.tsx
deleted file mode 100644
index 4ba42f9..0000000
--- a/app/sidebar.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import { Login } from '@/components/ui/login'
-import { NextChatLogo } from '@/components/ui/nextchat-logo'
-import { UserMenu } from '@/components/ui/user-menu'
-import { kv } from '@vercel/kv'
-import { Chat } from '@/lib/types'
-import { cn } from '@/lib/utils'
-import { type Session } from '@auth/nextjs/types'
-import { Plus } from 'lucide-react'
-import Link from 'next/link'
-import { ExternalLink } from './external-link'
-import { SidebarItem } from './sidebar-item'
-
-export interface SidebarProps {
- session?: Session
- newChat?: boolean
-}
-
-export function Sidebar({ session, newChat }: SidebarProps) {
- return (
-
-
-
-
-
-
-
- Next.js Chatbot
-
-
- {session?.user ? : }
-
-
-
-
- New Chat
-
-
-
-
- {/* @ts-ignore */}
-
-
-
-
-
-
-
-
-
- )
-}
-
-Sidebar.displayName = 'Sidebar'
-
-async function SidebarList({ session }: { session?: Session }) {
- const results: Chat[] = await getChats(session?.user?.email ?? '')
-
- return results.map(c => (
-
- ))
-}
-
-async function getChats(userId: string) {
- try {
- const pipeline = kv.pipeline()
- const chats: string[] = await kv.zrange(`user:chat:${userId}`, 0, -1)
-
- for (const chat of chats) {
- pipeline.hgetall(chat)
- }
-
- const results = await pipeline.exec()
-
- return results as Chat[]
- } catch (error) {
- return []
- }
-}
diff --git a/auth.ts b/auth.ts
index a55a7da..aee24e3 100644
--- a/auth.ts
+++ b/auth.ts
@@ -1,27 +1,27 @@
-import NextAuth from "@auth/nextjs";
-import GitHub from "@auth/nextjs/providers/github";
-import { NextResponse } from "next/server";
+import { NextResponse } from 'next/server'
+import NextAuth from '@auth/nextjs'
+import GitHub from '@auth/nextjs/providers/github'
export const {
handlers: { GET, POST },
auth,
- CSRF_experimental,
+ CSRF_experimental
} = NextAuth({
// @ts-ignore
providers: [GitHub],
- session: { strategy: "jwt" },
+ session: { strategy: 'jwt' },
async authorized({ request, auth }: any) {
- const url = request.nextUrl;
+ const url = request.nextUrl
- if (request.method === "POST") {
- const { authToken } = (await request.json()) ?? {};
+ if (request.method === 'POST') {
+ const { authToken } = (await request.json()) ?? {}
// If the request has a valid auth token, it is authorized
- const valid = true;
- if (valid) return true;
- return NextResponse.json("Invalid auth token", { status: 401 });
+ const valid = true
+ if (valid) return true
+ return NextResponse.json('Invalid auth token', { status: 401 })
}
// Logged in users are authenticated, otherwise redirect to login page
- return !!auth;
- },
-});
+ return !!auth
+ }
+})
diff --git a/components/button-scroll-to-bottom.tsx b/components/button-scroll-to-bottom.tsx
new file mode 100644
index 0000000..9ec0659
--- /dev/null
+++ b/components/button-scroll-to-bottom.tsx
@@ -0,0 +1,48 @@
+'use client'
+
+import * as React from 'react'
+import { ArrowDown } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+import { Button, type ButtonProps } from '@/components/ui/button'
+
+export function ButtonScrollToBottom({ className, ...props }: ButtonProps) {
+ const [isAtBottom, setIsAtBottom] = React.useState(false)
+
+ React.useEffect(() => {
+ const handleScroll = () => {
+ setIsAtBottom(
+ window.innerHeight + window.scrollY > document.body.offsetHeight - 100
+ )
+ }
+
+ window.addEventListener('scroll', handleScroll, { passive: true })
+ handleScroll()
+
+ return () => {
+ window.removeEventListener('scroll', handleScroll)
+ }
+ }, [])
+
+ return (
+
+ window.scrollTo({
+ top: document.body.offsetHeight,
+ behavior: 'smooth'
+ })
+ }
+ {...props}
+ >
+
+ Scroll to bottom
+
+ )
+}
diff --git a/components/chat-list.tsx b/components/chat-list.tsx
new file mode 100644
index 0000000..808fd90
--- /dev/null
+++ b/components/chat-list.tsx
@@ -0,0 +1,28 @@
+'use client'
+
+import { type Message } from 'ai-connector'
+
+import { Separator } from '@/components/ui/separator'
+import { ChatMessage } from '@/components/chat-message'
+import { EmptyScreen } from '@/components/empty-screen'
+
+export interface ChatList {
+ messages: Message[]
+}
+
+export function ChatList({ messages }: ChatList) {
+ return (
+
+ {messages.length > 0 ? (
+ messages.map((message, index) => (
+
+
+ {index < messages.length - 1 && }
+
+ ))
+ ) : (
+
+ )}
+
+ )
+}
diff --git a/components/chat-message.tsx b/components/chat-message.tsx
new file mode 100644
index 0000000..1de2707
--- /dev/null
+++ b/components/chat-message.tsx
@@ -0,0 +1,73 @@
+import { Message } from 'ai-connector'
+import { User } from 'lucide-react'
+import remarkGfm from 'remark-gfm'
+import remarkMath from 'remark-math'
+
+import { cn } from '@/lib/utils'
+import { CodeBlock } from '@/components/ui/codeblock'
+import { OpenAI } from '@/components/icons'
+import { MemoizedReactMarkdown } from '@/components/markdown'
+
+export interface ChatMessageProps {
+ message: Message
+}
+
+export function ChatMessage({ message, ...props }: ChatMessageProps) {
+ return (
+
+
+ {message.role === 'user' ? (
+
+ ) : (
+
+ )}
+
+
+ {children}
+ },
+ code({ node, inline, className, children, ...props }) {
+ if (children.length) {
+ if (children[0] == '▍') {
+ return (
+ ▍
+ )
+ }
+
+ children[0] = (children[0] as string).replace('`▍`', '▍')
+ }
+
+ const match = /language-(\w+)/.exec(className || '')
+
+ return !inline ? (
+
+ ) : (
+
+ {children}
+
+ )
+ }
+ }}
+ >
+ {message.content}
+
+
+
+ )
+}
diff --git a/components/chat-panel.tsx b/components/chat-panel.tsx
new file mode 100644
index 0000000..f1fa3b2
--- /dev/null
+++ b/components/chat-panel.tsx
@@ -0,0 +1,75 @@
+'use client'
+
+import { UseChatHelpers } from 'ai-connector'
+import { RefreshCcw, StopCircle } from 'lucide-react'
+
+import { Button } from '@/components/ui/button'
+import { ExternalLink } from '@/components/external-link'
+import { PromptForm } from '@/components/prompt-form'
+import { ButtonScrollToBottom } from '@/components/button-scroll-to-bottom'
+
+export interface ChatPanelProps
+ extends Pick<
+ UseChatHelpers,
+ 'append' | 'isLoading' | 'reload' | 'messages' | 'stop'
+ > {
+ id?: string
+}
+
+export function ChatPanel({
+ isLoading,
+ stop,
+ append,
+ reload,
+ messages
+}: ChatPanelProps) {
+ return (
+
+
+
+
+ {isLoading ? (
+ stop()}
+ className="bg-background"
+ >
+
+ Stop generating
+
+ ) : (
+ messages?.length > 0 && (
+ reload()}
+ className="bg-background"
+ >
+
+ Regenerate response
+
+ )
+ )}
+
+
+
{
+ append({
+ content: value,
+ role: 'user'
+ })
+ }}
+ isLoading={isLoading}
+ />
+
+ Open source AI chatbot app built with{' '}
+ Next.js and{' '}
+
+ Vercel KV
+
+ .
+
+
+
+
+ )
+}
diff --git a/components/chat.tsx b/components/chat.tsx
new file mode 100644
index 0000000..c7fff5d
--- /dev/null
+++ b/components/chat.tsx
@@ -0,0 +1,33 @@
+'use client'
+
+import { useChat, type Message } from 'ai-connector'
+
+import { ChatList } from '@/components/chat-list'
+import { ChatPanel } from '@/components/chat-panel'
+
+export interface ChatProps {
+ // create?: (input: string) => Chat | undefined;
+ initialMessages?: Message[]
+ id?: string
+}
+
+export function Chat({ id, initialMessages }: ChatProps) {
+ const { messages, append, reload, stop, isLoading } = useChat({
+ initialMessages,
+ id
+ })
+
+ return (
+
+
+
+
+ )
+}
diff --git a/components/empty-screen.tsx b/components/empty-screen.tsx
new file mode 100644
index 0000000..88a6d29
--- /dev/null
+++ b/components/empty-screen.tsx
@@ -0,0 +1,57 @@
+import { ArrowRight } from 'lucide-react'
+
+import { useChatStore } from '@/lib/hooks/use-chat-store'
+import { cn } from '@/lib/utils'
+import { Button } from '@/components/ui/button'
+import { ExternalLink } from '@/components/external-link'
+
+const exampleMessages = [
+ {
+ heading: 'Explain technical concepts',
+ message: `What is a "serverless function"?`
+ },
+ {
+ heading: 'Summarize an article',
+ message: 'Summarize the following article for a 2nd grader: \n'
+ },
+ {
+ heading: 'Draft an email',
+ message: `Draft an email to my boss about the following: \n`
+ }
+]
+
+export function EmptyScreen({ className }: React.ComponentProps<'div'>) {
+ const { setDefaultMessage } = useChatStore()
+
+ return (
+
+
+ Welcome to Next.js Chatbot!
+
+
+ This is an open source AI chatbot app built with{' '}
+ Next.js and{' '}
+
+ Vercel KV
+
+ .
+
+
+ You can start a conversation here or try the following examples:
+
+
+ {exampleMessages.map((message, index) => (
+
setDefaultMessage(message.message)}
+ >
+
+ {message.heading}
+
+ ))}
+
+
+ )
+}
diff --git a/app/external-link.tsx b/components/external-link.tsx
similarity index 87%
rename from app/external-link.tsx
rename to components/external-link.tsx
index 175cd6b..ba6cc01 100644
--- a/app/external-link.tsx
+++ b/components/external-link.tsx
@@ -9,7 +9,7 @@ export function ExternalLink({
{children}
+
+ {/* @ts-ignore */}
+
+ }>
+ {/* @ts-ignore */}
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/app/icon.png b/components/icon.png
similarity index 100%
rename from app/icon.png
rename to components/icon.png
diff --git a/components/icons.tsx b/components/icons.tsx
new file mode 100644
index 0000000..b94d72f
--- /dev/null
+++ b/components/icons.tsx
@@ -0,0 +1,160 @@
+'use client'
+
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+export function NextChat({
+ className,
+ inverted,
+ ...props
+}: React.ComponentProps<'svg'> & { inverted?: boolean }) {
+ const id = React.useId()
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+export function OpenAI({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+ OpenAI icon
+
+
+ )
+}
+
+export function Vercel({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
+
+export function GitHub({ className, ...props }: React.ComponentProps<'svg'>) {
+ return (
+
+ GitHub
+
+
+ )
+}
+
+export function Separator({
+ className,
+ ...props
+}: React.ComponentProps<'svg'>) {
+ return (
+
+
+
+ )
+}
diff --git a/components/prompt-form.tsx b/components/prompt-form.tsx
new file mode 100644
index 0000000..e90cca8
--- /dev/null
+++ b/components/prompt-form.tsx
@@ -0,0 +1,99 @@
+'use client'
+
+import * as React from 'react'
+import Link from 'next/link'
+import { CornerDownLeft, Plus } from 'lucide-react'
+import Textarea from 'react-textarea-autosize'
+
+import { useChatStore } from '@/lib/hooks/use-chat-store'
+import { useEnterSubmit } from '@/lib/hooks/use-enter-submit'
+import { cn } from '@/lib/utils'
+import { Button, buttonVariants } from '@/components/ui/button'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger
+} from '@/components/ui/tooltip'
+
+export interface PromptProps {
+ onSubmit: (value: string) => void
+ isLoading: boolean
+}
+
+export function PromptForm({ onSubmit, isLoading }: PromptProps) {
+ const { defaultMessage } = useChatStore()
+ const [input, setInput] = React.useState(defaultMessage)
+ const { formRef, onKeyDown } = useEnterSubmit()
+ const inputRef = React.useRef(null)
+
+ React.useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus()
+ }
+ }, [])
+
+ React.useEffect(() => {
+ setInput(defaultMessage)
+ }, [defaultMessage])
+
+ return (
+ {
+ e.preventDefault()
+ if (input === '') {
+ return
+ }
+ setInput('')
+ await onSubmit(input)
+ }}
+ ref={formRef}
+ >
+
+
+
+
+
+
+ New Chat
+
+
+ New Chat
+
+
setInput(e.target.value)}
+ placeholder="Send a message."
+ spellCheck={false}
+ className="min-h-[60px] w-full resize-none bg-transparent px-4 py-[1.3rem] focus-within:outline-none sm:text-sm"
+ />
+
+
+
+
+
+ Send message
+
+
+ Send message
+
+
+
+
+
+ )
+}
diff --git a/components/sidebar-item.tsx b/components/sidebar-item.tsx
new file mode 100644
index 0000000..55f55a6
--- /dev/null
+++ b/components/sidebar-item.tsx
@@ -0,0 +1,104 @@
+'use client'
+
+import * as React from 'react'
+import { useTransition } from 'react'
+import Link from 'next/link'
+import { usePathname, useRouter } from 'next/navigation'
+import { Loader2Icon, MessageSquare, Trash2Icon } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle
+} from '@/components/ui/alert-dialog'
+import { Button, buttonVariants } from '@/components/ui/button'
+import { removeChat } from '@/app/actions'
+
+export function SidebarItem({
+ title,
+ href,
+ id,
+ userId
+}: {
+ title: string
+ href: string
+ id: string
+ userId: string
+}) {
+ const [open, setIsOpen] = React.useState(false)
+ const pathname = usePathname()
+ const router = useRouter()
+ const isActive = pathname === href
+ const [isPending, startTransition] = useTransition()
+
+ if (!id) return null
+
+ return (
+ <>
+
+
+
+ {title}
+
+ {isActive && (
+ setIsOpen(true)}
+ >
+
+ Delete
+
+ )}
+
+
+
+
+ Are you absolutely sure?
+
+ This will permanently delete your chat message and remove your
+ data from our servers.
+
+
+
+ Cancel
+ {
+ event.preventDefault()
+ startTransition(async () => {
+ await removeChat({ id, userId, path: href })
+ setIsOpen(false)
+ router.push('/')
+ })
+ }}
+ >
+ {isPending && (
+
+ )}
+ Delete
+
+
+
+
+ >
+ )
+}
diff --git a/components/sidebar-list.tsx b/components/sidebar-list.tsx
new file mode 100644
index 0000000..22c405e
--- /dev/null
+++ b/components/sidebar-list.tsx
@@ -0,0 +1,40 @@
+import { getChats } from '@/app/actions'
+import { Session } from '@auth/core/types'
+import { SidebarItem } from './sidebar-item'
+
+export interface SidebarListProps {
+ session?: Session
+}
+
+export async function SidebarList(props: SidebarListProps) {
+ const chats = await getChats(props.session?.user?.email)
+ return (
+
+ {chats?.length ? (
+
+ {chats.map(chat => (
+
+ ))}
+
+ ) : (
+
+
+ {props?.session?.user ? (
+ <>No chat history>
+ ) : (
+ <>Login for history>
+ )}
+
+
+ )}
+
+ )
+}
+
+SidebarList.displayName = 'SidebarList'
diff --git a/components/sidebar.tsx b/components/sidebar.tsx
new file mode 100644
index 0000000..66d7a27
--- /dev/null
+++ b/components/sidebar.tsx
@@ -0,0 +1,55 @@
+'use client'
+
+import * as React from 'react'
+import { signOut } from '@auth/nextjs/client'
+import { type Session } from '@auth/nextjs/types'
+import { Sidebar as SidebarIcon } from 'lucide-react'
+
+import { Button } from '@/components/ui/button'
+import {
+ Sheet,
+ SheetContent,
+ SheetHeader,
+ SheetTitle,
+ SheetTrigger
+} from '@/components/ui/sheet'
+import { ThemeToggle } from '@/components/theme-toggle'
+
+export interface SidebarProps {
+ session?: Session
+ children?: React.ReactNode
+}
+
+export function Sidebar({ session, children }: SidebarProps) {
+ return (
+
+
+
+
+ Toggle Sidebar
+
+
+
+
+ Chat History
+
+ {children}
+
+
+ {session?.user && (
+ signOut()}
+ className="ml-auto"
+ >
+ Logout
+
+ )}
+
+
+
+ )
+}
diff --git a/components/tailwind-indicator.tsx b/components/tailwind-indicator.tsx
new file mode 100644
index 0000000..f2a1291
--- /dev/null
+++ b/components/tailwind-indicator.tsx
@@ -0,0 +1,14 @@
+export function TailwindIndicator() {
+ if (process.env.NODE_ENV === 'production') return null
+
+ return (
+
+
xs
+
sm
+
md
+
lg
+
xl
+
2xl
+
+ )
+}
diff --git a/components/theme-toggle.tsx b/components/theme-toggle.tsx
index 5493bd6..56f559d 100644
--- a/components/theme-toggle.tsx
+++ b/components/theme-toggle.tsx
@@ -1,33 +1,31 @@
'use client'
+import * as React from 'react'
+import { Moon, Sun } from 'lucide-react'
import { useTheme } from 'next-themes'
import { Button } from '@/components/ui/button'
-import { Moon, Sun } from 'lucide-react'
-import { useTransition } from 'react'
export function ThemeToggle() {
const { setTheme, theme } = useTheme()
- const [_, startTransition] = useTransition()
+ const [_, startTransition] = React.useTransition()
+
return (
{
startTransition(() => {
setTheme(theme === 'light' ? 'dark' : 'light')
})
}}
>
-
- Toggle theme
- {!theme ? null : theme === 'dark' ? (
-
- ) : (
-
- )}
-
+ {!theme ? null : theme === 'dark' ? (
+
+ ) : (
+
+ )}
+ Toggle theme
)
}
diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..17fec4d
--- /dev/null
+++ b/components/ui/alert-dialog.tsx
@@ -0,0 +1,150 @@
+'use client'
+
+import * as React from 'react'
+import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'
+
+import { cn } from '@/lib/utils'
+import { buttonVariants } from '@/components/ui/button'
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = ({
+ className,
+ children,
+ ...props
+}: AlertDialogPrimitive.AlertDialogPortalProps) => (
+
+
+ {children}
+
+
+)
+AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogHeader.displayName = 'AlertDialogHeader'
+
+const AlertDialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogFooter.displayName = 'AlertDialogFooter'
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogDescription.displayName =
+ AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+ AlertDialog,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel
+}
diff --git a/components/ui/button.tsx b/components/ui/button.tsx
index 788f113..4dd6924 100644
--- a/components/ui/button.tsx
+++ b/components/ui/button.tsx
@@ -1,28 +1,30 @@
import * as React from 'react'
-import { VariantProps, cva } from 'class-variance-authority'
+import { Slot } from '@radix-ui/react-slot'
+import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
- 'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
+ 'inline-flex items-center justify-center rounded-md text-sm font-medium shadow ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
- default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ default:
+ 'bg-primary text-primary-foreground shadow-md hover:bg-primary/90',
destructive:
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline:
'border border-input hover:bg-accent hover:text-accent-foreground',
secondary:
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
- ghost: 'hover:bg-accent hover:text-accent-foreground',
- link: 'underline-offset-4 hover:underline text-primary'
+ ghost: 'shadow-none hover:bg-accent hover:text-accent-foreground',
+ link: 'text-primary underline-offset-4 shadow-none hover:underline'
},
size: {
- default: 'h-10 py-2 px-4',
- sm: 'h-9 px-3 rounded-md',
- lg: 'h-11 px-8 rounded-md',
- xs: 'h-6 px-3 rounded-md'
+ default: 'h-8 px-4 py-2',
+ sm: 'h-8 rounded-md px-3',
+ lg: 'h-11 rounded-md px-8',
+ icon: 'h-8 w-8 p-0'
}
},
defaultVariants: {
@@ -34,12 +36,15 @@ const buttonVariants = cva(
export interface ButtonProps
extends React.ButtonHTMLAttributes,
- VariantProps {}
+ VariantProps {
+ asChild?: boolean
+}
const Button = React.forwardRef(
- ({ className, variant, size, ...props }, ref) => {
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button'
return (
- = memo(({ language, value }) => {
})
CodeBlock.displayName = 'CodeBlock'
-export default CodeBlock
+export { CodeBlock }
diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx
new file mode 100644
index 0000000..3abd062
--- /dev/null
+++ b/components/ui/dialog.tsx
@@ -0,0 +1,128 @@
+'use client'
+
+import * as React from 'react'
+import * as DialogPrimitive from '@radix-ui/react-dialog'
+import { X } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+const Dialog = DialogPrimitive.Root
+
+const DialogTrigger = DialogPrimitive.Trigger
+
+const DialogPortal = ({
+ className,
+ children,
+ ...props
+}: DialogPrimitive.DialogPortalProps) => (
+
+
+ {children}
+
+
+)
+DialogPortal.displayName = DialogPrimitive.Portal.displayName
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+DialogContent.displayName = DialogPrimitive.Content.displayName
+
+const DialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogHeader.displayName = 'DialogHeader'
+
+const DialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+DialogFooter.displayName = 'DialogFooter'
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogTitle.displayName = DialogPrimitive.Title.displayName
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DialogDescription.displayName = DialogPrimitive.Description.displayName
+
+export {
+ Dialog,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription
+}
diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx
new file mode 100644
index 0000000..4d5ed21
--- /dev/null
+++ b/components/ui/dropdown-menu.tsx
@@ -0,0 +1,200 @@
+'use client'
+
+import * as React from 'react'
+import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
+import { Check, ChevronRight, Circle } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+const DropdownMenu = DropdownMenuPrimitive.Root
+
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+
+const DropdownMenuGroup = DropdownMenuPrimitive.Group
+
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+
+const DropdownMenuSub = DropdownMenuPrimitive.Sub
+
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+
+const DropdownMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+))
+DropdownMenuSubTrigger.displayName =
+ DropdownMenuPrimitive.SubTrigger.displayName
+
+const DropdownMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSubContent.displayName =
+ DropdownMenuPrimitive.SubContent.displayName
+
+const DropdownMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+
+const DropdownMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+
+const DropdownMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuCheckboxItem.displayName =
+ DropdownMenuPrimitive.CheckboxItem.displayName
+
+const DropdownMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+
+const DropdownMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+
+const DropdownMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+
+const DropdownMenuShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'
+
+export {
+ DropdownMenu,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuGroup,
+ DropdownMenuPortal,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuRadioGroup
+}
diff --git a/components/ui/input.tsx b/components/ui/input.tsx
new file mode 100644
index 0000000..684a857
--- /dev/null
+++ b/components/ui/input.tsx
@@ -0,0 +1,25 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+export interface InputProps
+ extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Input.displayName = 'Input'
+
+export { Input }
diff --git a/components/ui/login.tsx b/components/ui/login.tsx
deleted file mode 100644
index 29ad1ae..0000000
--- a/components/ui/login.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-'use client'
-
-import { signIn } from '@auth/nextjs/client'
-
-export function Login() {
- return (
- signIn('github')}
- >
- Login
-
- )
-}
diff --git a/components/ui/nextchat-logo.tsx b/components/ui/nextchat-logo.tsx
deleted file mode 100644
index c092a6e..0000000
--- a/components/ui/nextchat-logo.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { useId } from 'react'
-
-export function NextChatLogo(
- props: JSX.IntrinsicElements['svg'] & { inverted?: boolean }
-) {
- const id = useId()
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- )
-}
-
-NextChatLogo.displayName = 'DevGPT-logo'
diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx
new file mode 100644
index 0000000..6c55e0b
--- /dev/null
+++ b/components/ui/separator.tsx
@@ -0,0 +1,31 @@
+'use client'
+
+import * as React from 'react'
+import * as SeparatorPrimitive from '@radix-ui/react-separator'
+
+import { cn } from '@/lib/utils'
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = 'horizontal', decorative = true, ...props },
+ ref
+ ) => (
+
+ )
+)
+Separator.displayName = SeparatorPrimitive.Root.displayName
+
+export { Separator }
diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx
new file mode 100644
index 0000000..655c1f6
--- /dev/null
+++ b/components/ui/sheet.tsx
@@ -0,0 +1,145 @@
+'use client'
+
+import * as React from 'react'
+import * as SheetPrimitive from '@radix-ui/react-dialog'
+import { cva, type VariantProps } from 'class-variance-authority'
+import { X } from 'lucide-react'
+
+import { cn } from '@/lib/utils'
+
+const Sheet = SheetPrimitive.Root
+
+const SheetTrigger = SheetPrimitive.Trigger
+
+const SheetClose = SheetPrimitive.Close
+
+const SheetPortal = ({
+ className,
+ children,
+ ...props
+}: SheetPrimitive.DialogPortalProps) => (
+
+ {children}
+
+)
+SheetPortal.displayName = SheetPrimitive.Portal.displayName
+
+const SheetOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+))
+SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
+
+const sheetVariants = cva(
+ 'fixed z-50 h-full scale-100 gap-4 border bg-background p-6 opacity-100 shadow-lg',
+ {
+ variants: {
+ position: {
+ left: 'data-[state=closed]:animate-slide-to-left data-[state=open]:animate-slide-from-left',
+ right: 'h-full animate-in slide-in-from-right duration-300'
+ }
+ },
+ defaultVariants: {
+ position: 'left'
+ }
+ }
+)
+
+export interface DialogContentProps
+ extends React.ComponentPropsWithoutRef,
+ VariantProps {}
+
+const SheetContent = React.forwardRef<
+ React.ElementRef,
+ DialogContentProps
+>(({ position, className, children, ...props }, ref) => (
+
+
+ {children}
+
+
+ Close
+
+
+
+))
+SheetContent.displayName = SheetPrimitive.Content.displayName
+
+const SheetHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetHeader.displayName = 'SheetHeader'
+
+const SheetFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+SheetFooter.displayName = 'SheetFooter'
+
+const SheetTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetTitle.displayName = SheetPrimitive.Title.displayName
+
+const SheetDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SheetDescription.displayName = SheetPrimitive.Description.displayName
+
+export {
+ Sheet,
+ SheetTrigger,
+ SheetClose,
+ SheetContent,
+ SheetHeader,
+ SheetFooter,
+ SheetTitle,
+ SheetDescription
+}
diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx
new file mode 100644
index 0000000..e25af72
--- /dev/null
+++ b/components/ui/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react'
+
+import { cn } from '@/lib/utils'
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes {}
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ )
+ }
+)
+Textarea.displayName = 'Textarea'
+
+export { Textarea }
diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx
new file mode 100644
index 0000000..32a9ddc
--- /dev/null
+++ b/components/ui/tooltip.tsx
@@ -0,0 +1,30 @@
+'use client'
+
+import * as React from 'react'
+import * as TooltipPrimitive from '@radix-ui/react-tooltip'
+
+import { cn } from '@/lib/utils'
+
+const TooltipProvider = TooltipPrimitive.Provider
+
+const Tooltip = TooltipPrimitive.Root
+
+const TooltipTrigger = TooltipPrimitive.Trigger
+
+const TooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+))
+TooltipContent.displayName = TooltipPrimitive.Content.displayName
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
diff --git a/components/ui/user-menu.tsx b/components/ui/user-menu.tsx
deleted file mode 100644
index e8f5357..0000000
--- a/components/ui/user-menu.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-'use client'
-import { ThemeToggle } from '@/components/theme-toggle'
-import { signOut } from '@auth/nextjs/client'
-import { type Session } from '@auth/nextjs/types'
-import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
-import Image from 'next/image'
-
-export interface UserMenuProps {
- session: Session
-}
-
-export function UserMenu({ session }: UserMenuProps) {
- return (
-
-
-
-
- {session.user?.image ? (
-
- ) : (
-
-
- {session.user?.name ? session.user?.name.slice(0, 2) : null}
-
-
- )}
-
-
-
-
-
-
- {session.user?.name}
- {session.user?.email}
-
-
-
-
-
-
-
-
- Vercel Homepage
-
-
-
-
-
-
-
-
-
- signOut()}
- >
- Log Out
-
-
-
-
-
- )
-}
-
-UserMenu.displayName = 'UserMenu'
diff --git a/components/ui/vercel-logo.tsx b/components/ui/vercel-logo.tsx
deleted file mode 100644
index d25b96d..0000000
--- a/components/ui/vercel-logo.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { cn } from '@/lib/utils'
-
-export const VercelLogo = ({ className }: { className?: string }) => (
-
-
-
-)
diff --git a/components/user-menu.tsx b/components/user-menu.tsx
new file mode 100644
index 0000000..8650c1c
--- /dev/null
+++ b/components/user-menu.tsx
@@ -0,0 +1,37 @@
+'use client'
+
+import * as React from 'react'
+import { signIn } from '@auth/nextjs/client'
+import { type Session } from '@auth/nextjs/types'
+import { Loader2Icon } from 'lucide-react'
+
+import { Button } from '@/components/ui/button'
+
+export interface UserMenuProps {
+ session?: Session
+}
+
+export function UserMenu({ session }: UserMenuProps) {
+ const [isLoading, setIsLoading] = React.useState(false)
+
+ if (!session?.user) {
+ return (
+ {
+ setIsLoading(true)
+ signIn('github')
+ }}
+ disabled={isLoading}
+ >
+ {isLoading && }
+ Login
+
+ )
+ }
+
+ return (
+ Logged in as {session.user.name}
+ )
+}
diff --git a/lib/analytics.ts b/lib/analytics.ts
index 5eead76..07a10c5 100644
--- a/lib/analytics.ts
+++ b/lib/analytics.ts
@@ -1,5 +1,5 @@
-import type { NextRequest, NextFetchEvent } from 'next/server'
import type { NextApiRequest } from 'next'
+import type { NextFetchEvent, NextRequest } from 'next/server'
export const initAnalytics = ({
request,
diff --git a/lib/fonts.ts b/lib/fonts.ts
index 0ba1393..cf02806 100644
--- a/lib/fonts.ts
+++ b/lib/fonts.ts
@@ -1,20 +1,10 @@
-import {
- JetBrains_Mono as FontMono,
- IBM_Plex_Sans as FontMessage,
- Inter as FontSans
-} from 'next/font/google'
+import { JetBrains_Mono as FontMono, Inter as FontSans } from 'next/font/google'
export const fontSans = FontSans({
subsets: ['latin'],
variable: '--font-sans'
})
-export const fontMessage = FontMessage({
- subsets: ['latin'],
- weight: ['400', '500', '600'],
- variable: '--font-message'
-})
-
export const fontMono = FontMono({
subsets: ['latin'],
variable: '--font-mono'
diff --git a/lib/hooks/use-chat-store.ts b/lib/hooks/use-chat-store.ts
new file mode 100644
index 0000000..e6e913c
--- /dev/null
+++ b/lib/hooks/use-chat-store.ts
@@ -0,0 +1,11 @@
+import { create } from 'zustand'
+
+interface UseChatStore {
+ defaultMessage: string
+ setDefaultMessage: (message: string) => void
+}
+
+export const useChatStore = create()(set => ({
+ defaultMessage: '',
+ setDefaultMessage: message => set({ defaultMessage: message })
+}))
diff --git a/lib/hooks/use-copy-to-cliipboard.tsx b/lib/hooks/use-copy-to-cliipboard.tsx
index df4d1f8..748a9bc 100644
--- a/lib/hooks/use-copy-to-cliipboard.tsx
+++ b/lib/hooks/use-copy-to-cliipboard.tsx
@@ -1,4 +1,5 @@
'use client'
+
import { useState } from 'react'
export interface useCopyToClipboardProps {
diff --git a/lib/hooks/use-command-enter-submit.tsx b/lib/hooks/use-enter-submit.tsx
similarity index 68%
rename from lib/hooks/use-command-enter-submit.tsx
rename to lib/hooks/use-enter-submit.tsx
index 61e796c..5091ddf 100644
--- a/lib/hooks/use-command-enter-submit.tsx
+++ b/lib/hooks/use-enter-submit.tsx
@@ -1,7 +1,6 @@
-import type { RefObject } from 'react'
-import { useRef } from 'react'
+import { useRef, type RefObject } from 'react'
-export function useCmdEnterSubmit(): {
+export function useEnterSubmit(): {
formRef: RefObject
onKeyDown: (event: React.KeyboardEvent) => void
} {
@@ -10,8 +9,9 @@ export function useCmdEnterSubmit(): {
const handleKeyDown = (
event: React.KeyboardEvent
): void => {
- if (event.key === 'Enter') {
+ if (event.key === 'Enter' && !event.shiftKey) {
formRef.current?.requestSubmit()
+ event.preventDefault()
}
}
diff --git a/lib/hooks/use-window-size.ts b/lib/hooks/use-window-size.ts
index 5bb92ce..9a3661d 100644
--- a/lib/hooks/use-window-size.ts
+++ b/lib/hooks/use-window-size.ts
@@ -1,4 +1,5 @@
'use client'
+
import { useEffect, useState } from 'react'
export default function useWindowSize() {
diff --git a/middleware.ts b/middleware.ts
index 2b96876..4afe145 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -1 +1 @@
-export { auth as middleware } from "./auth";
+export { auth as middleware } from './auth'
diff --git a/package.json b/package.json
index 20544cd..6948936 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,12 @@
"dependencies": {
"@auth/core": "0.0.0-manual.527fff6c",
"@auth/nextjs": "0.0.0-manual.030e8328",
+ "@radix-ui/react-alert-dialog": "^1.0.4",
+ "@radix-ui/react-dialog": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.4",
+ "@radix-ui/react-separator": "^1.0.3",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-tooltip": "^1.0.6",
"@vercel/analytics": "^1.0.0",
"@vercel/kv": "^0.2.1",
"ai-connector": "^0.0.5",
@@ -26,7 +31,7 @@
"focus-trap-react": "^10.1.1",
"lucide-react": "0.216.0",
"nanoid": "^4.0.2",
- "next": "13.4.5-canary.3",
+ "next": "13.4.5-canary.12",
"next-themes": "^0.2.1",
"openai-edge": "^0.5.1",
"react": "^18.2.0",
@@ -36,7 +41,8 @@
"react-syntax-highlighter": "^15.5.0",
"react-textarea-autosize": "^8.4.1",
"remark-gfm": "^3.0.1",
- "remark-math": "^5.1.1"
+ "remark-math": "^5.1.1",
+ "zustand": "^4.3.8"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.9",
@@ -44,30 +50,24 @@
"@types/react": "^18.0.22",
"@types/react-dom": "^18.0.7",
"@types/react-syntax-highlighter": "^15.5.6",
+ "@typescript-eslint/parser": "^5.59.7",
"autoprefixer": "^10.4.13",
"dotenv": "^16.0.3",
"drizzle-kit": "^0.18.0",
"eslint": "^8.31.0",
- "eslint-config-next": "13.4.5-canary.3",
+ "eslint-config-next": "13.4.5-canary.12",
"eslint-config-prettier": "^8.3.0",
+ "eslint-plugin-tailwindcss": "^3.12.0",
"postcss": "^8.4.21",
"prettier": "^2.7.1",
- "tailwindcss": "^3.3.1",
"tailwind-merge": "^1.12.0",
+ "tailwindcss": "^3.3.1",
"tailwindcss-animate": "^1.0.5",
- "typescript": "^4.9.3"
+ "typescript": "^5.1.3"
},
"pnpm": {
"overrides": {
"@auth/core": "0.0.0-manual.527fff6c"
}
- },
- "prettier": {
- "tabWidth": 2,
- "semi": false,
- "useTabs": false,
- "singleQuote": true,
- "arrowParens": "avoid",
- "trailingComma": "none"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 289e875..d7a6db4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -13,10 +13,25 @@ dependencies:
version: 0.0.0-manual.527fff6c
'@auth/nextjs':
specifier: 0.0.0-manual.030e8328
- version: 0.0.0-manual.030e8328(next@13.4.5-canary.3)(react@18.2.0)
+ version: 0.0.0-manual.030e8328(next@13.4.5-canary.12)(react@18.2.0)
+ '@radix-ui/react-alert-dialog':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-dialog':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
'@radix-ui/react-dropdown-menu':
specifier: ^2.0.4
version: 2.0.4(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-separator':
+ specifier: ^1.0.3
+ version: 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot':
+ specifier: ^1.0.2
+ version: 1.0.2(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-tooltip':
+ specifier: ^1.0.6
+ version: 1.0.6(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
'@vercel/analytics':
specifier: ^1.0.0
version: 1.0.1
@@ -31,7 +46,7 @@ dependencies:
version: 4.0.0-beta.0
class-variance-authority:
specifier: ^0.4.0
- version: 0.4.0(typescript@4.9.5)
+ version: 0.4.0(typescript@5.1.3)
clsx:
specifier: ^1.2.1
version: 1.2.1
@@ -45,11 +60,11 @@ dependencies:
specifier: ^4.0.2
version: 4.0.2
next:
- specifier: 13.4.5-canary.3
- version: 13.4.5-canary.3(react-dom@18.2.0)(react@18.2.0)
+ specifier: 13.4.5-canary.12
+ version: 13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0)
next-themes:
specifier: ^0.2.1
- version: 0.2.1(next@13.4.5-canary.3)(react-dom@18.2.0)(react@18.2.0)
+ version: 0.2.1(next@13.4.5-canary.12)(react-dom@18.2.0)(react@18.2.0)
openai-edge:
specifier: ^0.5.1
version: 0.5.1
@@ -77,6 +92,9 @@ dependencies:
remark-math:
specifier: ^5.1.1
version: 5.1.1
+ zustand:
+ specifier: ^4.3.8
+ version: 4.3.8(react@18.2.0)
devDependencies:
'@tailwindcss/typography':
@@ -94,6 +112,9 @@ devDependencies:
'@types/react-syntax-highlighter':
specifier: ^15.5.6
version: 15.5.6
+ '@typescript-eslint/parser':
+ specifier: ^5.59.7
+ version: 5.59.7(eslint@8.40.0)(typescript@5.1.3)
autoprefixer:
specifier: ^10.4.13
version: 10.4.14(postcss@8.4.23)
@@ -107,11 +128,14 @@ devDependencies:
specifier: ^8.31.0
version: 8.40.0
eslint-config-next:
- specifier: 13.4.5-canary.3
- version: 13.4.5-canary.3(eslint@8.40.0)(typescript@4.9.5)
+ specifier: 13.4.5-canary.12
+ version: 13.4.5-canary.12(eslint@8.40.0)(typescript@5.1.3)
eslint-config-prettier:
specifier: ^8.3.0
version: 8.8.0(eslint@8.40.0)
+ eslint-plugin-tailwindcss:
+ specifier: ^3.12.0
+ version: 3.12.0(tailwindcss@3.3.2)
postcss:
specifier: ^8.4.21
version: 8.4.23
@@ -128,8 +152,8 @@ devDependencies:
specifier: ^1.0.5
version: 1.0.5(tailwindcss@3.3.2)
typescript:
- specifier: ^4.9.3
- version: 4.9.5
+ specifier: ^5.1.3
+ version: 5.1.3
packages:
@@ -154,14 +178,14 @@ packages:
preact-render-to-string: 5.2.3(preact@10.11.3)
dev: false
- /@auth/nextjs@0.0.0-manual.030e8328(next@13.4.5-canary.3)(react@18.2.0):
+ /@auth/nextjs@0.0.0-manual.030e8328(next@13.4.5-canary.12)(react@18.2.0):
resolution: {integrity: sha512-AAtUl9EVCKqLRneDAq6KfUprWmsBRzsBoQ+8ytz/Nfgs8Uax5CWvCJs2CsnYNk04bXSB4L7eyMJmc1zTzZKaHg==}
peerDependencies:
next: ^13.4.2
react: ^18.2.0
dependencies:
'@auth/core': 0.0.0-manual.527fff6c
- next: 13.4.5-canary.3(react-dom@18.2.0)(react@18.2.0)
+ next: 13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
transitivePeerDependencies:
- nodemailer
@@ -232,12 +256,22 @@ packages:
resolution: {integrity: sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==}
dev: false
+ /@floating-ui/core@1.2.6:
+ resolution: {integrity: sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg==}
+ dev: false
+
/@floating-ui/dom@0.5.4:
resolution: {integrity: sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==}
dependencies:
'@floating-ui/core': 0.7.3
dev: false
+ /@floating-ui/dom@1.2.9:
+ resolution: {integrity: sha512-sosQxsqgxMNkV3C+3UqTS6LxP7isRLwX8WMepp843Rb3/b0Wz8+MdUkxJksByip3C2WwLugLHN1b4ibn//zKwQ==}
+ dependencies:
+ '@floating-ui/core': 1.2.6
+ dev: false
+
/@floating-ui/react-dom@0.7.2(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==}
peerDependencies:
@@ -252,6 +286,17 @@ packages:
- '@types/react'
dev: false
+ /@floating-ui/react-dom@2.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Ke0oU3SeuABC2C4OFu2mSAwHIP5WUiV98O9YWoHV4Q5aT6E9k06DV0Khi5uYspR8xmmBk08t8ZDcz3TR3ARkEg==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@floating-ui/dom': 1.2.9
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@humanwhocodes/config-array@0.11.8:
resolution: {integrity: sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==}
engines: {node: '>=10.10.0'}
@@ -306,18 +351,18 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
- /@next/env@13.4.5-canary.3:
- resolution: {integrity: sha512-2TrPvCIs8R9DpFpEDmGUOpWkVqbpb4+sGQMcZVP4zDYYg893p0nAbNbBxL996oBl+/vLTzcLIDceP5peHsGPzg==}
+ /@next/env@13.4.5-canary.12:
+ resolution: {integrity: sha512-2otdjrIdov9JJLphseW3+CAsSj98xculzYNm5GENNgRJanVBp+f2nlmLx+FDd+XJdcCfflMl9r8+qqTLaK3UWw==}
dev: false
- /@next/eslint-plugin-next@13.4.5-canary.3:
- resolution: {integrity: sha512-hsFfLSo8snWDLBwDT7+i4gEWZjsEzNHlZLT0ox8ftcZo3XBz8FrX11h9yGacageRbiiTotl/5U31Dxrt7MVoBA==}
+ /@next/eslint-plugin-next@13.4.5-canary.12:
+ resolution: {integrity: sha512-kLF31H90T69nhDBewjcF0amQfYhPDZwETCFYlCuL/h7PaNEw9YAiK/Z+14NUcwUPRsVJay6+2NAsyuvJ8UpsEQ==}
dependencies:
glob: 7.1.7
dev: true
- /@next/swc-darwin-arm64@13.4.5-canary.3:
- resolution: {integrity: sha512-mUlpzua0RDPLNfNeqi0DzWNCqVzMphNh42BS8PojVO3NPC55+RX9tllQngz+qFAOcLL7g4LgoeE9VWgh31s/WQ==}
+ /@next/swc-darwin-arm64@13.4.5-canary.12:
+ resolution: {integrity: sha512-qBTH4I81wDRzdkH+c026UBTPi8dvjzUCWQyOqa7PXIBeMv6y9wv2vIvrtCCms4FpyN3Z9HGDo7wgom347eW+3Q==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@@ -325,8 +370,8 @@ packages:
dev: false
optional: true
- /@next/swc-darwin-x64@13.4.5-canary.3:
- resolution: {integrity: sha512-0bkYuSFh1/0ZedCVsSMtHiSxdfQxF0BO/v5VwRdOLtOZE8WrQdoXsWiVsiFBOTutiZDhats9exFOFgmymoSqSA==}
+ /@next/swc-darwin-x64@13.4.5-canary.12:
+ resolution: {integrity: sha512-ZTCpA7QeUvImMAUCMOlpn8agtqNaWzmKrGfpbn/6xkBAdgyBPwv43rKxjb/JLFqR9pBMSizq2B4fuOO87ouA7g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@@ -334,8 +379,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-gnu@13.4.5-canary.3:
- resolution: {integrity: sha512-+PxhM2E98APKNEl1FCwhqU9ttGRoWsZcPPU20qcKFrJ/fKbfaWDnhzlSiZPmmERISZrmd54Uy3maNq9ys+bz6g==}
+ /@next/swc-linux-arm64-gnu@13.4.5-canary.12:
+ resolution: {integrity: sha512-zzKr25VJR0TrfEjt2fZgGjl6/oO2JtNT3QgHfzAroa7ZjKsfR6QL4N6m4AkCew3CuQm9FMSjVf9VfcwGJWWv4g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -343,8 +388,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-arm64-musl@13.4.5-canary.3:
- resolution: {integrity: sha512-KMRWPxeoZ41146NHpMAGMtD/RkQt/0QOlL13pREnNjeTzQI6P20X+ra5tHkCQoFB/8xseN9RXnuLMyYG3mYoGw==}
+ /@next/swc-linux-arm64-musl@13.4.5-canary.12:
+ resolution: {integrity: sha512-US2SJz1TQQWkHzbfX1F32UbZogsWR6c3C7+tCxt9X5yYK3eeesXO3rd5T65qvktUiI/F172PFR66A0YUsTDayA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@@ -352,8 +397,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-gnu@13.4.5-canary.3:
- resolution: {integrity: sha512-dwSxDddWMaTAmSPxSWWrwaHHPqLlnNJoc22dTfyQX9Hwecq+Sq2RGSkGicga3z6Z7rX3Nm4MbNduM2GWXwcvgg==}
+ /@next/swc-linux-x64-gnu@13.4.5-canary.12:
+ resolution: {integrity: sha512-Ztpq07X002LYcD/3zAN549i628Zs7d9H6e4BM9XJ18hfnsxRxwvUR9bSfhU5Pm8nzr4GHrz2GN+Xuge41D0gBw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -361,8 +406,8 @@ packages:
dev: false
optional: true
- /@next/swc-linux-x64-musl@13.4.5-canary.3:
- resolution: {integrity: sha512-R2SqJsC6QPNxO3t/U2fMouzLfUNLHP8PfZH/XTIwQ12dxZCwUwBg/cNX5ocBtoci3lCYNcOLNjSuc3z9b/yIeQ==}
+ /@next/swc-linux-x64-musl@13.4.5-canary.12:
+ resolution: {integrity: sha512-yACF+AK5uzOYHjrtIwEMqTQgkGHQhT6CiVUPD4sjMk7fxbX+j1dob4ZiAP49BwvXgaDRSn1tAWz21UTOt4GOjg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@@ -370,8 +415,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-arm64-msvc@13.4.5-canary.3:
- resolution: {integrity: sha512-/spv7kDp2xKUJzdvGFU2gf/N1lXtm/E2p/Fx/XreUyajiaT0f57d2mHfDabW5saq3KL3wPlWZ5zW7iDh2Awm+Q==}
+ /@next/swc-win32-arm64-msvc@13.4.5-canary.12:
+ resolution: {integrity: sha512-LxiT5+SDxYsbry5nidv7nMUhbFb9i3ZH6CH4QlIw94KVF73/dJ98IcWWIRemhDYTJuYuNKsYb86ZRTBywi/F1A==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@@ -379,8 +424,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-ia32-msvc@13.4.5-canary.3:
- resolution: {integrity: sha512-VIlQ+emO306CYbIyEKu8RTAzmMw/z+akEhu2urZPKjj+UMKCLfq1w4u3GJfURfdRiJ5ws7fLXDHVAjon4G+Ygg==}
+ /@next/swc-win32-ia32-msvc@13.4.5-canary.12:
+ resolution: {integrity: sha512-dP5sukEbbGH3ojxXjU+0Vtp6ZizSk1AyLlhs0Ps6n5WBU0XPhv3uFVH11Nn4qGK4kn4yoMxtsPL5pF/PQj+QvA==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@@ -388,8 +433,8 @@ packages:
dev: false
optional: true
- /@next/swc-win32-x64-msvc@13.4.5-canary.3:
- resolution: {integrity: sha512-JSb5haHmRxn2Jysrsw3UJ/mgNu0TD+JJdHyGkdQeGIM4V1Q1G/KgQNCTovWfimd8e1JUoZqSV4aX+y7zEDVkEg==}
+ /@next/swc-win32-x64-msvc@13.4.5-canary.12:
+ resolution: {integrity: sha512-4cOy2Owpiypyx8FbhQO4NsH0dyDwEornZ2UrH9auPMC6LU6fgelj4MTI/xi83D0pSjopbuX88wR1rmh1x7F9uw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@@ -440,6 +485,38 @@ packages:
'@babel/runtime': 7.21.5
dev: false
+ /@radix-ui/primitive@1.0.1:
+ resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
+ dependencies:
+ '@babel/runtime': 7.21.5
+ dev: false
+
+ /@radix-ui/react-alert-dialog@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-jbfBCRlKYlhbitueOAv7z74PXYeIQmWpKwm3jllsdkw7fGWNkxqP3v0nY9WmOzcPqpQuoorNtvViBgL46n5gVg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-dialog': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-arrow@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==}
peerDependencies:
@@ -452,6 +529,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-collection@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-s8WdQQ6wNXpaxdZ308KSr8fEWGrg4un8i4r/w7fhiS4ElRNjk5rRcl0/C6TANG2LvLOGIxtzo/jAg6Qf73TEBw==}
peerDependencies:
@@ -476,6 +574,20 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-context@1.0.0(react@18.2.0):
resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==}
peerDependencies:
@@ -485,6 +597,54 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-context@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-dialog@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-hJtRy/jPULGQZceSAP2Re6/4NpKo8im6V8P2hUqZsdFiSL8l35kYsw3qbRI6Ay5mQd2+wlLqje770eq+RJ3yZg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-focus-scope': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ aria-hidden: 1.2.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-remove-scroll: 2.5.5(@types/react@18.2.6)(react@18.2.0)
+ dev: false
+
/@radix-ui/react-direction@1.0.0(react@18.2.0):
resolution: {integrity: sha512-2HV05lGUgYcA6xgLQ4BKPDmtL+QbIZYH5fCOTAOOcJ5O0QbWS3i9lKaurLzliYUDhORI2Qr3pyjhJh44lKA3rQ==}
peerDependencies:
@@ -510,6 +670,31 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-dismissable-layer@1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-7UpBa/RKMoHJYjie1gkF1DlK8l1fdU/VKDpoS3rCCo8YBJR294GwcEHyxHw72yvphJ7ld0AXEcSLAzY2F/WyCg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-dropdown-menu@2.0.4(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-y6AT9+MydyXcByivdK1+QpjWoKaC7MLjkS/cH1Q3keEyMvDkiY85m8o2Bi6+Z1PPUlCsMULopxagQOSfN0wahg==}
peerDependencies:
@@ -539,6 +724,20 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-focus-scope@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-spwXlNTfeIprt+kaEWE/qYuYT3ZAqJiAGjN/JgdvgVDTu8yc+HuX+WOWXrKliKnLnwck0F6JDkqIERncnih+4A==}
peerDependencies:
@@ -553,6 +752,29 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-focus-scope@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-upXdPfqI4islj2CslyfUBNlaJCPybbqRHAi1KER7Isel9Q2AtSJ0zRBZv8mWQiFXD2nyAJ4BhC3yXgZ6kMBSrQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-id@1.0.0(react@18.2.0):
resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==}
peerDependencies:
@@ -563,6 +785,21 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-id@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-menu@2.0.4(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-mzKR47tZ1t193trEqlQoJvzY4u9vYfVH16ryBrVrCAGZzkgyWnMQYEZdUkM7y8ak9mrkKtJiqB47TlEnubeOFQ==}
peerDependencies:
@@ -617,6 +854,36 @@ packages:
- '@types/react'
dev: false
+ /@radix-ui/react-popper@1.1.2(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-1CnGGfFi/bbqtJZZ0P/NQY20xdG3E0LALJaLUEoKwPLwl6PPPfbeiCqMVQnhoFRAxjJj4RpBRJzDmUgsex2tSg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@floating-ui/react-dom': 2.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-portal@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-swu32idoCW7KA2VEiUZGBSu9nB6qwGdV6k6HYhUoOo3M1FFpD+VgLzUqtt3mwL1ssz7r2x8MggpLSQach2Xy/Q==}
peerDependencies:
@@ -629,6 +896,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-portal@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-xLYZeHrWoPmA5mEKEfZZevoVRK/Q43GfzRXkWV6qawIWWK8t6ifIiLQdd7rmQ4Vk1bmI21XhqF9BN3jWf+phpA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==}
peerDependencies:
@@ -642,6 +930,28 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-primitive@1.0.2(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==}
peerDependencies:
@@ -654,6 +964,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-roving-focus@1.0.3(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-stjCkIoMe6h+1fWtXlA6cRfikdBzCLp3SnVk7c48cv/uy3DTGoXhN76YaOYUJuy3aEDvDIKwKR5KSmvrtPvQPQ==}
peerDependencies:
@@ -674,6 +1005,27 @@ packages:
react-dom: 18.2.0(react@18.2.0)
dev: false
+ /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-slot@1.0.1(react@18.2.0):
resolution: {integrity: sha512-avutXAFL1ehGvAXtPquu0YK5oz6ctS474iM3vNGQIkswrVhdrS52e3uoMQBzZhNRAIE0jBnUyXWNmSjGHhCFcw==}
peerDependencies:
@@ -684,6 +1036,53 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-slot@1.0.2(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-tooltip@1.0.6(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-DmNFOiwEc2UDigsYj6clJENma58OelxD24O4IODoZ+3sQc3Zb+L8w1EP+y9laTuKCLAysPw4fD6/v0j4KNV8rg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-dismissable-layer': 1.0.4(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-popper': 1.1.2(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-portal': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@radix-ui/react-visually-hidden': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0):
resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==}
peerDependencies:
@@ -693,6 +1092,20 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0):
resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==}
peerDependencies:
@@ -703,6 +1116,21 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-escape-keydown@1.0.2(react@18.2.0):
resolution: {integrity: sha512-DXGim3x74WgUv+iMNCF+cAo8xUHHeqvjx8zs7trKf+FkQKPQXLk2sX7Gx1ysH7Q76xCpZuxIJE7HLPxRE+Q+GA==}
peerDependencies:
@@ -713,6 +1141,21 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0):
resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==}
peerDependencies:
@@ -722,6 +1165,20 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-rect@1.0.0(react@18.2.0):
resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==}
peerDependencies:
@@ -732,6 +1189,21 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
/@radix-ui/react-use-size@1.0.0(react@18.2.0):
resolution: {integrity: sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==}
peerDependencies:
@@ -742,12 +1214,54 @@ packages:
react: 18.2.0
dev: false
+ /@radix-ui/react-use-size@1.0.1(@types/react@18.2.6)(react@18.2.0):
+ resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.6)(react@18.2.0)
+ '@types/react': 18.2.6
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-visually-hidden@1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.21.5
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.4)(@types/react@18.2.6)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.6
+ '@types/react-dom': 18.2.4
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/@radix-ui/rect@1.0.0:
resolution: {integrity: sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==}
dependencies:
'@babel/runtime': 7.21.5
dev: false
+ /@radix-ui/rect@1.0.1:
+ resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
+ dependencies:
+ '@babel/runtime': 7.21.5
+ dev: false
+
/@rushstack/eslint-patch@1.2.0:
resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==}
dev: true
@@ -811,7 +1325,6 @@ packages:
resolution: {integrity: sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==}
dependencies:
'@types/react': 18.2.6
- dev: true
/@types/react-syntax-highlighter@15.5.6:
resolution: {integrity: sha512-i7wFuLbIAFlabTeD2I1cLjEOrG/xdMa/rpx2zwzAoGHuXJDhSqp9BSfDlMHSh9JSuNfxHk9eEmMX6D55GiyjGg==}
@@ -833,8 +1346,8 @@ packages:
resolution: {integrity: sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ==}
dev: false
- /@typescript-eslint/parser@5.59.5(eslint@8.40.0)(typescript@4.9.5):
- resolution: {integrity: sha512-NJXQC4MRnF9N9yWqQE2/KLRSOLvrrlZb48NGVfBa+RuPMN6B7ZcK5jZOvhuygv4D64fRKnZI4L4p8+M+rfeQuw==}
+ /@typescript-eslint/parser@5.59.7(eslint@8.40.0)(typescript@5.1.3):
+ resolution: {integrity: sha512-VhpsIEuq/8i5SF+mPg9jSdIwgMBBp0z9XqjiEay+81PYLJuroN+ET1hM5IhkiYMJd9MkTz8iJLt7aaGAgzWUbQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
@@ -843,31 +1356,31 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/scope-manager': 5.59.5
- '@typescript-eslint/types': 5.59.5
- '@typescript-eslint/typescript-estree': 5.59.5(typescript@4.9.5)
+ '@typescript-eslint/scope-manager': 5.59.7
+ '@typescript-eslint/types': 5.59.7
+ '@typescript-eslint/typescript-estree': 5.59.7(typescript@5.1.3)
debug: 4.3.4
eslint: 8.40.0
- typescript: 4.9.5
+ typescript: 5.1.3
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/scope-manager@5.59.5:
- resolution: {integrity: sha512-jVecWwnkX6ZgutF+DovbBJirZcAxgxC0EOHYt/niMROf8p4PwxxG32Qdhj/iIQQIuOflLjNkxoXyArkcIP7C3A==}
+ /@typescript-eslint/scope-manager@5.59.7:
+ resolution: {integrity: sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.59.5
- '@typescript-eslint/visitor-keys': 5.59.5
+ '@typescript-eslint/types': 5.59.7
+ '@typescript-eslint/visitor-keys': 5.59.7
dev: true
- /@typescript-eslint/types@5.59.5:
- resolution: {integrity: sha512-xkfRPHbqSH4Ggx4eHRIO/eGL8XL4Ysb4woL8c87YuAo8Md7AUjyWKa9YMwTL519SyDPrfEgKdewjkxNCVeJW7w==}
+ /@typescript-eslint/types@5.59.7:
+ resolution: {integrity: sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dev: true
- /@typescript-eslint/typescript-estree@5.59.5(typescript@4.9.5):
- resolution: {integrity: sha512-+XXdLN2CZLZcD/mO7mQtJMvCkzRfmODbeSKuMY/yXbGkzvA9rJyDY5qDYNoiz2kP/dmyAxXquL2BvLQLJFPQIg==}
+ /@typescript-eslint/typescript-estree@5.59.7(typescript@5.1.3):
+ resolution: {integrity: sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
peerDependencies:
typescript: '*'
@@ -875,23 +1388,23 @@ packages:
typescript:
optional: true
dependencies:
- '@typescript-eslint/types': 5.59.5
- '@typescript-eslint/visitor-keys': 5.59.5
+ '@typescript-eslint/types': 5.59.7
+ '@typescript-eslint/visitor-keys': 5.59.7
debug: 4.3.4
globby: 11.1.0
is-glob: 4.0.3
semver: 7.5.0
- tsutils: 3.21.0(typescript@4.9.5)
- typescript: 4.9.5
+ tsutils: 3.21.0(typescript@5.1.3)
+ typescript: 5.1.3
transitivePeerDependencies:
- supports-color
dev: true
- /@typescript-eslint/visitor-keys@5.59.5:
- resolution: {integrity: sha512-qL+Oz+dbeBRTeyJTIy0eniD3uvqU7x+y1QceBismZ41hd4aBSRh8UAw4pZP0+XzLuPZmx4raNMq/I+59W2lXKA==}
+ /@typescript-eslint/visitor-keys@5.59.7:
+ resolution: {integrity: sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
dependencies:
- '@typescript-eslint/types': 5.59.5
+ '@typescript-eslint/types': 5.59.7
eslint-visitor-keys: 3.4.1
dev: true
@@ -1232,7 +1745,7 @@ packages:
fsevents: 2.3.2
dev: true
- /class-variance-authority@0.4.0(typescript@4.9.5):
+ /class-variance-authority@0.4.0(typescript@5.1.3):
resolution: {integrity: sha512-74enNN8O9ZNieycac/y8FxqgyzZhZbxmCitAtAeUrLPlxjSd5zA7LfpprmxEcOmQBnaGs5hYhiSGnJ0mqrtBLQ==}
peerDependencies:
typescript: '>= 4.5.5 < 5'
@@ -1240,7 +1753,7 @@ packages:
typescript:
optional: true
dependencies:
- typescript: 4.9.5
+ typescript: 5.1.3
dev: false
/cli-color@2.0.3:
@@ -1864,8 +2377,8 @@ packages:
engines: {node: '>=12'}
dev: false
- /eslint-config-next@13.4.5-canary.3(eslint@8.40.0)(typescript@4.9.5):
- resolution: {integrity: sha512-F1W76ozmRCPk8sxPXO4tSy3pstBerEfiZ1Aiagr+d2a+l/Mx5CgHWf75CwsQfpiC6BbAee8RrdIjb8kvGlPeww==}
+ /eslint-config-next@13.4.5-canary.12(eslint@8.40.0)(typescript@5.1.3):
+ resolution: {integrity: sha512-QAb1cLyev4psngQEV5ItJOtYNs6KvCFEVslRDFHMAtxGuOlwMFTd7MWLRWZBRWnkfUSNbLFUdxhURHOEUTVv5w==}
peerDependencies:
eslint: ^7.23.0 || ^8.0.0
typescript: '>=3.3.1'
@@ -1873,17 +2386,17 @@ packages:
typescript:
optional: true
dependencies:
- '@next/eslint-plugin-next': 13.4.5-canary.3
+ '@next/eslint-plugin-next': 13.4.5-canary.12
'@rushstack/eslint-patch': 1.2.0
- '@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
+ '@typescript-eslint/parser': 5.59.7(eslint@8.40.0)(typescript@5.1.3)
eslint: 8.40.0
eslint-import-resolver-node: 0.3.7
- eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0)
- eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
+ eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0)
+ eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
eslint-plugin-jsx-a11y: 6.7.1(eslint@8.40.0)
eslint-plugin-react: 7.32.2(eslint@8.40.0)
eslint-plugin-react-hooks: 4.6.0(eslint@8.40.0)
- typescript: 4.9.5
+ typescript: 5.1.3
transitivePeerDependencies:
- eslint-import-resolver-webpack
- supports-color
@@ -1908,7 +2421,7 @@ packages:
- supports-color
dev: true
- /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0):
+ /eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0):
resolution: {integrity: sha512-TdJqPHs2lW5J9Zpe17DZNQuDnox4xo2o+0tE7Pggain9Rbc19ik8kFtXdxZ250FVx2kF4vlt2RSf4qlUpG7bhw==}
engines: {node: ^14.18.0 || >=16.0.0}
peerDependencies:
@@ -1918,8 +2431,8 @@ packages:
debug: 4.3.4
enhanced-resolve: 5.14.0
eslint: 8.40.0
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
- eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
+ eslint-plugin-import: 2.27.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
get-tsconfig: 4.5.0
globby: 13.1.4
is-core-module: 2.12.0
@@ -1932,7 +2445,7 @@ packages:
- supports-color
dev: true
- /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0):
+ /eslint-module-utils@2.8.0(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0):
resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==}
engines: {node: '>=4'}
peerDependencies:
@@ -1953,16 +2466,16 @@ packages:
eslint-import-resolver-webpack:
optional: true
dependencies:
- '@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
+ '@typescript-eslint/parser': 5.59.7(eslint@8.40.0)(typescript@5.1.3)
debug: 3.2.7
eslint: 8.40.0
eslint-import-resolver-node: 0.3.7
- eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0)
+ eslint-import-resolver-typescript: 3.5.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-plugin-import@2.27.5)(eslint@8.40.0)
transitivePeerDependencies:
- supports-color
dev: true
- /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0):
+ /eslint-plugin-import@2.27.5(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0):
resolution: {integrity: sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==}
engines: {node: '>=4'}
peerDependencies:
@@ -1972,7 +2485,7 @@ packages:
'@typescript-eslint/parser':
optional: true
dependencies:
- '@typescript-eslint/parser': 5.59.5(eslint@8.40.0)(typescript@4.9.5)
+ '@typescript-eslint/parser': 5.59.7(eslint@8.40.0)(typescript@5.1.3)
array-includes: 3.1.6
array.prototype.flat: 1.3.1
array.prototype.flatmap: 1.3.1
@@ -1980,7 +2493,7 @@ packages:
doctrine: 2.1.0
eslint: 8.40.0
eslint-import-resolver-node: 0.3.7
- eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.5)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@5.59.7)(eslint-import-resolver-node@0.3.7)(eslint-import-resolver-typescript@3.5.5)(eslint@8.40.0)
has: 1.0.3
is-core-module: 2.12.0
is-glob: 4.0.3
@@ -2053,6 +2566,17 @@ packages:
string.prototype.matchall: 4.0.8
dev: true
+ /eslint-plugin-tailwindcss@3.12.0(tailwindcss@3.3.2):
+ resolution: {integrity: sha512-DMfg8NcSV04V1v3iBgJGEhmRuapW36XZXyRV8WHdNFGEXGUkBwM9R8MujguKXeQKBG6VhjiX4t98rhzXdIlUFw==}
+ engines: {node: '>=12.13.0'}
+ peerDependencies:
+ tailwindcss: ^3.3.2
+ dependencies:
+ fast-glob: 3.2.12
+ postcss: 8.4.23
+ tailwindcss: 3.3.2
+ dev: true
+
/eslint-scope@7.2.0:
resolution: {integrity: sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@@ -2378,6 +2902,10 @@ packages:
is-glob: 4.0.3
dev: true
+ /glob-to-regexp@0.4.1:
+ resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+ dev: false
+
/glob@7.1.6:
resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
dependencies:
@@ -2475,7 +3003,6 @@ packages:
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
- dev: true
/grapheme-splitter@1.0.4:
resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
@@ -3508,14 +4035,14 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
- /next-themes@0.2.1(next@13.4.5-canary.3)(react-dom@18.2.0)(react@18.2.0):
+ /next-themes@0.2.1(next@13.4.5-canary.12)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
next: '*'
react: '*'
react-dom: '*'
dependencies:
- next: 13.4.5-canary.3(react-dom@18.2.0)(react@18.2.0)
+ next: 13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
dev: false
@@ -3524,8 +4051,8 @@ packages:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: true
- /next@13.4.5-canary.3(react-dom@18.2.0)(react@18.2.0):
- resolution: {integrity: sha512-5YLpHWSLjjInYlqxtkZZr7cwYp62wXEm7ik6Wu+4JqWQdbwD/pemuXwErxRIt65ruDQwe9RvE/dzMOuQuNDE+A==}
+ /next@13.4.5-canary.12(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-HL5+P55Jj9Zzd8372kDOyUqEUrI7ZYtUSeBpCJf8OAtTu7stBAte2oGZ2FJFDWItsfsJLvfWsz4Hqj8IYc1IZA==}
engines: {node: '>=16.8.0'}
hasBin: true
peerDependencies:
@@ -3542,7 +4069,7 @@ packages:
sass:
optional: true
dependencies:
- '@next/env': 13.4.5-canary.3
+ '@next/env': 13.4.5-canary.12
'@swc/helpers': 0.5.1
busboy: 1.6.0
caniuse-lite: 1.0.30001486
@@ -3550,17 +4077,18 @@ packages:
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
styled-jsx: 5.1.1(react@18.2.0)
+ watchpack: 2.4.0
zod: 3.21.4
optionalDependencies:
- '@next/swc-darwin-arm64': 13.4.5-canary.3
- '@next/swc-darwin-x64': 13.4.5-canary.3
- '@next/swc-linux-arm64-gnu': 13.4.5-canary.3
- '@next/swc-linux-arm64-musl': 13.4.5-canary.3
- '@next/swc-linux-x64-gnu': 13.4.5-canary.3
- '@next/swc-linux-x64-musl': 13.4.5-canary.3
- '@next/swc-win32-arm64-msvc': 13.4.5-canary.3
- '@next/swc-win32-ia32-msvc': 13.4.5-canary.3
- '@next/swc-win32-x64-msvc': 13.4.5-canary.3
+ '@next/swc-darwin-arm64': 13.4.5-canary.12
+ '@next/swc-darwin-x64': 13.4.5-canary.12
+ '@next/swc-linux-arm64-gnu': 13.4.5-canary.12
+ '@next/swc-linux-arm64-musl': 13.4.5-canary.12
+ '@next/swc-linux-x64-gnu': 13.4.5-canary.12
+ '@next/swc-linux-x64-musl': 13.4.5-canary.12
+ '@next/swc-win32-arm64-msvc': 13.4.5-canary.12
+ '@next/swc-win32-ia32-msvc': 13.4.5-canary.12
+ '@next/swc-win32-x64-msvc': 13.4.5-canary.12
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@@ -4569,14 +5097,14 @@ packages:
/tslib@2.5.0:
resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==}
- /tsutils@3.21.0(typescript@4.9.5):
+ /tsutils@3.21.0(typescript@5.1.3):
resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
engines: {node: '>= 6'}
peerDependencies:
typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
dependencies:
tslib: 1.14.1
- typescript: 4.9.5
+ typescript: 5.1.3
dev: true
/type-check@0.4.0:
@@ -4607,9 +5135,9 @@ packages:
is-typed-array: 1.1.10
dev: true
- /typescript@4.9.5:
- resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
- engines: {node: '>=4.2.0'}
+ /typescript@5.1.3:
+ resolution: {integrity: sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==}
+ engines: {node: '>=14.17'}
hasBin: true
/unbox-primitive@1.0.2:
@@ -4797,6 +5325,14 @@ packages:
vfile-message: 3.1.4
dev: false
+ /watchpack@2.4.0:
+ resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+ dev: false
+
/webidl-conversions@3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
dev: false
@@ -4886,6 +5422,22 @@ packages:
/zod@3.21.4:
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
+ /zustand@4.3.8(react@18.2.0):
+ resolution: {integrity: sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==}
+ engines: {node: '>=12.7.0'}
+ peerDependencies:
+ immer: '>=9.0'
+ react: '>=16.8'
+ peerDependenciesMeta:
+ immer:
+ optional: true
+ react:
+ optional: true
+ dependencies:
+ react: 18.2.0
+ use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
+
/zwitch@2.0.4:
resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
dev: false
diff --git a/prettier.config.cjs b/prettier.config.cjs
new file mode 100644
index 0000000..842ca6b
--- /dev/null
+++ b/prettier.config.cjs
@@ -0,0 +1,34 @@
+/** @type {import('prettier').Config} */
+module.exports = {
+ endOfLine: 'lf',
+ semi: false,
+ useTabs: false,
+ singleQuote: true,
+ arrowParens: 'avoid',
+ tabWidth: 2,
+ trailingComma: 'none',
+ importOrder: [
+ '^(react/(.*)$)|^(react$)',
+ '^(next/(.*)$)|^(next$)',
+ '',
+ '',
+ '^types$',
+ '^@/types/(.*)$',
+ '^@/config/(.*)$',
+ '^@/lib/(.*)$',
+ '^@/hooks/(.*)$',
+ '^@/components/ui/(.*)$',
+ '^@/components/(.*)$',
+ '^@/registry/(.*)$',
+ '^@/styles/(.*)$',
+ '^@/app/(.*)$',
+ '',
+ '^[./]'
+ ],
+ importOrderSeparation: false,
+ importOrderSortSpecifiers: true,
+ importOrderBuiltinModulesToTop: true,
+ importOrderParserPlugins: ['typescript', 'jsx', 'decorators-legacy'],
+ importOrderMergeDuplicateImports: true,
+ importOrderCombineTypeAndValueImports: true
+}
diff --git a/tailwind.config.js b/tailwind.config.js
index 2d20fa8..f37c844 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,36 +1,99 @@
-const { fontFamily } = require("tailwindcss/defaultTheme");
+const { fontFamily } = require('tailwindcss/defaultTheme')
/** @type {import('tailwindcss').Config} */
module.exports = {
- darkMode: ["class"],
- content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
+ darkMode: ['class'],
+ content: ['app/**/*.{ts,tsx}', 'components/**/*.{ts,tsx}'],
theme: {
container: {
center: true,
- padding: "2rem",
+ padding: '2rem',
screens: {
- "2xl": "1400px",
- },
+ '2xl': '1400px'
+ }
},
extend: {
fontFamily: {
- sans: ["var(--font-sans)", ...fontFamily.sans],
+ sans: ['var(--font-sans)', ...fontFamily.sans]
+ },
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))'
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))'
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))'
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))'
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))'
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))'
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))'
+ }
+ },
+ borderRadius: {
+ lg: `var(--radius)`,
+ md: `calc(var(--radius) - 2px)`,
+ sm: 'calc(var(--radius) - 4px)'
+ },
+ fontFamily: {
+ sans: ['var(--font-sans)', ...fontFamily.sans]
},
keyframes: {
- "accordion-down": {
+ 'accordion-down': {
from: { height: 0 },
- to: { height: "var(--radix-accordion-content-height)" },
+ to: { height: 'var(--radix-accordion-content-height)' }
},
- "accordion-up": {
- from: { height: "var(--radix-accordion-content-height)" },
- to: { height: 0 },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: 0 }
},
+ 'slide-from-left': {
+ '0%': {
+ transform: 'translateX(-100%)'
+ },
+ '100%': {
+ transform: 'translateX(0)'
+ }
+ },
+ 'slide-to-left': {
+ '0%': {
+ transform: 'translateX(0)'
+ },
+ '100%': {
+ transform: 'translateX(-100%)'
+ }
+ }
},
animation: {
- "accordion-down": "accordion-down 0.2s ease-out",
- "accordion-up": "accordion-up 0.2s ease-out",
- },
- },
+ 'slide-from-left':
+ 'slide-from-left 0.4s cubic-bezier(0.82, 0.085, 0.395, 0.895)',
+ 'slide-to-left':
+ 'slide-to-left 0.25s cubic-bezier(0.82, 0.085, 0.395, 0.895)',
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out'
+ }
+ }
},
- plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
-};
+ plugins: [require('tailwindcss-animate'), require('@tailwindcss/typography')]
+}