Merge branch 'main' into fix/next-auth-protocol-detection

This commit is contained in:
Balázs Orbán 2023-06-22 14:39:50 +02:00 committed by GitHub
commit 8a5b2fdf84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 255 additions and 82 deletions

View file

@ -1,4 +1,5 @@
## Get your OpenAI API Key ## You must first activate a Billing Account here: https://platform.openai.com/account/billing/overview
## Then get your OpenAI API Key here: https://platform.openai.com/account/api-keys
OPENAI_API_KEY=XXXXXXXX OPENAI_API_KEY=XXXXXXXX
## Generate a random secret: https://generate-secret.vercel.app/32 ## Generate a random secret: https://generate-secret.vercel.app/32

View file

@ -21,7 +21,7 @@
- [Next.js](https://nextjs.org) App Router - [Next.js](https://nextjs.org) App Router
- 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, HuggingFace, or custom AI chat models and/or LangChain - Support for OpenAI (default), Anthropic, Hugging Face, or custom AI chat models and/or LangChain
- Edge runtime-ready - 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)
@ -32,7 +32,7 @@
## Model Providers ## 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), you can switch 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. This template ships with OpenAI `gpt-3.5-turbo` as the default. However, thanks to the [Vercel AI SDK](https://sdk.vercel.ai/docs), you can switch LLM providers to [Anthropic](https://anthropic.com), [Hugging Face](https://huggingface.co), or using [LangChain](https://js.langchain.com) with just a few lines of code.
## Deploy Your Own ## Deploy Your Own

View file

@ -74,7 +74,9 @@ export async function clearChats() {
} }
const chats: string[] = await kv.zrange(`user:chat:${session.user.id}`, 0, -1) const chats: string[] = await kv.zrange(`user:chat:${session.user.id}`, 0, -1)
if (!chats.length) {
return redirect('/')
}
const pipeline = kv.pipeline() const pipeline = kv.pipeline()
for (const chat of chats) { for (const chat of chats) {

View file

@ -1,2 +1 @@
export { GET, POST } from '@/auth' export { GET, POST } from '@/auth'
export const runtime = 'edge'

View file

@ -7,22 +7,26 @@ import { nanoid } from '@/lib/utils'
export const runtime = 'edge' export const runtime = 'edge'
const configuration = new Configuration({
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
const session = await auth() const session = await auth()
if (process.env.VERCEL_ENV !== 'preview') { if (process.env.VERCEL_ENV === 'preview') {
if (session == null) { if (session == null) {
return new Response('Unauthorized', { status: 401 }) return new Response('Unauthorized', { status: 401 })
} }
} }
const configuration = new Configuration({ if (previewToken) {
apiKey: previewToken || process.env.OPENAI_API_KEY configuration.apiKey = previewToken
}) }
const openai = new OpenAIApi(configuration)
const res = await openai.createChatCompletion({ const res = await openai.createChatCompletion({
model: 'gpt-3.5-turbo', model: 'gpt-3.5-turbo',
@ -34,7 +38,7 @@ export async function POST(req: Request) {
const stream = OpenAIStream(res, { const stream = OpenAIStream(res, {
async onCompletion(completion) { async onCompletion(completion) {
const title = json.messages[0].content.substring(0, 100) const title = json.messages[0].content.substring(0, 100)
const userId = session?.user.id const userId = session?.user?.id
if (userId) { if (userId) {
const id = json.id ?? nanoid() const id = json.id ?? nanoid()
const createdAt = Date.now() const createdAt = Date.now()

View file

@ -25,7 +25,7 @@ export async function generateMetadata({
const chat = await getChat(params.id, session.user.id) const chat = await getChat(params.id, session.user.id)
return { return {
title: chat?.title.slice(0, 50) ?? 'Chat' title: chat?.title.toString().slice(0, 50) ?? 'Chat'
} }
} }

View file

@ -35,9 +35,9 @@ export default async function SharePage({ params }: SharePageProps) {
return ( return (
<> <>
<div className="flex-1 space-y-6"> <div className="flex-1 space-y-6">
<div className="border-b bg-background px-4 py-6 md:px-6 md:py-8"> <div className="px-4 py-6 border-b bg-background md:px-6 md:py-8">
<div className="mx-auto max-w-2xl md:px-6"> <div className="max-w-2xl mx-auto md:px-6">
<div className="space-y-1"> <div className="space-y-1 md:-mx-8">
<h1 className="text-2xl font-bold">{chat.title}</h1> <h1 className="text-2xl font-bold">{chat.title}</h1>
<div className="text-sm text-muted-foreground"> <div className="text-sm text-muted-foreground">
{formatDate(chat.createdAt)} · {chat.messages.length} messages {formatDate(chat.createdAt)} · {chat.messages.length} messages

15
auth.ts
View file

@ -1,25 +1,18 @@
import NextAuth from 'next-auth' import NextAuth from 'next-auth'
import GitHub from 'next-auth/providers/github' import GitHub from 'next-auth/providers/github'
declare module 'next-auth' {
interface Session {
user: {
/** The user's id. */
id: string
}
}
}
export const { export const {
handlers: { GET, POST }, handlers: { GET, POST },
auth, auth,
CSRF_experimental CSRF_experimental
// @ts-ignore
} = NextAuth({ } = NextAuth({
// @ts-expect-error // @ts-ignore
providers: [GitHub], providers: [GitHub],
callbacks: { callbacks: {
// @ts-ignore
jwt: async ({ token, profile }) => { jwt: async ({ token, profile }) => {
if (profile) { if (profile?.id) {
token.id = profile.id token.id = profile.id
token.image = profile.picture token.image = profile.picture
} }

View file

@ -18,6 +18,11 @@ export function ChatMessageActions({
}: ChatMessageActionsProps) { }: ChatMessageActionsProps) {
const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }) const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
const onCopy = () => {
if (isCopied) return
copyToClipboard(message.content)
}
return ( return (
<div <div
className={cn( className={cn(
@ -26,11 +31,7 @@ export function ChatMessageActions({
)} )}
{...props} {...props}
> >
<Button <Button variant="ghost" size="icon" onClick={onCopy}>
variant="ghost"
size="icon"
onClick={() => copyToClipboard(message.content)}
>
{isCopied ? <IconCheck /> : <IconCopy />} {isCopied ? <IconCheck /> : <IconCopy />}
<span className="sr-only">Copy message</span> <span className="sr-only">Copy message</span>
</Button> </Button>

View file

@ -4,7 +4,7 @@ import Link from 'next/link'
import { cn } from '@/lib/utils' import { cn } from '@/lib/utils'
import { auth } from '@/auth' import { auth } from '@/auth'
import { clearChats } from '@/app/actions' import { clearChats } from '@/app/actions'
import { buttonVariants } from '@/components/ui/button' import { Button, buttonVariants } from '@/components/ui/button'
import { Sidebar } from '@/components/sidebar' import { Sidebar } from '@/components/sidebar'
import { SidebarList } from '@/components/sidebar-list' import { SidebarList } from '@/components/sidebar-list'
import { import {
@ -22,7 +22,7 @@ import { LoginButton } from '@/components/login-button'
export async function Header() { export async function Header() {
const session = await auth() const session = await auth()
return ( return (
<header className="sticky top-0 z-50 flex h-16 w-full shrink-0 items-center justify-between border-b bg-gradient-to-b from-background/10 via-background/50 to-background/80 px-4 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 ? ( {session?.user ? (
<Sidebar> <Sidebar>
@ -37,21 +37,18 @@ export async function Header() {
</Sidebar> </Sidebar>
) : ( ) : (
<Link href="/" target="_blank" rel="nofollow"> <Link href="/" target="_blank" rel="nofollow">
<IconNextChat className="mr-2 h-6 w-6 dark:hidden" inverted /> <IconNextChat className="w-6 h-6 mr-2 dark:hidden" inverted />
<IconNextChat className="mr-2 hidden h-6 w-6 dark:block" /> <IconNextChat className="hidden w-6 h-6 mr-2 dark:block" />
</Link> </Link>
)} )}
<div className="flex items-center"> <div className="flex items-center">
<IconSeparator className="h-6 w-6 text-muted-foreground/50" /> <IconSeparator className="w-6 h-6 text-muted-foreground/50" />
{session?.user ? ( {session?.user ? (
<UserMenu user={session.user} /> <UserMenu user={session.user} />
) : ( ) : (
<LoginButton <Button variant="link" asChild className="-ml-2">
variant="link" <Link href="/sign-in?callbackUrl=/">Login</Link>
showGithubIcon={false} </Button>
text="Login"
className="-ml-2"
/>
)} )}
</div> </div>
</div> </div>
@ -63,7 +60,7 @@ export async function Header() {
className={cn(buttonVariants({ variant: 'outline' }))} className={cn(buttonVariants({ variant: 'outline' }))}
> >
<IconGitHub /> <IconGitHub />
<span className="ml-2 hidden md:flex">GitHub</span> <span className="hidden ml-2 md:flex">GitHub</span>
</a> </a>
<a <a
href="https://github.com/vercel/nextjs-ai-chatbot/" href="https://github.com/vercel/nextjs-ai-chatbot/"

View file

@ -24,6 +24,7 @@ export function LoginButton({
variant="outline" variant="outline"
onClick={() => { onClick={() => {
setIsLoading(true) setIsLoading(true)
// next-auth signIn() function doesn't work yet at Edge Runtime due to usage of BroadcastChannel
signIn('github', { callbackUrl: `/` }) signIn('github', { callbackUrl: `/` })
}} }}
disabled={isLoading} disabled={isLoading}

View file

@ -15,7 +15,7 @@ import { IconArrowElbow, IconPlus } from '@/components/ui/icons'
export interface PromptProps export interface PromptProps
extends Pick<UseChatHelpers, 'input' | 'setInput'> { extends Pick<UseChatHelpers, 'input' | 'setInput'> {
onSubmit: (value: string) => void onSubmit: (value: string) => Promise<void>
isLoading: boolean isLoading: boolean
} }
@ -38,7 +38,7 @@ export function PromptForm({
<form <form
onSubmit={async e => { onSubmit={async e => {
e.preventDefault() e.preventDefault()
if (input === '') { if (!input?.trim()) {
return return
} }
setInput('') setInput('')
@ -46,7 +46,7 @@ export function PromptForm({
}} }}
ref={formRef} ref={formRef}
> >
<div className="relative flex w-full grow flex-col overflow-hidden bg-background px-8 sm:rounded-md sm:border sm:px-12"> <div className="relative flex max-h-60 w-full grow flex-col overflow-hidden bg-background px-8 sm:rounded-md sm:border sm:px-12">
<Tooltip> <Tooltip>
<TooltipTrigger asChild> <TooltipTrigger asChild>
<Link <Link

View file

@ -84,6 +84,11 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
} }
const onCopy = () => {
if (isCopied) return
copyToClipboard(value)
}
return ( return (
<div className="codeblock relative w-full bg-zinc-950 font-sans"> <div className="codeblock relative w-full bg-zinc-950 font-sans">
<div className="flex w-full items-center justify-between bg-zinc-800 px-6 py-2 pr-4 text-zinc-100"> <div className="flex w-full items-center justify-between bg-zinc-800 px-6 py-2 pr-4 text-zinc-100">
@ -102,7 +107,7 @@ const CodeBlock: FC<Props> = memo(({ language, value }) => {
variant="ghost" variant="ghost"
size="icon" size="icon"
className="text-xs hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0" className="text-xs hover:bg-zinc-800 focus-visible:ring-1 focus-visible:ring-slate-700 focus-visible:ring-offset-0"
onClick={() => copyToClipboard(value)} onClick={onCopy}
> >
{isCopied ? <IconCheck /> : <IconCopy />} {isCopied ? <IconCheck /> : <IconCopy />}
<span className="sr-only">Copy code</span> <span className="sr-only">Copy code</span>

View file

@ -12,11 +12,11 @@ export function useCopyToClipboard({
const [isCopied, setIsCopied] = React.useState<Boolean>(false) const [isCopied, setIsCopied] = React.useState<Boolean>(false)
const copyToClipboard = (value: string) => { const copyToClipboard = (value: string) => {
if ( if (typeof window === 'undefined' || !navigator.clipboard?.writeText) {
typeof window === 'undefined' || return
!navigator.clipboard || }
!navigator.clipboard.writeText
) { if (!value) {
return return
} }

View file

@ -9,7 +9,11 @@ export function useEnterSubmit(): {
const handleKeyDown = ( const handleKeyDown = (
event: React.KeyboardEvent<HTMLTextAreaElement> event: React.KeyboardEvent<HTMLTextAreaElement>
): void => { ): void => {
if (event.key === 'Enter' && !event.shiftKey) { if (
event.key === 'Enter' &&
!event.shiftKey &&
!event.nativeEvent.isComposing
) {
formRef.current?.requestSubmit() formRef.current?.requestSubmit()
event.preventDefault() event.preventDefault()
} }

13
next-auth.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
import NextAuth, { DefaultSession } from 'next-auth'
declare module 'next-auth' {
/**
* Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context
*/
interface Session {
user: {
/** The user's postal address. */
id: string
} & DefaultSession['user']
}
}

View file

@ -26,14 +26,14 @@
"@vercel/analytics": "^1.0.0", "@vercel/analytics": "^1.0.0",
"@vercel/kv": "^0.2.1", "@vercel/kv": "^0.2.1",
"@vercel/og": "^0.5.7", "@vercel/og": "^0.5.7",
"ai": "^2.1.2", "ai": "^2.1.6",
"body-scroll-lock": "4.0.0-beta.0", "body-scroll-lock": "4.0.0-beta.0",
"class-variance-authority": "^0.4.0", "class-variance-authority": "^0.4.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"focus-trap-react": "^10.1.1", "focus-trap-react": "^10.1.1",
"nanoid": "^4.0.2", "nanoid": "^4.0.2",
"next": "13.4.7-canary.1", "next": "13.4.7-canary.1",
"next-auth": "0.0.0-manual.f414f2f5", "next-auth": "0.0.0-manual.4cd21ea5",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"openai-edge": "^0.5.1", "openai-edge": "^0.5.1",
"react": "^18.2.0", "react": "^18.2.0",
@ -67,5 +67,10 @@
"tailwindcss-animate": "^1.0.5", "tailwindcss-animate": "^1.0.5",
"typescript": "^5.1.3" "typescript": "^5.1.3"
}, },
"pnpm": {
"overrides": {
"@auth/nextjs": "0.0.0-manual.223c6467"
}
},
"packageManager": "pnpm@8.6.1" "packageManager": "pnpm@8.6.1"
} }

196
pnpm-lock.yaml generated
View file

@ -1,9 +1,12 @@
lockfileVersion: '6.1' lockfileVersion: '6.0'
settings: settings:
autoInstallPeers: false autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
'@auth/nextjs': 0.0.0-manual.223c6467
dependencies: dependencies:
'@radix-ui/react-alert-dialog': '@radix-ui/react-alert-dialog':
specifier: ^1.0.4 specifier: ^1.0.4
@ -42,8 +45,8 @@ dependencies:
specifier: ^0.5.7 specifier: ^0.5.7
version: 0.5.7 version: 0.5.7
ai: ai:
specifier: ^2.1.2 specifier: ^2.1.6
version: 2.1.2(react@18.2.0) version: 2.1.6(react@18.2.0)(svelte@3.59.2)(vue@3.3.4)
body-scroll-lock: body-scroll-lock:
specifier: 4.0.0-beta.0 specifier: 4.0.0-beta.0
version: 4.0.0-beta.0 version: 4.0.0-beta.0
@ -55,7 +58,7 @@ dependencies:
version: 1.2.1 version: 1.2.1
focus-trap-react: focus-trap-react:
specifier: ^10.1.1 specifier: ^10.1.1
version: 10.1.1(react-dom@18.2.0)(react@18.2.0) version: 10.1.1(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0)
nanoid: nanoid:
specifier: ^4.0.2 specifier: ^4.0.2
version: 4.0.2 version: 4.0.2
@ -63,8 +66,8 @@ dependencies:
specifier: 13.4.7-canary.1 specifier: 13.4.7-canary.1
version: 13.4.7-canary.1(react-dom@18.2.0)(react@18.2.0) version: 13.4.7-canary.1(react-dom@18.2.0)(react@18.2.0)
next-auth: next-auth:
specifier: 0.0.0-manual.f414f2f5 specifier: 0.0.0-manual.4cd21ea5
version: 0.0.0-manual.f414f2f5(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0) version: 0.0.0-manual.4cd21ea5(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0)
next-themes: next-themes:
specifier: ^0.2.1 specifier: ^0.2.1
version: 0.2.1(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0) version: 0.2.1(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0)
@ -79,7 +82,7 @@ dependencies:
version: 18.2.0(react@18.2.0) version: 18.2.0(react@18.2.0)
react-hot-toast: react-hot-toast:
specifier: ^2.4.1 specifier: ^2.4.1
version: 2.4.1(react-dom@18.2.0)(react@18.2.0) version: 2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0)
react-intersection-observer: react-intersection-observer:
specifier: ^9.4.4 specifier: ^9.4.4
version: 9.4.4(react@18.2.0) version: 9.4.4(react@18.2.0)
@ -184,7 +187,7 @@ packages:
/@auth/nextjs@0.0.0-manual.179c08d4(next@13.4.7-canary.1)(react@18.2.0): /@auth/nextjs@0.0.0-manual.179c08d4(next@13.4.7-canary.1)(react@18.2.0):
resolution: {integrity: sha512-HVuDgb6xlYLV5mHTGc0Pycg8BlwtcvjvS+54QXEbOBpA5tulzKxAH8QlCoKj5R9XHW9hP9DiCnvXyVV9x2N+jA==} resolution: {integrity: sha512-HVuDgb6xlYLV5mHTGc0Pycg8BlwtcvjvS+54QXEbOBpA5tulzKxAH8QlCoKj5R9XHW9hP9DiCnvXyVV9x2N+jA==}
peerDependencies: peerDependencies:
next: ^13.4.6 next: ^13.4.2
react: ^18.2.0 react: ^18.2.0
dependencies: dependencies:
'@auth/core': 0.0.0-manual.8fcd46b0 '@auth/core': 0.0.0-manual.8fcd46b0
@ -194,12 +197,39 @@ packages:
- nodemailer - nodemailer
dev: false dev: false
/@babel/helper-string-parser@7.22.5:
resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/helper-validator-identifier@7.22.5:
resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==}
engines: {node: '>=6.9.0'}
dev: false
/@babel/parser@7.22.5:
resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==}
engines: {node: '>=6.0.0'}
hasBin: true
dependencies:
'@babel/types': 7.22.5
dev: false
/@babel/runtime@7.21.5: /@babel/runtime@7.21.5:
resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==} resolution: {integrity: sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==}
engines: {node: '>=6.9.0'} engines: {node: '>=6.9.0'}
dependencies: dependencies:
regenerator-runtime: 0.13.11 regenerator-runtime: 0.13.11
/@babel/types@7.22.5:
resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==}
engines: {node: '>=6.9.0'}
dependencies:
'@babel/helper-string-parser': 7.22.5
'@babel/helper-validator-identifier': 7.22.5
to-fast-properties: 2.0.0
dev: false
/@esbuild/android-arm@0.15.18: /@esbuild/android-arm@0.15.18:
resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==} resolution: {integrity: sha512-5GT+kcs2WVGjVs7+boataCkO5Fg0y4kCjzkB5bAip7H4jfnOS3dA6KPiww9W1OEKTKeAcUVhdZGvgI65OXmUnw==}
engines: {node: '>=12'} engines: {node: '>=12'}
@ -321,7 +351,6 @@ packages:
/@jridgewell/sourcemap-codec@1.4.15: /@jridgewell/sourcemap-codec@1.4.15:
resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
dev: true
/@jridgewell/trace-mapping@0.3.18: /@jridgewell/trace-mapping@0.3.18:
resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==}
@ -1358,6 +1387,89 @@ packages:
yoga-wasm-web: 0.3.3 yoga-wasm-web: 0.3.3
dev: false dev: false
/@vue/compiler-core@3.3.4:
resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
dependencies:
'@babel/parser': 7.22.5
'@vue/shared': 3.3.4
estree-walker: 2.0.2
source-map-js: 1.0.2
dev: false
/@vue/compiler-dom@3.3.4:
resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
dependencies:
'@vue/compiler-core': 3.3.4
'@vue/shared': 3.3.4
dev: false
/@vue/compiler-sfc@3.3.4:
resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
dependencies:
'@babel/parser': 7.22.5
'@vue/compiler-core': 3.3.4
'@vue/compiler-dom': 3.3.4
'@vue/compiler-ssr': 3.3.4
'@vue/reactivity-transform': 3.3.4
'@vue/shared': 3.3.4
estree-walker: 2.0.2
magic-string: 0.30.0
postcss: 8.4.23
source-map-js: 1.0.2
dev: false
/@vue/compiler-ssr@3.3.4:
resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
dependencies:
'@vue/compiler-dom': 3.3.4
'@vue/shared': 3.3.4
dev: false
/@vue/reactivity-transform@3.3.4:
resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
dependencies:
'@babel/parser': 7.22.5
'@vue/compiler-core': 3.3.4
'@vue/shared': 3.3.4
estree-walker: 2.0.2
magic-string: 0.30.0
dev: false
/@vue/reactivity@3.3.4:
resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==}
dependencies:
'@vue/shared': 3.3.4
dev: false
/@vue/runtime-core@3.3.4:
resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==}
dependencies:
'@vue/reactivity': 3.3.4
'@vue/shared': 3.3.4
dev: false
/@vue/runtime-dom@3.3.4:
resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==}
dependencies:
'@vue/runtime-core': 3.3.4
'@vue/shared': 3.3.4
csstype: 3.1.2
dev: false
/@vue/server-renderer@3.3.4(vue@3.3.4):
resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==}
peerDependencies:
vue: 3.3.4
dependencies:
'@vue/compiler-ssr': 3.3.4
'@vue/shared': 3.3.4
vue: 3.3.4
dev: false
/@vue/shared@3.3.4:
resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
dev: false
/acorn-jsx@5.3.2(acorn@8.8.2): /acorn-jsx@5.3.2(acorn@8.8.2):
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies: peerDependencies:
@ -1372,8 +1484,8 @@ packages:
hasBin: true hasBin: true
dev: true dev: true
/ai@2.1.2(react@18.2.0): /ai@2.1.6(react@18.2.0)(svelte@3.59.2)(vue@3.3.4):
resolution: {integrity: sha512-WhyGH95Pv/gpgVMQT5eCyKXJh7irozr+bmpJ94somfRYBcAN2U270ii/OOHzGLrPM3liTbBilGQL2Ds7/5sz9Q==} resolution: {integrity: sha512-YFCy7KEUDvuNGNuD8Bz7BynXB/eW2jJRAxAIM8Hb5/gpppZJs6/nTSdpocROxoGESJchtkFgYdygieG/wciEEQ==}
engines: {node: '>=14.6'} engines: {node: '>=14.6'}
peerDependencies: peerDependencies:
react: ^18.0.0 react: ^18.0.0
@ -1390,9 +1502,11 @@ packages:
eventsource-parser: 1.0.0 eventsource-parser: 1.0.0
nanoid: 3.3.6 nanoid: 3.3.6
react: 18.2.0 react: 18.2.0
sswr: 1.10.0 sswr: 1.10.0(svelte@3.59.2)
svelte: 3.59.2
swr: 2.1.5(react@18.2.0) swr: 2.1.5(react@18.2.0)
swrv: 1.0.3 swrv: 1.0.3(vue@3.3.4)
vue: 3.3.4
dev: false dev: false
/ajv@6.12.6: /ajv@6.12.6:
@ -2644,6 +2758,10 @@ packages:
engines: {node: '>=4.0'} engines: {node: '>=4.0'}
dev: true dev: true
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: false
/esutils@2.0.3: /esutils@2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@ -2774,7 +2892,7 @@ packages:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
dev: true dev: true
/focus-trap-react@10.1.1(react-dom@18.2.0)(react@18.2.0): /focus-trap-react@10.1.1(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-OtLeSIQPKFzMzbLHkGtfZYwGLMhTRHd3CDhfyd0DDx8tvXzlgpseClDiuiKoiIHZtdjsbXTfTmUuuLKaxrwSyQ==} resolution: {integrity: sha512-OtLeSIQPKFzMzbLHkGtfZYwGLMhTRHd3CDhfyd0DDx8tvXzlgpseClDiuiKoiIHZtdjsbXTfTmUuuLKaxrwSyQ==}
peerDependencies: peerDependencies:
prop-types: ^15.8.1 prop-types: ^15.8.1
@ -2782,6 +2900,7 @@ packages:
react-dom: '>=16.3.0' react-dom: '>=16.3.0'
dependencies: dependencies:
focus-trap: 7.4.0 focus-trap: 7.4.0
prop-types: 15.8.1
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
tabbable: 6.1.2 tabbable: 6.1.2
@ -2967,10 +3086,12 @@ packages:
slash: 4.0.0 slash: 4.0.0
dev: true dev: true
/goober@2.1.13: /goober@2.1.13(csstype@3.1.2):
resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==} resolution: {integrity: sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==}
peerDependencies: peerDependencies:
csstype: ^3.0.10 csstype: ^3.0.10
dependencies:
csstype: 3.1.2
dev: false dev: false
/gopd@1.0.1: /gopd@1.0.1:
@ -3515,6 +3636,13 @@ packages:
es5-ext: 0.10.62 es5-ext: 0.10.62
dev: true dev: true
/magic-string@0.30.0:
resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
engines: {node: '>=12'}
dependencies:
'@jridgewell/sourcemap-codec': 1.4.15
dev: false
/markdown-table@3.0.3: /markdown-table@3.0.3:
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
dev: false dev: false
@ -4017,8 +4145,8 @@ packages:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true dev: true
/next-auth@0.0.0-manual.f414f2f5(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0): /next-auth@0.0.0-manual.4cd21ea5(next@13.4.7-canary.1)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-qJmSX4QUV8V2JOQavl0fQyJymt0fte7eDQsMGTpx8ZRU2Qvx8uh+xrvXlHt3dXf603OB3QkWaj77ipIEt62Hfg==} resolution: {integrity: sha512-qzsrs2xeopmIYUz1C+FbudBIoNI7xSTd1xWeECr/Qa4NFzbCt8Dr9tb2Yocpa5jnRpk0xN96HwsJqimOYVjtRg==}
peerDependencies: peerDependencies:
next: ^13.4.5 next: ^13.4.5
nodemailer: ^6.6.5 nodemailer: ^6.6.5
@ -4429,7 +4557,6 @@ packages:
nanoid: 3.3.6 nanoid: 3.3.6
picocolors: 1.0.0 picocolors: 1.0.0
source-map-js: 1.0.2 source-map-js: 1.0.2
dev: true
/preact-render-to-string@5.2.3(preact@10.11.3): /preact-render-to-string@5.2.3(preact@10.11.3):
resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
@ -4505,14 +4632,14 @@ packages:
scheduler: 0.23.0 scheduler: 0.23.0
dev: false dev: false
/react-hot-toast@2.4.1(react-dom@18.2.0)(react@18.2.0): /react-hot-toast@2.4.1(csstype@3.1.2)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==} resolution: {integrity: sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
peerDependencies: peerDependencies:
react: '>=16' react: '>=16'
react-dom: '>=16' react-dom: '>=16'
dependencies: dependencies:
goober: 2.1.13 goober: 2.1.13(csstype@3.1.2)
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0(react@18.2.0) react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies: transitivePeerDependencies:
@ -4867,11 +4994,12 @@ packages:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
dev: false dev: false
/sswr@1.10.0: /sswr@1.10.0(svelte@3.59.2):
resolution: {integrity: sha512-nLWAJSQy3h8t7rrbTXanRyVHuQPj4PwKIVGe4IMlxJFdhyaxnN/JGACnvQKGDeWiTGYIZIx/jRuUsPEF0867Pg==} resolution: {integrity: sha512-nLWAJSQy3h8t7rrbTXanRyVHuQPj4PwKIVGe4IMlxJFdhyaxnN/JGACnvQKGDeWiTGYIZIx/jRuUsPEF0867Pg==}
peerDependencies: peerDependencies:
svelte: ^3.29.0 svelte: ^3.29.0
dependencies: dependencies:
svelte: 3.59.2
swrev: 3.0.0 swrev: 3.0.0
dev: false dev: false
@ -5005,6 +5133,11 @@ packages:
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
dev: true dev: true
/svelte@3.59.2:
resolution: {integrity: sha512-vzSyuGr3eEoAtT/A6bmajosJZIUWySzY2CzB3w2pgPvnkUjGqlDnsNnA0PMO+mMAhuyMul6C2uuZzY6ELSkzyA==}
engines: {node: '>= 8'}
dev: false
/swr@2.1.5(react@18.2.0): /swr@2.1.5(react@18.2.0):
resolution: {integrity: sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==} resolution: {integrity: sha512-/OhfZMcEpuz77KavXST5q6XE9nrOBOVcBLWjMT+oAE/kQHyE3PASrevXCtQDZ8aamntOfFkbVJp7Il9tNBQWrw==}
peerDependencies: peerDependencies:
@ -5018,10 +5151,12 @@ packages:
resolution: {integrity: sha512-QJuZiptdOmbDY45pECBRVEgnoBlOKjeT2MWVz04wKHpWX15hM3P7EjcIbHDg5yLoPCMQ7to3349MEE+l9QF5HA==} resolution: {integrity: sha512-QJuZiptdOmbDY45pECBRVEgnoBlOKjeT2MWVz04wKHpWX15hM3P7EjcIbHDg5yLoPCMQ7to3349MEE+l9QF5HA==}
dev: false dev: false
/swrv@1.0.3: /swrv@1.0.3(vue@3.3.4):
resolution: {integrity: sha512-sl+eLEE+aPPjhP1E8gQ75q3RPRyw5Gd/kROnrTFo3+LkCeLskv7F+uAl5W97wgJkzitobL6FLsRPVm0DgIgN8A==} resolution: {integrity: sha512-sl+eLEE+aPPjhP1E8gQ75q3RPRyw5Gd/kROnrTFo3+LkCeLskv7F+uAl5W97wgJkzitobL6FLsRPVm0DgIgN8A==}
peerDependencies: peerDependencies:
vue: '>=3.2.26 < 4' vue: '>=3.2.26 < 4'
dependencies:
vue: 3.3.4
dev: false dev: false
/synckit@0.8.5: /synckit@0.8.5:
@ -5118,6 +5253,11 @@ packages:
engines: {node: '>=12'} engines: {node: '>=12'}
dev: true dev: true
/to-fast-properties@2.0.0:
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
engines: {node: '>=4'}
dev: false
/to-regex-range@5.0.1: /to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
@ -5392,6 +5532,16 @@ packages:
vfile-message: 3.1.4 vfile-message: 3.1.4
dev: false dev: false
/vue@3.3.4:
resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
dependencies:
'@vue/compiler-dom': 3.3.4
'@vue/compiler-sfc': 3.3.4
'@vue/runtime-dom': 3.3.4
'@vue/server-renderer': 3.3.4(vue@3.3.4)
'@vue/shared': 3.3.4
dev: false
/watchpack@2.4.0: /watchpack@2.4.0:
resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}

View file

@ -56,9 +56,6 @@ module.exports = {
md: `calc(var(--radius) - 2px)`, md: `calc(var(--radius) - 2px)`,
sm: 'calc(var(--radius) - 4px)' sm: 'calc(var(--radius) - 4px)'
}, },
fontFamily: {
sans: ['var(--font-sans)', ...fontFamily.sans]
},
keyframes: { keyframes: {
'accordion-down': { 'accordion-down': {
from: { height: 0 }, from: { height: 0 },

View file

@ -26,6 +26,7 @@
}, },
"include": [ "include": [
"next-env.d.ts", "next-env.d.ts",
"next-auth.d.ts",
"**/*.ts", "**/*.ts",
"**/*.tsx", "**/*.tsx",
".next/types/**/*.ts" ".next/types/**/*.ts"