diff --git a/app/(logout)/sign-in/page.tsx b/app/(logout)/sign-in/page.tsx index f302d3c2a260597734a01c9b3d8de79ce842e709..7516b70783c0658bfb62f301a674012b2dacb7b8 100644 --- a/app/(logout)/sign-in/page.tsx +++ b/app/(logout)/sign-in/page.tsx @@ -3,16 +3,16 @@ import { LoginButton } from '@/components/LoginButton'; import { redirect } from 'next/navigation'; export default async function SignInPage() { - const session = await auth(); - // redirect to home if user is already logged in - if (session?.user) { - redirect('/'); - } + const session = await auth(); + // redirect to home if user is already logged in + if (session?.user) { + redirect('/'); + } - return ( -
- - -
- ); + return ( +
+ + +
+ ); } diff --git a/app/(logout)/unauthorized/page.tsx b/app/(logout)/unauthorized/page.tsx index 83cf9d22d891d3d9c4e624d713f8dd626e4f8a7f..7e73cb3b8b54ce514c3e277e86604bcfa5d1d7c4 100644 --- a/app/(logout)/unauthorized/page.tsx +++ b/app/(logout)/unauthorized/page.tsx @@ -4,21 +4,21 @@ import { Button } from '@/components/ui/Button'; import Link from 'next/link'; export default async function Unauthorized() { - const session = await auth(); - // redirect to home if user is already logged in - if (session?.user) { - redirect('/'); - } + const session = await auth(); + // redirect to home if user is already logged in + if (session?.user) { + redirect('/'); + } - return ( -
-
- You are not authorized to view this page. Please sign in with Landing - account to continue. -
- -
- ); + return ( +
+
+ You are not authorized to view this page. Please sign in with Landing + account to continue. +
+ +
+ ); } diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index 883210bb213e0a3f7a58029dd681b049dc7bd5ba..74197d24391f596ce15dec509e3080fea9defbc0 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 { GET, POST } from '@/auth'; +export const runtime = 'edge'; diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index e8227fbe8f791f236b1134d2da1cbf94a00f4204..df1acf5d684ae230d05706e6a9a48155db9f2204 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -3,9 +3,9 @@ import OpenAI from 'openai'; import { auth } from '@/auth'; import { - ChatCompletionMessageParam, - ChatCompletionContentPart, - ChatCompletionContentPartImage, + ChatCompletionMessageParam, + ChatCompletionContentPart, + ChatCompletionContentPartImage, } from 'openai/resources'; import { MessageWithSelectedDataset } from '../../../lib/types'; // import { postAgentChat } from '@/lib/fetch'; @@ -13,56 +13,56 @@ import { MessageWithSelectedDataset } from '../../../lib/types'; export const runtime = 'edge'; const openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, + apiKey: process.env.OPENAI_API_KEY, }); export async function POST(req: Request) { - const json = await req.json(); - const { messages } = json as { - messages: MessageWithSelectedDataset[]; - }; - console.log('[Ming] ~ POST ~ messages:', messages); + const json = await req.json(); + const { messages } = json as { + messages: MessageWithSelectedDataset[]; + }; + console.log('[Ming] ~ POST ~ messages:', messages); - const session = await auth(); - if (!session?.user?.email) { - return new Response('Unauthorized', { - status: 401, - }); - } + const session = await auth(); + if (!session?.user?.email) { + return new Response('Unauthorized', { + status: 401, + }); + } - const formattedMessage: ChatCompletionMessageParam[] = messages.map( - message => { - const { dataset, ...rest } = message; + const formattedMessage: ChatCompletionMessageParam[] = messages.map( + message => { + const { dataset, ...rest } = message; - const contentWithImage: ChatCompletionContentPart[] = [ - { - type: 'text', - text: message.content as string, - }, - ...(dataset ?? []).map( - entity => - ({ - type: 'image_url', - image_url: { url: entity.url }, - }) satisfies ChatCompletionContentPartImage, - ), - ]; - return { - role: 'user', - content: contentWithImage, - }; - }, - ); + const contentWithImage: ChatCompletionContentPart[] = [ + { + type: 'text', + text: message.content as string, + }, + ...(dataset ?? []).map( + entity => + ({ + type: 'image_url', + image_url: { url: entity.url }, + }) satisfies ChatCompletionContentPartImage, + ), + ]; + return { + role: 'user', + content: contentWithImage, + }; + }, + ); - const res = await openai.chat.completions.create({ - model: 'gpt-4-vision-preview', - messages: formattedMessage, - temperature: 0.3, - stream: true, - max_tokens: 300, - }); + const res = await openai.chat.completions.create({ + model: 'gpt-4-vision-preview', + messages: formattedMessage, + temperature: 0.3, + stream: true, + max_tokens: 300, + }); - const stream = OpenAIStream(res); + const stream = OpenAIStream(res); - return new StreamingTextResponse(stream); + return new StreamingTextResponse(stream); } diff --git a/app/api/upload/route.ts b/app/api/upload/route.ts index ce9354fc2d53006abae76e1edeb0abc8f94c5455..d6ad947f4d177ea94dedf3becf20b0e62bbe3f16 100644 --- a/app/api/upload/route.ts +++ b/app/api/upload/route.ts @@ -4,29 +4,29 @@ import { kv } from '@vercel/kv'; import { format } from 'date-fns'; export async function POST(req: Request) { - const session = await auth(); - console.log('[Ming] ~ POST ~ session:', session); - const email = session?.user?.email; - if (!email) { - return new Response('Unauthorized', { - status: 401, - }); - } + const session = await auth(); + console.log('[Ming] ~ POST ~ session:', session); + const email = session?.user?.email; + if (!email) { + return new Response('Unauthorized', { + status: 401, + }); + } - const json = await req.json(); - console.log('[Ming] ~ POST ~ json:', json); + const json = await req.json(); + console.log('[Ming] ~ POST ~ json:', json); - const id = nanoid(); + const id = nanoid(); - await kv.hmset(`chat:${id}`, json); - await kv.zadd(`user:chat:${email}`, { - score: Date.now(), - member: `chat:${id}`, - }); - await kv.zadd('user:chat:all', { - score: Date.now(), - member: `chat:${id}`, - }); + await kv.hmset(`chat:${id}`, json); + await kv.zadd(`user:chat:${email}`, { + score: Date.now(), + member: `chat:${id}`, + }); + await kv.zadd('user:chat:all', { + score: Date.now(), + member: `chat:${id}`, + }); - return 'success'; + return 'success'; } diff --git a/app/chat/layout.tsx b/app/chat/layout.tsx index 5dbaf0fd4663c6401fd3b4028580e69298bc3c87..279ff0a27ec2e1b579659db29b820af40ab75c7a 100644 --- a/app/chat/layout.tsx +++ b/app/chat/layout.tsx @@ -3,25 +3,25 @@ import Loading from '@/components/ui/Loading'; import { Suspense } from 'react'; interface ChatLayoutProps { - children: React.ReactNode; + children: React.ReactNode; } export default async function Layout({ children }: ChatLayoutProps) { - return ( -
-
- }> - - -
- }> -
- {children} -
-
-
- ); + return ( +
+
+ }> + + +
+ }> +
+ {children} +
+
+
+ ); } diff --git a/app/chat/page.tsx b/app/chat/page.tsx index 0b643506bde0ddddecea21cad18533a840f67d37..17209d45bbd24b614f8111bde04575662159a53e 100644 --- a/app/chat/page.tsx +++ b/app/chat/page.tsx @@ -2,6 +2,6 @@ import { nanoid } from '@/lib/utils'; import { Chat } from '@/components/chat'; export default function Page() { - const id = nanoid(); - return ; + const id = nanoid(); + return ; } diff --git a/app/globals.css b/app/globals.css index 92cc978fbdd094b6219d94fb4ca2f5176519ba8e..04add7d3b944671afc4a0a4d07be88e17d3f0ec6 100644 --- a/app/globals.css +++ b/app/globals.css @@ -3,110 +3,110 @@ @tailwind utilities; @layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-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%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; - --accent: 240 4.8% 95.9%; - --accent-foreground: ; + --accent: 240 4.8% 95.9%; + --accent-foreground: ; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; - --ring: 240 5% 64.9%; + --ring: 240 5% 64.9%; - --radius: 0.5rem; - } + --radius: 0.5rem; + } - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; + .dark { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-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%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; - --accent: 240 3.7% 15.9%; - --accent-foreground: ; + --accent: 240 3.7% 15.9%; + --accent-foreground: ; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 85.7% 97.3%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 0 85.7% 97.3%; - --ring: 240 3.7% 15.9%; - } + --ring: 240 3.7% 15.9%; + } } @layer base { - * { - @apply border-border; - } - body { - @apply bg-background text-foreground; - } + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } } @layer components { - .scroll-fade::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 50px; - background: linear-gradient( - to bottom, - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); - pointer-events: none; - } - .scroll-fade:active::after, - .scroll-fade:hover::after { - background: none; - } - .image-shadow::after { - content: ''; - position: absolute; - top: 0; - right: 0; - bottom: 0; - left: 0; - box-shadow: - 0 10px 15px -3px rgba(0, 0, 0, 0.1), - 0 4px 6px -2px rgba(0, 0, 0, 0.05); - border-radius: 0.5rem; - pointer-events: none; - } + .scroll-fade::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 50px; + background: linear-gradient( + to bottom, + rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0) + ); + pointer-events: none; + } + .scroll-fade:active::after, + .scroll-fade:hover::after { + background: none; + } + .image-shadow::after { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + box-shadow: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); + border-radius: 0.5rem; + pointer-events: none; + } } diff --git a/app/layout.tsx b/app/layout.tsx index d0e1ef2b30393e2781498ca7f47ce5ac8efb16d9..59e432a64d8384380d68c606dc9d878a95f40153 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -10,54 +10,54 @@ import { Header } from '@/components/Header'; import { ThemeToggle } from '@/components/ThemeToggle'; export const metadata = { - metadataBase: new URL(`https://${process.env.VERCEL_URL}`), - title: { - default: 'Insight Playground', - template: `%s - Insight Playground`, - }, - description: 'By Landing AI', - icons: { - icon: '/landing.png', - shortcut: '/landing.png', - apple: '/landing.png', - }, + metadataBase: new URL(`https://${process.env.VERCEL_URL}`), + title: { + default: 'Insight Playground', + template: `%s - Insight Playground`, + }, + description: 'By Landing AI', + icons: { + icon: '/landing.png', + shortcut: '/landing.png', + apple: '/landing.png', + }, }; export const viewport = { - themeColor: [ - { media: '(prefers-color-scheme: light)', color: 'white' }, - { media: '(prefers-color-scheme: dark)', color: 'black' }, - ], + themeColor: [ + { media: '(prefers-color-scheme: light)', color: 'white' }, + { media: '(prefers-color-scheme: dark)', color: 'black' }, + ], }; interface RootLayoutProps { - children: React.ReactNode; + children: React.ReactNode; } export default function RootLayout({ children }: RootLayoutProps) { - return ( - - - - -
-
-
{children}
-
- -
- - - ); + return ( + + + + +
+
+
{children}
+
+ +
+ + + ); } diff --git a/app/page.tsx b/app/page.tsx index 6bb50019d2d190bb44977296ae7e4734d163cea3..bcb7272c6d399b4d075bc05f1aebb66fc2a0dbba 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,16 +2,16 @@ import { auth } from '@/auth'; import { redirect } from 'next/navigation'; export default async function Page() { - const session = await auth(); - if (!session) { - return null; - } + const session = await auth(); + if (!session) { + return null; + } - redirect('/chat'); + redirect('/chat'); - // return ( - //
- // Welcome to Insight Playground - //
- // ); + // return ( + //
+ // Welcome to Insight Playground + //
+ // ); } diff --git a/app/project/[projectId]/page.tsx b/app/project/[projectId]/page.tsx index a3abbcf244f0993f5a5f03faae98f16f00ec84e8..3df1085842dea61101aa5c63849e92dfb12e459d 100644 --- a/app/project/[projectId]/page.tsx +++ b/app/project/[projectId]/page.tsx @@ -5,26 +5,26 @@ import Loading from '../../../components/ui/Loading'; import Chat from '@/components/project/Chat'; interface PageProps { - params: { - projectId: string; - }; + params: { + projectId: string; + }; } export default async function Page({ params }: PageProps) { - const { projectId } = params; + const { projectId } = params; - const mediaList = await fetchProjectMedia({ projectId: Number(projectId) }); + const mediaList = await fetchProjectMedia({ projectId: Number(projectId) }); - return ( -
-
-
- -
-
- -
-
-
- ); + return ( +
+
+
+ +
+
+ +
+
+
+ ); } diff --git a/app/project/layout.tsx b/app/project/layout.tsx index 6958325a062a2b4412e708c4cf17d315f52ecd14..91324f6265b6c8ca7c05c2d17c43a6f7c85630d6 100644 --- a/app/project/layout.tsx +++ b/app/project/layout.tsx @@ -3,25 +3,25 @@ import { Suspense } from 'react'; import Loading from '@/components/ui/Loading'; interface ChatLayoutProps { - children: React.ReactNode; + children: React.ReactNode; } export default async function Layout({ children }: ChatLayoutProps) { - return ( -
-
- }> - - -
- }> -
- {children} -
-
-
- ); + return ( +
+
+ }> + + +
+ }> +
+ {children} +
+
+
+ ); } diff --git a/app/project/page.tsx b/app/project/page.tsx index 101fc80c5848a108ba90bce81c9dd56984bb8fe8..e51dad8188912bc927ff9a77fcdf5fe0af8240cc 100644 --- a/app/project/page.tsx +++ b/app/project/page.tsx @@ -1,8 +1,8 @@ export default function Page() { - const content = '<- Select a project from sidebar'; - return ( -
- {content} -
- ); + const content = '<- Select a project from sidebar'; + return ( +
+ {content} +
+ ); } diff --git a/auth.ts b/auth.ts index c2f220c72503e55eb1013d16d6530997779035ca..cdde3af945d569c13f406fa4539db989efdf37b7 100644 --- a/auth.ts +++ b/auth.ts @@ -3,51 +3,51 @@ import GitHub from 'next-auth/providers/github'; import Google from 'next-auth/providers/google'; declare module 'next-auth' { - interface Session { - user: { - /** The user's id. */ - id: string; - } & DefaultSession['user']; - } + interface Session { + user: { + /** The user's id. */ + id: string; + } & DefaultSession['user']; + } } export const { - handlers: { GET, POST }, - auth, + handlers: { GET, POST }, + auth, } = NextAuth({ - providers: [ - GitHub, - Google({ - clientId: process.env.GOOGLE_CLIENT_ID!, - clientSecret: process.env.GOOGLE_SECRET!, - }), - ], - callbacks: { - signIn({ profile }) { - if (profile?.email?.endsWith('@landing.ai')) { - return !!profile; - } else { - return '/unauthorized'; - } - }, - jwt({ token, profile }) { - if (profile) { - token.id = profile.id || profile.sub; - token.image = profile.avatar_url || profile.picture; - } - return token; - }, - session: ({ session, token }) => { - if (session?.user && token?.id) { - session.user.id = String(token.id); - } - return session; - }, - authorized({ request, auth }) { - return !!auth?.user || request.nextUrl.pathname === '/unauthorized'; // this ensures there is a logged in user for -every- request - }, - }, - pages: { - signIn: '/sign-in', // overrides the next-auth default signin page https://authjs.dev/guides/basics/pages - }, + providers: [ + GitHub, + Google({ + clientId: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_SECRET!, + }), + ], + callbacks: { + signIn({ profile }) { + if (profile?.email?.endsWith('@landing.ai')) { + return !!profile; + } else { + return '/unauthorized'; + } + }, + jwt({ token, profile }) { + if (profile) { + token.id = profile.id || profile.sub; + token.image = profile.avatar_url || profile.picture; + } + return token; + }, + session: ({ session, token }) => { + if (session?.user && token?.id) { + session.user.id = String(token.id); + } + return session; + }, + authorized({ request, auth }) { + return !!auth?.user || request.nextUrl.pathname === '/unauthorized'; // this ensures there is a logged in user for -every- request + }, + }, + pages: { + signIn: '/sign-in', // overrides the next-auth default signin page https://authjs.dev/guides/basics/pages + }, }); diff --git a/components/Header.tsx b/components/Header.tsx index 072de5e834c61cd764696d60b6613557b52dd16e..2e0f2a785729a84ef44cebd0d982b263b4613047 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -7,24 +7,24 @@ import { UserMenu } from '@/components/UserMenu'; import { IconSeparator } from './ui/Icons'; export async function Header() { - const session = await auth(); + const session = await auth(); - if (!session?.user) { - return null; - } + if (!session?.user) { + return null; + } - return ( -
- {/* */} - - -
- -
-
- ); + + +
+ +
+ + ); } diff --git a/components/LoginButton.tsx b/components/LoginButton.tsx index a05028ef64c6057d8f7402e75bbb272795fd5d3e..6efcf0f8fbee219c32dc7e5458996693a43ebbab 100644 --- a/components/LoginButton.tsx +++ b/components/LoginButton.tsx @@ -8,31 +8,31 @@ import { Button, type ButtonProps } from '@/components/ui/Button'; import { IconGitHub, IconSpinner, IconGoogle } from '@/components/ui/Icons'; interface LoginButtonProps extends ButtonProps { - oauth: 'github' | 'google'; + oauth: 'github' | 'google'; } export function LoginButton({ oauth, ...props }: LoginButtonProps) { - const [isLoading, setIsLoading] = React.useState(false); + const [isLoading, setIsLoading] = React.useState(false); - const icon = - oauth === 'github' ? ( - - ) : ( - - ); - return ( - - ); + const icon = + oauth === 'github' ? ( + + ) : ( + + ); + return ( + + ); } diff --git a/components/Providers.tsx b/components/Providers.tsx index 622125bfb4386c5b35d69339444a2b686b028573..e77080f1e76b8ebb215623d6b16425b33f7640ed 100644 --- a/components/Providers.tsx +++ b/components/Providers.tsx @@ -6,9 +6,9 @@ import { ThemeProviderProps } from 'next-themes/dist/types'; import { TooltipProvider } from '@/components/ui/Tooltip'; export function Providers({ children, ...props }: ThemeProviderProps) { - return ( - - {children} - - ); + return ( + + {children} + + ); } diff --git a/components/TailwindIndicator.tsx b/components/TailwindIndicator.tsx index 248a94e4ac3f4affe2783cd60d579c4c7d2af385..0e60a7b2ca2333421bfb85846655517720f7cb03 100644 --- a/components/TailwindIndicator.tsx +++ b/components/TailwindIndicator.tsx @@ -1,14 +1,14 @@ export function TailwindIndicator() { - if (process.env.NODE_ENV === 'production') return null; + if (process.env.NODE_ENV === 'production') return null; - return ( -
-
xs
-
sm
-
md
-
lg
-
xl
-
2xl
-
- ); + return ( +
+
xs
+
sm
+
md
+
lg
+
xl
+
2xl
+
+ ); } diff --git a/components/ThemeToggle.tsx b/components/ThemeToggle.tsx index 664794f53351cca5e1b20badacb0f5502f60e8e7..06bd44564b8190f346c1d39530f12074836b88a0 100644 --- a/components/ThemeToggle.tsx +++ b/components/ThemeToggle.tsx @@ -7,26 +7,26 @@ import { Button } from '@/components/ui/Button'; import { IconMoon, IconSun } from '@/components/ui/Icons'; export function ThemeToggle() { - const { setTheme, theme } = useTheme(); - const [_, startTransition] = React.useTransition(); + const { setTheme, theme } = useTheme(); + const [_, startTransition] = React.useTransition(); - return ( - - ); + return ( + + ); } diff --git a/components/UserMenu.tsx b/components/UserMenu.tsx index a0ba98d6e3474e36572c2f391cb5b69248c17c51..14fe47924926cbcdd8818ae2702cbcedf45478f5 100644 --- a/components/UserMenu.tsx +++ b/components/UserMenu.tsx @@ -6,63 +6,63 @@ import { signOut } from 'next-auth/react'; import { Button } from '@/components/ui/Button'; import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger, + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, } from '@/components/ui/DropdownMenu'; import { IconExternalLink } from '@/components/ui/Icons'; export interface UserMenuProps { - user: Session['user']; + user: Session['user']; } function getUserInitials(name: string) { - const [firstName, lastName] = name.split(' '); - return lastName ? `${firstName[0]}${lastName[0]}` : firstName.slice(0, 2); + const [firstName, lastName] = name.split(' '); + return lastName ? `${firstName[0]}${lastName[0]}` : firstName.slice(0, 2); } export function UserMenu({ user }: UserMenuProps) { - return ( -
- - - - - - -
{user?.name}
-
{user?.email}
-
- - - signOut({ - callbackUrl: '/', - }) - } - className="text-xs" - > - Log Out - -
-
-
- ); + return ( +
+ + + + + + +
{user?.name}
+
{user?.email}
+
+ + + signOut({ + callbackUrl: '/', + }) + } + className="text-xs" + > + Log Out + +
+
+
+ ); } diff --git a/components/chat-sidebar/ChatCard.tsx b/components/chat-sidebar/ChatCard.tsx index 422cd2d9acc96d78eb4d5c131af622b13b939fe9..7aa27fd1f5747c66d0740881b54e8161d80adb31 100644 --- a/components/chat-sidebar/ChatCard.tsx +++ b/components/chat-sidebar/ChatCard.tsx @@ -5,25 +5,25 @@ import { useParams } from 'next/navigation'; import { cn } from '@/lib/utils'; export interface ChatCardProps { - id: string; - title: string; + id: string; + title: string; } const ChatCard: React.FC = ({ id, title }) => { - const { chatId: chatIdFromParam } = useParams(); - return ( - -
-

{title}

-
- - ); + const { chatId: chatIdFromParam } = useParams(); + return ( + +
+

{title}

+
+ + ); }; export default ChatCard; diff --git a/components/chat-sidebar/ChatListSidebar.tsx b/components/chat-sidebar/ChatListSidebar.tsx index 5eddda84613a6267d6a4617a65f5bca1bd54d0da..e981b7313dca535a8a61612db8d93b862567160b 100644 --- a/components/chat-sidebar/ChatListSidebar.tsx +++ b/components/chat-sidebar/ChatListSidebar.tsx @@ -4,13 +4,13 @@ import ChatCard from './ChatCard'; export interface ChatSidebarListProps {} export default async function ChatSidebarList({}: ChatSidebarListProps) { - const chats = await getKVChats(); - console.log('[Ming] ~ ChatSidebarList ~ chats:', chats); - return ( - <> - {chats.map(chat => ( - - ))} - - ); + const chats = await getKVChats(); + console.log('[Ming] ~ ChatSidebarList ~ chats:', chats); + return ( + <> + {chats.map(chat => ( + + ))} + + ); } diff --git a/components/chat/ButtonScrollToBottom.tsx b/components/chat/ButtonScrollToBottom.tsx index 8e17567c4e3b15a8536b38b4a654aa8a7f6423a0..0ca5563436916cb58293dd6e45d8bac48bd2d749 100644 --- a/components/chat/ButtonScrollToBottom.tsx +++ b/components/chat/ButtonScrollToBottom.tsx @@ -8,27 +8,27 @@ import { Button, type ButtonProps } from '@/components/ui/Button'; import { IconArrowDown } from '@/components/ui/Icons'; export function ButtonScrollToBottom({ className, ...props }: ButtonProps) { - const isAtBottom = useAtBottom(); + const isAtBottom = useAtBottom(); - return ( - - ); + return ( + + ); } diff --git a/components/chat/ChatList.tsx b/components/chat/ChatList.tsx index 10cc7b41edad1344aca39c36463c207999ae493c..e8a51ea94acf6c00b46cfc6f030fc4a87304a0a5 100644 --- a/components/chat/ChatList.tsx +++ b/components/chat/ChatList.tsx @@ -5,22 +5,22 @@ import { ChatMessage } from '@/components/chat/ChatMessage'; import { MessageWithSelectedDataset } from '../../lib/types'; export interface ChatList { - messages: MessageWithSelectedDataset[]; + messages: MessageWithSelectedDataset[]; } export function ChatList({ messages }: ChatList) { - return ( -
- {messages - .filter(message => message.role !== 'system') - .map((message, index) => ( -
- - {index < messages.length - 1 && ( - - )} -
- ))} -
- ); + return ( +
+ {messages + .filter(message => message.role !== 'system') + .map((message, index) => ( +
+ + {index < messages.length - 1 && ( + + )} +
+ ))} +
+ ); } diff --git a/components/chat/ChatMessage.tsx b/components/chat/ChatMessage.tsx index f9ec21110890b7aed7efc577659b768f7cd89080..e0a74dff525353cdb540b1219cfd405dfd61fc4d 100644 --- a/components/chat/ChatMessage.tsx +++ b/components/chat/ChatMessage.tsx @@ -12,66 +12,66 @@ import { ChatMessageActions } from '@/components/chat/ChatMessageActions'; import { MessageWithSelectedDataset } from '../../lib/types'; export interface ChatMessageProps { - message: MessageWithSelectedDataset; + message: MessageWithSelectedDataset; } export function ChatMessage({ message, ...props }: ChatMessageProps) { - return ( -
-
- {message.role === 'user' ? : } -
-
- {children}

; - }, - code({ node, inline, className, children, ...props }) { - if (children.length) { - if (children[0] == '▍') { - return ( - - ); - } + 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('`▍`', '▍'); - } + children[0] = (children[0] as string).replace('`▍`', '▍'); + } - const match = /language-(\w+)/.exec(className || ''); + const match = /language-(\w+)/.exec(className || ''); - if (inline) { - return ( - - {children} - - ); - } + if (inline) { + return ( + + {children} + + ); + } - return ( - - ); - }, - }} - > - {message.content} -
- -
-
- ); + return ( + + ); + }, + }} + > + {message.content} +
+ +
+
+ ); } diff --git a/components/chat/ChatMessageActions.tsx b/components/chat/ChatMessageActions.tsx index 8fbb94182485de7fcb1d3a20174726edbf0caa3c..2ee45bc2e85bcfc6937277d789f7d36f96b02d28 100644 --- a/components/chat/ChatMessageActions.tsx +++ b/components/chat/ChatMessageActions.tsx @@ -9,33 +9,33 @@ import { cn } from '@/lib/utils'; import { MessageWithSelectedDataset } from '../../lib/types'; interface ChatMessageActionsProps extends React.ComponentProps<'div'> { - message: MessageWithSelectedDataset; + message: MessageWithSelectedDataset; } export function ChatMessageActions({ - message, - className, - ...props + message, + className, + ...props }: ChatMessageActionsProps) { - const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }); + const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 }); - const onCopy = () => { - if (isCopied) return; - copyToClipboard(message.content); - }; + const onCopy = () => { + if (isCopied) return; + copyToClipboard(message.content); + }; - return ( -
- -
- ); + return ( +
+ +
+ ); } diff --git a/components/chat/ChatPanel.tsx b/components/chat/ChatPanel.tsx index f7582a215ead48e9affae30fe09c549599ea4e41..bcd6b06c44103385325c48479c30dd62fa8f9171 100644 --- a/components/chat/ChatPanel.tsx +++ b/components/chat/ChatPanel.tsx @@ -8,68 +8,68 @@ import { IconRefresh, IconShare, IconStop } from '@/components/ui/Icons'; import { MessageWithSelectedDataset } from '../../lib/types'; export interface ChatPanelProps - extends Pick< - UseChatHelpers, - 'append' | 'isLoading' | 'reload' | 'stop' | 'input' | 'setInput' - > { - id?: string; - title?: string; - messages: MessageWithSelectedDataset[]; + extends Pick< + UseChatHelpers, + 'append' | 'isLoading' | 'reload' | 'stop' | 'input' | 'setInput' + > { + id?: string; + title?: string; + messages: MessageWithSelectedDataset[]; } export function ChatPanel({ - id, - title, - isLoading, - stop, - append, - reload, - input, - setInput, - messages, + id, + title, + isLoading, + stop, + append, + reload, + input, + setInput, + messages, }: ChatPanelProps) { - const [shareDialogOpen, setShareDialogOpen] = React.useState(false); + const [shareDialogOpen, setShareDialogOpen] = React.useState(false); - return ( -
- -
-
- {isLoading ? ( - - ) : ( - messages?.length >= 2 && ( -
- -
- ) - )} -
-
- { - await append({ - id, - content: value, - role: 'user', - }); - }} - input={input} - setInput={setInput} - isLoading={isLoading} - /> -
-
-
- ); + return ( +
+ +
+
+ {isLoading ? ( + + ) : ( + messages?.length >= 2 && ( +
+ +
+ ) + )} +
+
+ { + await append({ + id, + content: value, + role: 'user', + }); + }} + input={input} + setInput={setInput} + isLoading={isLoading} + /> +
+
+
+ ); } diff --git a/components/chat/ChatScrollAnchor.tsx b/components/chat/ChatScrollAnchor.tsx index 6f29fdf03b479b310622dd8f1b149bf63d85c740..6b719936516e93856d890c133744536798b5b5a2 100644 --- a/components/chat/ChatScrollAnchor.tsx +++ b/components/chat/ChatScrollAnchor.tsx @@ -6,24 +6,24 @@ import { useInView } from 'react-intersection-observer'; import { useAtBottom } from '@/lib/hooks/useAtBottom'; interface ChatScrollAnchorProps { - trackVisibility?: boolean; + trackVisibility?: boolean; } export function ChatScrollAnchor({ trackVisibility }: ChatScrollAnchorProps) { - const isAtBottom = useAtBottom(); - const { ref, entry, inView } = useInView({ - trackVisibility, - delay: 100, - rootMargin: '0px 0px -150px 0px', - }); + const isAtBottom = useAtBottom(); + const { ref, entry, inView } = useInView({ + trackVisibility, + delay: 100, + rootMargin: '0px 0px -150px 0px', + }); - React.useEffect(() => { - if (isAtBottom && trackVisibility && !inView) { - entry?.target.scrollIntoView({ - block: 'start', - }); - } - }, [inView, entry, isAtBottom, trackVisibility]); + React.useEffect(() => { + if (isAtBottom && trackVisibility && !inView) { + entry?.target.scrollIntoView({ + block: 'start', + }); + } + }, [inView, entry, isAtBottom, trackVisibility]); - return
; + return
; } diff --git a/components/chat/EmptyScreen.tsx b/components/chat/EmptyScreen.tsx index cf0ec99f367271c1129f569162278a5e2ecc0552..d6ea6e9b4a114192a09c9ab167b557f907cdfea6 100644 --- a/components/chat/EmptyScreen.tsx +++ b/components/chat/EmptyScreen.tsx @@ -5,48 +5,48 @@ import Image from 'next/image'; import useImageUpload from '../../lib/hooks/useImageUpload'; const examples = [ - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/cereal-example.jpg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/people-example.jpeg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/house-exmaple.jpg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/safari-example.png', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/cereal-example.jpg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/people-example.jpeg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/house-exmaple.jpg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/safari-example.png', ]; export function EmptyScreen() { - const [, setTarget] = useAtom(datasetAtom); - const { getRootProps, getInputProps } = useImageUpload(); - return ( -
-
-

Welcome to Vision Agent

-

Lets start by choosing an image

-
- -

- Drag or drop image here, or click to select images -

-
-

- You can also choose from below examples we provided -

-
- {examples.map((example, index) => ( - example images - setTarget([{ url: example, name: 'i-1', selected: false }]) - } - /> - ))} -
-
-
- ); + const [, setTarget] = useAtom(datasetAtom); + const { getRootProps, getInputProps } = useImageUpload(); + return ( +
+
+

Welcome to Vision Agent

+

Lets start by choosing an image

+
+ +

+ Drag or drop image here, or click to select images +

+
+

+ You can also choose from below examples we provided +

+
+ {examples.map((example, index) => ( + example images + setTarget([{ url: example, name: 'i-1', selected: false }]) + } + /> + ))} +
+
+
+ ); } diff --git a/components/chat/ImageList.tsx b/components/chat/ImageList.tsx index 052265b93ae7c5cfbe330fbfb7053dd19e8c052f..96338d611248d99fa8c4d6c90957548ca5442e0c 100644 --- a/components/chat/ImageList.tsx +++ b/components/chat/ImageList.tsx @@ -8,14 +8,14 @@ import { produce } from 'immer'; export interface ImageListProps {} const ImageList: React.FC = () => { - const { getRootProps, getInputProps, isDragActive } = useImageUpload({ - noClick: true, - }); + const { getRootProps, getInputProps, isDragActive } = useImageUpload({ + noClick: true, + }); - const [dataset, setDataset] = useAtom(datasetAtom); - return ( -
- {/* {dataset.length < 10 ? ( + const [dataset, setDataset] = useAtom(datasetAtom); + return ( +
+ {/* {dataset.length < 10 ? (
You can upload up to 10 images max by dragging image.
@@ -24,53 +24,53 @@ const ImageList: React.FC = () => { You have reached the maximum limit of 10 images.
)} */} -
- {dataset.map(entity => { - const { url: imageSrc, name, selected } = entity; - return ( -
- setDataset(prev => - produce(prev, draft => { - const index = draft.findIndex(d => d.name === name); - draft[index].selected = !selected; - }), - ) - } - className={`relative rounded-xl overflow-hidden shadow-md cursor-pointer transition-transform hover:scale-105 box-content ${selected ? 'border-4 border-blue-500' : ''}`} - > - dataset images -
-

{name}

-
-
- ); - })} -
+
+ {dataset.map(entity => { + const { url: imageSrc, name, selected } = entity; + return ( +
+ setDataset(prev => + produce(prev, draft => { + const index = draft.findIndex(d => d.name === name); + draft[index].selected = !selected; + }), + ) + } + className={`relative rounded-xl overflow-hidden shadow-md cursor-pointer transition-transform hover:scale-105 box-content ${selected ? 'border-4 border-blue-500' : ''}`} + > + dataset images +
+

{name}

+
+
+ ); + })} +
- {isDragActive && ( -
- -

Drop the files here ...

-
- )} -
- ); + {isDragActive && ( +
+ +

Drop the files here ...

+
+ )} +
+ ); }; export default ImageList; diff --git a/components/chat/ImageSelector.tsx b/components/chat/ImageSelector.tsx index 22ff7be9dae71e4a99360bd76c6ef5e75380fe9c..f385fb3b8656041d409a1c2f8d1046ea5fbc2a30 100644 --- a/components/chat/ImageSelector.tsx +++ b/components/chat/ImageSelector.tsx @@ -6,52 +6,52 @@ import { fetcher } from '@/lib/utils'; export interface ImageSelectorProps {} const examples = [ - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/cereal-example.jpg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/people-example.jpeg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/house-exmaple.jpg', - 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/safari-example.png', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/cereal-example.jpg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/people-example.jpeg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/house-exmaple.jpg', + 'https://landing-lens-support.s3.us-east-2.amazonaws.com/vision-agent-examples/safari-example.png', ]; const ImageSelector: React.FC = () => { - const { getRootProps, getInputProps } = useImageUpload(); - return ( -
-
-

Welcome to Vision Agent

-

Lets start by choosing an image

-
- -

- Drag or drop image here, or click to select images -

-
-

- You can also choose from below examples we provided -

-
- {examples.map((example, index) => ( - example images - fetcher('/api/upload', { - method: 'POST', - body: JSON.stringify({ url: example }), - }) - } - /> - ))} -
-
-
- ); + const { getRootProps, getInputProps } = useImageUpload(); + return ( +
+
+

Welcome to Vision Agent

+

Lets start by choosing an image

+
+ +

+ Drag or drop image here, or click to select images +

+
+

+ You can also choose from below examples we provided +

+
+ {examples.map((example, index) => ( + example images + fetcher('/api/upload', { + method: 'POST', + body: JSON.stringify({ url: example }), + }) + } + /> + ))} +
+
+
+ ); }; export default ImageSelector; diff --git a/components/chat/MemoizedReactMarkdown.tsx b/components/chat/MemoizedReactMarkdown.tsx index db8075ed10890324d7cf6fdd36ede7951599bcfe..ca5cad4640deb9c0868b6561ec4d49544b164302 100644 --- a/components/chat/MemoizedReactMarkdown.tsx +++ b/components/chat/MemoizedReactMarkdown.tsx @@ -2,8 +2,8 @@ import { FC, memo } from 'react'; import ReactMarkdown, { Options } from 'react-markdown'; export const MemoizedReactMarkdown: FC = memo( - ReactMarkdown, - (prevProps, nextProps) => - prevProps.children === nextProps.children && - prevProps.className === nextProps.className, + ReactMarkdown, + (prevProps, nextProps) => + prevProps.children === nextProps.children && + prevProps.className === nextProps.className, ); diff --git a/components/chat/PromptForm.tsx b/components/chat/PromptForm.tsx index 82c615c4a19c0170de62da69b0e5160b33e7756e..2721bb75b8ba5cc1e2956154777379d0f6190097 100644 --- a/components/chat/PromptForm.tsx +++ b/components/chat/PromptForm.tsx @@ -5,48 +5,48 @@ import { useEnterSubmit } from '@/lib/hooks/useEnterSubmit'; import { cn } from '@/lib/utils'; import { Button, buttonVariants } from '@/components/ui/Button'; import { - Tooltip, - TooltipContent, - TooltipTrigger, + Tooltip, + TooltipContent, + TooltipTrigger, } from '@/components/ui/Tooltip'; import { IconArrowElbow, IconPlus } from '@/components/ui/Icons'; import { useRouter } from 'next/navigation'; export interface PromptProps - extends Pick { - onSubmit: (value: string) => void; - isLoading: boolean; + extends Pick { + onSubmit: (value: string) => void; + isLoading: boolean; } export function PromptForm({ - onSubmit, - input, - setInput, - isLoading, + onSubmit, + input, + setInput, + isLoading, }: PromptProps) { - const { formRef, onKeyDown } = useEnterSubmit(); - const inputRef = React.useRef(null); - const router = useRouter(); - React.useEffect(() => { - if (inputRef.current) { - inputRef.current.focus(); - } - }, []); + const { formRef, onKeyDown } = useEnterSubmit(); + const inputRef = React.useRef(null); + const router = useRouter(); + React.useEffect(() => { + if (inputRef.current) { + inputRef.current.focus(); + } + }, []); - return ( -
{ - e.preventDefault(); - if (!input?.trim()) { - return; - } - setInput(''); - await onSubmit(input); - }} - ref={formRef} - > -
- {/* + return ( + { + e.preventDefault(); + if (!input?.trim()) { + return; + } + setInput(''); + await onSubmit(input); + }} + ref={formRef} + > +
+ {/*