Update to Next.js 14 and latest dependencies. (#186)
This commit is contained in:
commit
35e83dc87e
25 changed files with 1969 additions and 2012 deletions
|
|
@ -8,7 +8,8 @@
|
||||||
],
|
],
|
||||||
"plugins": ["tailwindcss"],
|
"plugins": ["tailwindcss"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"tailwindcss/no-custom-classname": "off"
|
"tailwindcss/no-custom-classname": "off",
|
||||||
|
"tailwindcss/classnames-order": "off"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"tailwindcss": {
|
"tailwindcss": {
|
||||||
|
|
|
||||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -32,7 +32,7 @@ yarn-error.log*
|
||||||
# turbo
|
# turbo
|
||||||
.turbo
|
.turbo
|
||||||
|
|
||||||
.contentlayer
|
|
||||||
.env
|
.env
|
||||||
.vercel
|
.vercel
|
||||||
.vscode
|
.vscode
|
||||||
|
.env*.local
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<a href="https://chat.vercel.ai/">
|
<a href="https://chat.vercel.ai/">
|
||||||
<img alt="Next.js 13 and app template Router-ready AI chatbot." src="https://chat.vercel.ai/opengraph-image.png">
|
<img alt="Next.js 14 and App Router-ready AI chatbot." src="https://chat.vercel.ai/opengraph-image.png">
|
||||||
<h1 align="center">Next.js AI Chatbot</h1>
|
<h1 align="center">Next.js AI Chatbot</h1>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
|
@ -22,7 +22,6 @@
|
||||||
- React Server Components (RSCs), Suspense, and Server Actions
|
- React Server Components (RSCs), Suspense, and Server Actions
|
||||||
- [Vercel AI SDK](https://sdk.vercel.ai/docs) for streaming chat UI
|
- [Vercel AI SDK](https://sdk.vercel.ai/docs) for streaming chat UI
|
||||||
- Support for OpenAI (default), Anthropic, Cohere, Hugging Face, or custom AI chat models and/or LangChain
|
- Support for OpenAI (default), Anthropic, Cohere, Hugging Face, or custom AI chat models and/or LangChain
|
||||||
- Edge runtime-ready
|
|
||||||
- [shadcn/ui](https://ui.shadcn.com)
|
- [shadcn/ui](https://ui.shadcn.com)
|
||||||
- Styling with [Tailwind CSS](https://tailwindcss.com)
|
- Styling with [Tailwind CSS](https://tailwindcss.com)
|
||||||
- [Radix UI](https://radix-ui.com) for headless component primitives
|
- [Radix UI](https://radix-ui.com) for headless component primitives
|
||||||
|
|
@ -46,7 +45,6 @@ Follow the steps outlined in the [quick start guide](https://vercel.com/docs/sto
|
||||||
|
|
||||||
Remember to update your environment variables (`KV_URL`, `KV_REST_API_URL`, `KV_REST_API_TOKEN`, `KV_REST_API_READ_ONLY_TOKEN`) in the `.env` file with the appropriate credentials provided during the KV database setup.
|
Remember to update your environment variables (`KV_URL`, `KV_REST_API_URL`, `KV_REST_API_TOKEN`, `KV_REST_API_READ_ONLY_TOKEN`) in the `.env` file with the appropriate credentials provided during the KV database setup.
|
||||||
|
|
||||||
|
|
||||||
## Running locally
|
## 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/projects/environment-variables) for this, but a `.env` file is all that is necessary.
|
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/projects/environment-variables) for this, but a `.env` file is all that is necessary.
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
import { kv } from '@vercel/kv'
|
import { kv } from '@vercel/kv'
|
||||||
import { OpenAIStream, StreamingTextResponse } from 'ai'
|
import { OpenAIStream, StreamingTextResponse } from 'ai'
|
||||||
import { Configuration, OpenAIApi } from 'openai-edge'
|
import OpenAI from 'openai'
|
||||||
|
|
||||||
import { auth } from '@/auth'
|
import { auth } from '@/auth'
|
||||||
import { nanoid } from '@/lib/utils'
|
import { nanoid } from '@/lib/utils'
|
||||||
|
|
||||||
export const runtime = 'edge'
|
export const runtime = 'edge'
|
||||||
|
|
||||||
const configuration = new Configuration({
|
const openai = new OpenAI({
|
||||||
apiKey: process.env.OPENAI_API_KEY
|
apiKey: process.env.OPENAI_API_KEY
|
||||||
})
|
})
|
||||||
|
|
||||||
const openai = new OpenAIApi(configuration)
|
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
const json = await req.json()
|
const json = await req.json()
|
||||||
const { messages, previewToken } = json
|
const { messages, previewToken } = json
|
||||||
|
|
@ -25,10 +23,10 @@ export async function POST(req: Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previewToken) {
|
if (previewToken) {
|
||||||
configuration.apiKey = previewToken
|
openai.apiKey = previewToken
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await openai.createChatCompletion({
|
const res = await openai.chat.completions.create({
|
||||||
model: 'gpt-3.5-turbo',
|
model: 'gpt-3.5-turbo',
|
||||||
messages,
|
messages,
|
||||||
temperature: 0.7,
|
temperature: 0.7,
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@ import { auth } from '@/auth'
|
||||||
import { getChat } from '@/app/actions'
|
import { getChat } from '@/app/actions'
|
||||||
import { Chat } from '@/components/chat'
|
import { Chat } from '@/components/chat'
|
||||||
|
|
||||||
export const runtime = 'edge'
|
|
||||||
export const preferredRegion = 'home'
|
|
||||||
|
|
||||||
export interface ChatPageProps {
|
export interface ChatPageProps {
|
||||||
params: {
|
params: {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,20 @@
|
||||||
import { Metadata } from 'next'
|
|
||||||
|
|
||||||
import { Toaster } from 'react-hot-toast'
|
import { Toaster } from 'react-hot-toast'
|
||||||
|
import { GeistSans } from 'geist/font/sans'
|
||||||
|
import { GeistMono } from 'geist/font/mono'
|
||||||
|
|
||||||
import '@/app/globals.css'
|
import '@/app/globals.css'
|
||||||
import { fontMono, fontSans } from '@/lib/fonts'
|
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { TailwindIndicator } from '@/components/tailwind-indicator'
|
import { TailwindIndicator } from '@/components/tailwind-indicator'
|
||||||
import { Providers } from '@/components/providers'
|
import { Providers } from '@/components/providers'
|
||||||
import { Header } from '@/components/header'
|
import { Header } from '@/components/header'
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata = {
|
||||||
|
metadataBase: new URL(`https://${process.env.VERCEL_URL}`),
|
||||||
title: {
|
title: {
|
||||||
default: 'Next.js AI Chatbot',
|
default: 'Next.js AI Chatbot',
|
||||||
template: `%s - Next.js AI Chatbot`
|
template: `%s - Next.js AI Chatbot`
|
||||||
},
|
},
|
||||||
description: 'An AI-powered chatbot template built with Next.js and Vercel.',
|
description: 'An AI-powered chatbot template built with Next.js and Vercel.',
|
||||||
themeColor: [
|
|
||||||
{ media: '(prefers-color-scheme: light)', color: 'white' },
|
|
||||||
{ media: '(prefers-color-scheme: dark)', color: 'black' }
|
|
||||||
],
|
|
||||||
icons: {
|
icons: {
|
||||||
icon: '/favicon.ico',
|
icon: '/favicon.ico',
|
||||||
shortcut: '/favicon-16x16.png',
|
shortcut: '/favicon-16x16.png',
|
||||||
|
|
@ -26,6 +22,13 @@ export const metadata: Metadata = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const viewport = {
|
||||||
|
themeColor: [
|
||||||
|
{ media: '(prefers-color-scheme: light)', color: 'white' },
|
||||||
|
{ media: '(prefers-color-scheme: dark)', color: 'black' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
interface RootLayoutProps {
|
interface RootLayoutProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
}
|
}
|
||||||
|
|
@ -33,18 +36,16 @@ interface RootLayoutProps {
|
||||||
export default function RootLayout({ children }: RootLayoutProps) {
|
export default function RootLayout({ children }: RootLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<head />
|
|
||||||
<body
|
<body
|
||||||
className={cn(
|
className={cn(
|
||||||
'font-sans antialiased',
|
'font-sans antialiased',
|
||||||
fontSans.variable,
|
GeistSans.variable,
|
||||||
fontMono.variable
|
GeistMono.variable
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Toaster />
|
<Toaster />
|
||||||
<Providers attribute="class" defaultTheme="system" enableSystem>
|
<Providers attribute="class" defaultTheme="system" enableSystem>
|
||||||
<div className="flex flex-col min-h-screen">
|
<div className="flex flex-col min-h-screen">
|
||||||
{/* @ts-ignore */}
|
|
||||||
<Header />
|
<Header />
|
||||||
<main className="flex flex-col flex-1 bg-muted/50">{children}</main>
|
<main className="flex flex-col flex-1 bg-muted/50">{children}</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
import { nanoid } from '@/lib/utils'
|
import { nanoid } from '@/lib/utils'
|
||||||
import { Chat } from '@/components/chat'
|
import { Chat } from '@/components/chat'
|
||||||
|
|
||||||
export const runtime = 'edge'
|
|
||||||
|
|
||||||
export default function IndexPage() {
|
export default function IndexPage() {
|
||||||
const id = nanoid()
|
const id = nanoid()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
import { ImageResponse } from 'next/server'
|
|
||||||
|
|
||||||
import { getSharedChat } from '@/app/actions'
|
|
||||||
|
|
||||||
export const runtime = 'edge'
|
|
||||||
|
|
||||||
export const alt = 'AI Chatbot'
|
|
||||||
|
|
||||||
export const size = {
|
|
||||||
width: 1200,
|
|
||||||
height: 630
|
|
||||||
}
|
|
||||||
|
|
||||||
export const contentType = 'image/png'
|
|
||||||
|
|
||||||
const interRegular = fetch(
|
|
||||||
new URL('../../../assets/fonts/Inter-Regular.woff', import.meta.url)
|
|
||||||
).then(res => res.arrayBuffer())
|
|
||||||
|
|
||||||
const interBold = fetch(
|
|
||||||
new URL('../../../assets/fonts/Inter-Bold.woff', import.meta.url)
|
|
||||||
).then(res => res.arrayBuffer())
|
|
||||||
|
|
||||||
interface ImageProps {
|
|
||||||
params: {
|
|
||||||
id: string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async function Image({ params }: ImageProps) {
|
|
||||||
const chat = await getSharedChat(params.id)
|
|
||||||
|
|
||||||
if (!chat || !chat?.sharePath) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const textAlign = chat?.title?.length > 40 ? 'items-start' : 'items-center'
|
|
||||||
|
|
||||||
return new ImageResponse(
|
|
||||||
(
|
|
||||||
<div tw="flex w-full items-start h-full flex-col bg-[#09090b] text-white p-[80px]">
|
|
||||||
<div tw="flex flex-col w-full pt-[40px]">
|
|
||||||
<div tw={`flex w-full ${textAlign}`}>
|
|
||||||
<div tw="flex h-18 w-18 items-center justify-center rounded-md border border-[#9b9ba4]">
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 256 256"
|
|
||||||
fill="currentColor"
|
|
||||||
width={48}
|
|
||||||
height={48}
|
|
||||||
>
|
|
||||||
<path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div tw="flex text-white font-bold text-4xl leading-normal ml-10">
|
|
||||||
{chat.title.length > 120
|
|
||||||
? `${chat.title.slice(0, 120)}...`
|
|
||||||
: chat.title}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div tw="flex w-full mt-14 items-start">
|
|
||||||
<div tw="flex h-18 w-18 items-center justify-center rounded-md border border-[#9b9ba4]">
|
|
||||||
<svg
|
|
||||||
fill="currentColor"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
role="img"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width={48}
|
|
||||||
height={48}
|
|
||||||
>
|
|
||||||
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z"></path>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div tw="flex text-white font-bold text-6xl leading-none ml-10">
|
|
||||||
...
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div tw="flex items-center justify-between w-full mt-auto">
|
|
||||||
<div tw="flex items-center">
|
|
||||||
<svg
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
fill="#9b9ba4"
|
|
||||||
width={40}
|
|
||||||
height={40}
|
|
||||||
>
|
|
||||||
<path d="M24 22.525H0l12-21.05 12 21.05z" />
|
|
||||||
</svg>
|
|
||||||
<div tw="flex text-[1.8rem] ml-4 text-[#9b9ba4]">
|
|
||||||
Built with{' '}
|
|
||||||
<div tw="flex text-[#eaeaf0] ml-2 mr-2">Vercel AI SDK</div> &
|
|
||||||
<div tw="flex text-[#eaeaf0] ml-2">KV</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div tw="text-[1.8rem] ml-auto text-[#9b9ba4]">chat.vercel.ai</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
{
|
|
||||||
...size,
|
|
||||||
fonts: [
|
|
||||||
{
|
|
||||||
name: 'Inter',
|
|
||||||
data: await interRegular,
|
|
||||||
style: 'normal',
|
|
||||||
weight: 400
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Inter',
|
|
||||||
data: await interBold,
|
|
||||||
style: 'normal',
|
|
||||||
weight: 700
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
@ -6,9 +6,6 @@ import { getSharedChat } from '@/app/actions'
|
||||||
import { ChatList } from '@/components/chat-list'
|
import { ChatList } from '@/components/chat-list'
|
||||||
import { FooterText } from '@/components/footer'
|
import { FooterText } from '@/components/footer'
|
||||||
|
|
||||||
export const runtime = 'edge'
|
|
||||||
export const preferredRegion = 'home'
|
|
||||||
|
|
||||||
interface SharePageProps {
|
interface SharePageProps {
|
||||||
params: {
|
params: {
|
||||||
id: string
|
id: string
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
9
auth.ts
9
auth.ts
|
|
@ -12,8 +12,7 @@ declare module 'next-auth' {
|
||||||
|
|
||||||
export const {
|
export const {
|
||||||
handlers: { GET, POST },
|
handlers: { GET, POST },
|
||||||
auth,
|
auth
|
||||||
CSRF_experimental // will be removed in future
|
|
||||||
} = NextAuth({
|
} = NextAuth({
|
||||||
providers: [GitHub],
|
providers: [GitHub],
|
||||||
callbacks: {
|
callbacks: {
|
||||||
|
|
@ -24,6 +23,12 @@ export const {
|
||||||
}
|
}
|
||||||
return token
|
return token
|
||||||
},
|
},
|
||||||
|
session: ({ session, token }) => {
|
||||||
|
if (session?.user && token?.id) {
|
||||||
|
session.user.id = String(token.id)
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
},
|
||||||
authorized({ auth }) {
|
authorized({ auth }) {
|
||||||
return !!auth?.user // this ensures there is a logged in user for -every- request
|
return !!auth?.user // this ensures there is a logged in user for -every- request
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,40 +17,48 @@ import { SidebarFooter } from '@/components/sidebar-footer'
|
||||||
import { ThemeToggle } from '@/components/theme-toggle'
|
import { ThemeToggle } from '@/components/theme-toggle'
|
||||||
import { ClearHistory } from '@/components/clear-history'
|
import { ClearHistory } from '@/components/clear-history'
|
||||||
import { UserMenu } from '@/components/user-menu'
|
import { UserMenu } from '@/components/user-menu'
|
||||||
import { LoginButton } from '@/components/login-button'
|
|
||||||
|
|
||||||
export async function Header() {
|
async function UserOrLogin() {
|
||||||
const session = await auth()
|
const session = await auth()
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{session?.user ? (
|
||||||
|
<Sidebar>
|
||||||
|
<React.Suspense fallback={<div className="flex-1 overflow-auto" />}>
|
||||||
|
<SidebarList userId={session?.user?.id} />
|
||||||
|
</React.Suspense>
|
||||||
|
<SidebarFooter>
|
||||||
|
<ThemeToggle />
|
||||||
|
<ClearHistory clearChats={clearChats} />
|
||||||
|
</SidebarFooter>
|
||||||
|
</Sidebar>
|
||||||
|
) : (
|
||||||
|
<Link href="/" target="_blank" rel="nofollow">
|
||||||
|
<IconNextChat className="w-6 h-6 mr-2 dark:hidden" inverted />
|
||||||
|
<IconNextChat className="hidden w-6 h-6 mr-2 dark:block" />
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center">
|
||||||
|
<IconSeparator className="w-6 h-6 text-muted-foreground/50" />
|
||||||
|
{session?.user ? (
|
||||||
|
<UserMenu user={session.user} />
|
||||||
|
) : (
|
||||||
|
<Button variant="link" asChild className="-ml-2">
|
||||||
|
<Link href="/sign-in?callbackUrl=/">Login</Link>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Header() {
|
||||||
return (
|
return (
|
||||||
<header className="sticky top-0 z-50 flex items-center justify-between w-full h-16 px-4 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
|
<header className="sticky top-0 z-50 flex items-center justify-between w-full h-16 px-4 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{session?.user ? (
|
<React.Suspense fallback={<div className="flex-1 overflow-auto" />}>
|
||||||
<Sidebar>
|
<UserOrLogin />
|
||||||
<React.Suspense fallback={<div className="flex-1 overflow-auto" />}>
|
</React.Suspense>
|
||||||
{/* @ts-ignore */}
|
|
||||||
<SidebarList userId={session?.user?.id} />
|
|
||||||
</React.Suspense>
|
|
||||||
<SidebarFooter>
|
|
||||||
<ThemeToggle />
|
|
||||||
<ClearHistory clearChats={clearChats} />
|
|
||||||
</SidebarFooter>
|
|
||||||
</Sidebar>
|
|
||||||
) : (
|
|
||||||
<Link href="/" target="_blank" rel="nofollow">
|
|
||||||
<IconNextChat className="w-6 h-6 mr-2 dark:hidden" inverted />
|
|
||||||
<IconNextChat className="hidden w-6 h-6 mr-2 dark:block" />
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
<div className="flex items-center">
|
|
||||||
<IconSeparator className="w-6 h-6 text-muted-foreground/50" />
|
|
||||||
{session?.user ? (
|
|
||||||
<UserMenu user={session.user} />
|
|
||||||
) : (
|
|
||||||
<Button variant="link" asChild className="-ml-2">
|
|
||||||
<Link href="/sign-in?callbackUrl=/">Login</Link>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-end space-x-2">
|
<div className="flex items-center justify-end space-x-2">
|
||||||
<a
|
<a
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,8 @@ export function SidebarActions({
|
||||||
<div className="space-y-1 rounded-md border p-4 text-sm">
|
<div className="space-y-1 rounded-md border p-4 text-sm">
|
||||||
<div className="font-medium">{chat.title}</div>
|
<div className="font-medium">{chat.title}</div>
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
{formatDate(chat.createdAt)} · {chat.messages.length} messages
|
{formatDate(Number(chat.createdAt))} · {chat.messages.length}{' '}
|
||||||
|
messages
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DialogFooter className="items-center">
|
<DialogFooter className="items-center">
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
'use client'
|
|
||||||
|
|
||||||
export { Toaster } from 'react-hot-toast'
|
|
||||||
|
|
@ -10,26 +10,15 @@ const AlertDialog = AlertDialogPrimitive.Root
|
||||||
|
|
||||||
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
|
||||||
|
|
||||||
const AlertDialogPortal = ({
|
const AlertDialogPortal = AlertDialogPrimitive.Portal
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: AlertDialogPrimitive.AlertDialogPortalProps) => (
|
|
||||||
<AlertDialogPrimitive.Portal className={cn(className)} {...props}>
|
|
||||||
<div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</AlertDialogPrimitive.Portal>
|
|
||||||
)
|
|
||||||
AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName
|
|
||||||
|
|
||||||
const AlertDialogOverlay = React.forwardRef<
|
const AlertDialogOverlay = React.forwardRef<
|
||||||
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
React.ElementRef<typeof AlertDialogPrimitive.Overlay>,
|
||||||
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<AlertDialogPrimitive.Overlay
|
<AlertDialogPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-opacity animate-in fade-in',
|
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -47,7 +36,7 @@ const AlertDialogContent = React.forwardRef<
|
||||||
<AlertDialogPrimitive.Content
|
<AlertDialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed z-50 grid w-full max-w-lg scale-100 gap-4 border bg-background p-6 opacity-100 shadow-lg animate-in fade-in-90 slide-in-from-bottom-10 sm:rounded-lg sm:zoom-in-90 sm:slide-in-from-bottom-0 md:w-full',
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -139,6 +128,8 @@ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
|
||||||
|
|
||||||
export {
|
export {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
|
AlertDialogPortal,
|
||||||
|
AlertDialogOverlay,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
AlertDialogContent,
|
AlertDialogContent,
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,9 @@ const Dialog = DialogPrimitive.Root
|
||||||
|
|
||||||
const DialogTrigger = DialogPrimitive.Trigger
|
const DialogTrigger = DialogPrimitive.Trigger
|
||||||
|
|
||||||
const DialogPortal = ({
|
const DialogPortal = DialogPrimitive.Portal
|
||||||
className,
|
|
||||||
children,
|
const DialogClose = DialogPrimitive.Close
|
||||||
...props
|
|
||||||
}: DialogPrimitive.DialogPortalProps) => (
|
|
||||||
<DialogPrimitive.Portal className={cn(className)} {...props}>
|
|
||||||
<div className="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
</DialogPrimitive.Portal>
|
|
||||||
)
|
|
||||||
DialogPortal.displayName = DialogPrimitive.Portal.displayName
|
|
||||||
|
|
||||||
const DialogOverlay = React.forwardRef<
|
const DialogOverlay = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
||||||
|
|
@ -30,7 +21,7 @@ const DialogOverlay = React.forwardRef<
|
||||||
<DialogPrimitive.Overlay
|
<DialogPrimitive.Overlay
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in',
|
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -47,14 +38,14 @@ const DialogContent = React.forwardRef<
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed z-50 grid w-full gap-4 rounded-b-lg border bg-background p-6 shadow-sm animate-in data-[state=open]:fade-in-90 data-[state=open]:slide-in-from-bottom-10 sm:max-w-lg sm:rounded-lg sm:zoom-in-90 data-[state=open]:sm:slide-in-from-bottom-0',
|
'fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
<IconClose />
|
<IconClose className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</DialogPrimitive.Close>
|
</DialogPrimitive.Close>
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
|
|
@ -119,6 +110,9 @@ DialogDescription.displayName = DialogPrimitive.Description.displayName
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
DialogPortal,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogClose,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
import * as React from 'react'
|
import * as React from 'react'
|
||||||
import * as SheetPrimitive from '@radix-ui/react-dialog'
|
import * as SheetPrimitive from '@radix-ui/react-dialog'
|
||||||
|
import { cva, type VariantProps } from 'class-variance-authority'
|
||||||
|
import { IconClose } from '@/components/ui/icons'
|
||||||
|
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { IconClose } from '@/components/ui/icons'
|
|
||||||
|
|
||||||
const Sheet = SheetPrimitive.Root
|
const Sheet = SheetPrimitive.Root
|
||||||
|
|
||||||
|
|
@ -12,27 +13,15 @@ const SheetTrigger = SheetPrimitive.Trigger
|
||||||
|
|
||||||
const SheetClose = SheetPrimitive.Close
|
const SheetClose = SheetPrimitive.Close
|
||||||
|
|
||||||
const SheetPortal = ({
|
const SheetPortal = SheetPrimitive.Portal
|
||||||
className,
|
|
||||||
children,
|
|
||||||
...props
|
|
||||||
}: SheetPrimitive.DialogPortalProps) => (
|
|
||||||
<SheetPrimitive.Portal
|
|
||||||
className={cn('fixed inset-0 z-50 flex', className)}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</SheetPrimitive.Portal>
|
|
||||||
)
|
|
||||||
SheetPortal.displayName = SheetPrimitive.Portal.displayName
|
|
||||||
|
|
||||||
const SheetOverlay = React.forwardRef<
|
const SheetOverlay = React.forwardRef<
|
||||||
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
React.ElementRef<typeof SheetPrimitive.Overlay>,
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, ...props }, ref) => (
|
||||||
<SheetPrimitive.Overlay
|
<SheetPrimitive.Overlay
|
||||||
className={cn(
|
className={cn(
|
||||||
'fixed inset-0 z-50 transition-all duration-100 data-[state=closed]:animate-out data-[state=closed]:fade-out data-[state=open]:fade-in',
|
'fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
@ -41,22 +30,43 @@ const SheetOverlay = React.forwardRef<
|
||||||
))
|
))
|
||||||
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
|
||||||
|
|
||||||
|
const sheetVariants = cva(
|
||||||
|
'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500',
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
side: {
|
||||||
|
top: 'inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top',
|
||||||
|
bottom:
|
||||||
|
'inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom',
|
||||||
|
left: 'inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm',
|
||||||
|
right:
|
||||||
|
'inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
side: 'right'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
interface SheetContentProps
|
||||||
|
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
|
||||||
|
VariantProps<typeof sheetVariants> {}
|
||||||
|
|
||||||
const SheetContent = React.forwardRef<
|
const SheetContent = React.forwardRef<
|
||||||
React.ElementRef<typeof SheetPrimitive.Content>,
|
React.ElementRef<typeof SheetPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>
|
SheetContentProps
|
||||||
>(({ className, children, ...props }, ref) => (
|
>(({ side = 'left', className, children, ...props }, ref) => (
|
||||||
<SheetPortal>
|
<SheetPortal>
|
||||||
|
<SheetOverlay />
|
||||||
<SheetPrimitive.Content
|
<SheetPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(sheetVariants({ side }), className)}
|
||||||
'fixed inset-y-0 left-0 z-50 h-full border-r bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left data-[state=closed]:duration-300 data-[state=open]:duration-500 sm:max-w-sm',
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
|
||||||
<IconClose />
|
<IconClose className="h-4 w-4" />
|
||||||
<span className="sr-only">Close</span>
|
<span className="sr-only">Close</span>
|
||||||
</SheetPrimitive.Close>
|
</SheetPrimitive.Close>
|
||||||
</SheetPrimitive.Content>
|
</SheetPrimitive.Content>
|
||||||
|
|
@ -68,7 +78,13 @@ const SheetHeader = ({
|
||||||
className,
|
className,
|
||||||
...props
|
...props
|
||||||
}: React.HTMLAttributes<HTMLDivElement>) => (
|
}: React.HTMLAttributes<HTMLDivElement>) => (
|
||||||
<div className={cn('flex flex-col space-y-2', className)} {...props} />
|
<div
|
||||||
|
className={cn(
|
||||||
|
'flex flex-col space-y-2 text-center sm:text-left',
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
SheetHeader.displayName = 'SheetHeader'
|
SheetHeader.displayName = 'SheetHeader'
|
||||||
|
|
||||||
|
|
@ -112,6 +128,8 @@ SheetDescription.displayName = SheetPrimitive.Description.displayName
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Sheet,
|
Sheet,
|
||||||
|
SheetPortal,
|
||||||
|
SheetOverlay,
|
||||||
SheetTrigger,
|
SheetTrigger,
|
||||||
SheetClose,
|
SheetClose,
|
||||||
SheetContent,
|
SheetContent,
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ export function UserMenu({ user }: UserMenuProps) {
|
||||||
className="w-6 h-6 transition-opacity duration-300 rounded-full select-none ring-1 ring-zinc-100/10 hover:opacity-80"
|
className="w-6 h-6 transition-opacity duration-300 rounded-full select-none ring-1 ring-zinc-100/10 hover:opacity-80"
|
||||||
src={user?.image ? `${user.image}&s=60` : ''}
|
src={user?.image ? `${user.image}&s=60` : ''}
|
||||||
alt={user.name ?? 'Avatar'}
|
alt={user.name ?? 'Avatar'}
|
||||||
height={48} width={48}
|
height={48}
|
||||||
|
width={48}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-center text-xs font-medium uppercase rounded-full select-none h-7 w-7 shrink-0 bg-muted/50 text-muted-foreground">
|
<div className="flex items-center justify-center text-xs font-medium uppercase rounded-full select-none h-7 w-7 shrink-0 bg-muted/50 text-muted-foreground">
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import type { NextApiRequest } from 'next'
|
|
||||||
import type { NextFetchEvent, NextRequest } from 'next/server'
|
|
||||||
|
|
||||||
export const initAnalytics = ({
|
|
||||||
request,
|
|
||||||
event
|
|
||||||
}: {
|
|
||||||
request: NextRequest | NextApiRequest | Request
|
|
||||||
event?: NextFetchEvent
|
|
||||||
}) => {
|
|
||||||
const endpoint = process.env.VERCEL_URL
|
|
||||||
|
|
||||||
return {
|
|
||||||
track: async (eventName: string, data?: any) => {
|
|
||||||
try {
|
|
||||||
if (!endpoint && process.env.NODE_ENV === 'development') {
|
|
||||||
console.log(
|
|
||||||
`[Vercel Web Analytics] Track "${eventName}"` +
|
|
||||||
(data ? ` with data ${JSON.stringify(data || {})}` : '')
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers: { [key: string]: string } = {}
|
|
||||||
Object.entries(request.headers).map(([key, value]) => {
|
|
||||||
headers[key] = value
|
|
||||||
})
|
|
||||||
|
|
||||||
const body = {
|
|
||||||
o: headers.referer,
|
|
||||||
ts: new Date().getTime(),
|
|
||||||
r: '',
|
|
||||||
en: eventName,
|
|
||||||
ed: data
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = fetch(
|
|
||||||
`https://${process.env.VERCEL_URL}/_vercel/insights/event`,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'content-type': 'application/json',
|
|
||||||
'user-agent': headers['user-agent'] as string,
|
|
||||||
'x-forwarded-for': headers['x-forwarded-for'] as string,
|
|
||||||
'x-va-server': '1'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(body),
|
|
||||||
method: 'POST'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (event) {
|
|
||||||
event.waitUntil(promise)
|
|
||||||
}
|
|
||||||
{
|
|
||||||
await promise
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
11
lib/fonts.ts
11
lib/fonts.ts
|
|
@ -1,11 +0,0 @@
|
||||||
import { JetBrains_Mono as FontMono, Inter as FontSans } from 'next/font/google'
|
|
||||||
|
|
||||||
export const fontSans = FontSans({
|
|
||||||
subsets: ['latin'],
|
|
||||||
variable: '--font-sans'
|
|
||||||
})
|
|
||||||
|
|
||||||
export const fontMono = FontMono({
|
|
||||||
subsets: ['latin'],
|
|
||||||
variable: '--font-mono'
|
|
||||||
})
|
|
||||||
|
|
@ -1,9 +1,5 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
reactStrictMode: true,
|
|
||||||
experimental: {
|
|
||||||
serverActions: true,
|
|
||||||
},
|
|
||||||
images: {
|
images: {
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
|
|
|
||||||
75
package.json
75
package.json
|
|
@ -1,9 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "next-template",
|
|
||||||
"version": "0.0.2",
|
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev --turbo",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
|
@ -14,55 +12,56 @@
|
||||||
"format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache"
|
"format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.4",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dialog": "^1.0.4",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
"@radix-ui/react-dropdown-menu": "^2.0.6",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-select": "^1.2.2",
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
"@radix-ui/react-separator": "^1.0.3",
|
"@radix-ui/react-separator": "^1.0.3",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
"@radix-ui/react-tooltip": "^1.0.6",
|
"@radix-ui/react-tooltip": "^1.0.7",
|
||||||
"@vercel/analytics": "^1.0.0",
|
"@vercel/analytics": "^1.1.1",
|
||||||
"@vercel/kv": "^0.2.1",
|
"@vercel/kv": "^1.0.0",
|
||||||
"@vercel/og": "^0.5.7",
|
"@vercel/og": "^0.5.20",
|
||||||
"ai": "^2.1.6",
|
"ai": "^2.2.25",
|
||||||
"class-variance-authority": "^0.4.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^1.2.1",
|
"clsx": "^2.0.0",
|
||||||
"focus-trap-react": "^10.1.1",
|
"focus-trap-react": "^10.2.3",
|
||||||
"nanoid": "^4.0.2",
|
"geist": "^1.1.0",
|
||||||
"next": "13.4.7-canary.1",
|
"nanoid": "^5.0.3",
|
||||||
"next-auth": "0.0.0-manual.83c4ebd1",
|
"next": "14.0.4-canary.17",
|
||||||
|
"next-auth": "5.0.0-beta.3",
|
||||||
"next-themes": "^0.2.1",
|
"next-themes": "^0.2.1",
|
||||||
"openai-edge": "^0.5.1",
|
"openai": "^4.20.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-intersection-observer": "^9.4.4",
|
"react-intersection-observer": "^9.5.3",
|
||||||
"react-markdown": "^8.0.7",
|
"react-markdown": "^8.0.7",
|
||||||
"react-syntax-highlighter": "^15.5.0",
|
"react-syntax-highlighter": "^15.5.0",
|
||||||
"react-textarea-autosize": "^8.4.1",
|
"react-textarea-autosize": "^8.5.3",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"remark-math": "^5.1.1"
|
"remark-math": "^5.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/typography": "^0.5.9",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@types/node": "^17.0.12",
|
"@types/node": "^20.10.0",
|
||||||
"@types/react": "^18.0.22",
|
"@types/react": "^18.2.38",
|
||||||
"@types/react-dom": "^18.0.7",
|
"@types/react-dom": "^18.2.17",
|
||||||
"@types/react-syntax-highlighter": "^15.5.6",
|
"@types/react-syntax-highlighter": "^15.5.10",
|
||||||
"@typescript-eslint/parser": "^5.59.7",
|
"@typescript-eslint/parser": "^6.12.0",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.16",
|
||||||
"eslint": "^8.31.0",
|
"eslint": "^8.54.0",
|
||||||
"eslint-config-next": "13.4.7-canary.1",
|
"eslint-config-next": "14.0.3",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-tailwindcss": "^3.12.0",
|
"eslint-plugin-tailwindcss": "^3.13.0",
|
||||||
"postcss": "^8.4.21",
|
"postcss": "^8.4.31",
|
||||||
"prettier": "^2.7.1",
|
"prettier": "^3.1.0",
|
||||||
"tailwind-merge": "^1.12.0",
|
"tailwind-merge": "^2.0.0",
|
||||||
"tailwindcss": "^3.3.1",
|
"tailwindcss": "^3.3.5",
|
||||||
"tailwindcss-animate": "^1.0.5",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.3.2"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@8.6.3"
|
"packageManager": "pnpm@8.6.3"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
3457
pnpm-lock.yaml
generated
3457
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,3 @@
|
||||||
const { fontFamily } = require('tailwindcss/defaultTheme')
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
darkMode: ['class'],
|
darkMode: ['class'],
|
||||||
|
|
@ -14,7 +12,8 @@ module.exports = {
|
||||||
},
|
},
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['var(--font-sans)', ...fontFamily.sans]
|
sans: ['var(--font-geist-sans)'],
|
||||||
|
mono: ['var(--font-geist-mono)']
|
||||||
},
|
},
|
||||||
colors: {
|
colors: {
|
||||||
border: 'hsl(var(--border))',
|
border: 'hsl(var(--border))',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue