MingruiZhang commited on
Commit
fcd4478
1 Parent(s): 60612a5

project list

Browse files
app/api/chat/route.ts CHANGED
@@ -2,7 +2,6 @@ import { OpenAIStream, StreamingTextResponse } from 'ai';
2
  import OpenAI from 'openai';
3
 
4
  import { auth } from '@/auth';
5
- import { nanoid } from '@/lib/utils';
6
  import {
7
  ChatCompletionMessageParam,
8
  ChatCompletionContentPart,
 
2
  import OpenAI from 'openai';
3
 
4
  import { auth } from '@/auth';
 
5
  import {
6
  ChatCompletionMessageParam,
7
  ChatCompletionContentPart,
app/api/llens/projects/route.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ export async function GET(req: Request) {}
app/chat/page.tsx CHANGED
@@ -7,7 +7,7 @@ import { useAtomValue } from 'jotai';
7
  import { datasetAtom } from '../../state';
8
  import { EmptyScreen } from '../../components/chat/EmptyScreen';
9
 
10
- export default function IndexPage() {
11
  const id = nanoid();
12
  const dataset = useAtomValue(datasetAtom);
13
 
 
7
  import { datasetAtom } from '../../state';
8
  import { EmptyScreen } from '../../components/chat/EmptyScreen';
9
 
10
+ export default function Page() {
11
  const id = nanoid();
12
  const dataset = useAtomValue(datasetAtom);
13
 
app/page.tsx CHANGED
@@ -1,6 +1,6 @@
1
  'use client';
2
 
3
- export default function IndexPage() {
4
  return (
5
  <div className="flex flex-col h-[calc(100vh-theme(spacing.16))] items-center justify-center py-10 space-y-2">
6
  Welcome to Insight Playground
 
1
  'use client';
2
 
3
+ export default function Page() {
4
  return (
5
  <div className="flex flex-col h-[calc(100vh-theme(spacing.16))] items-center justify-center py-10 space-y-2">
6
  Welcome to Insight Playground
app/project/[projectId]/page.tsx ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import MediaGrid from '@/components/project/MediaGrid';
2
+ import { fetchProjectMedia } from '@/lib/fetch/clef';
3
+ import { Suspense } from 'react';
4
+
5
+ interface PageProps {
6
+ params: {
7
+ projectId: string;
8
+ };
9
+ }
10
+
11
+ export default async function Page({ params }: PageProps) {
12
+ const { projectId } = params;
13
+
14
+ return (
15
+ <div className="pb-[150px] pt-4 md:pt-10 h-full">
16
+ <div className="flex h-full">
17
+ <div className="w-1/2 relative border-r border-gray-300 overflow-auto">
18
+ <Suspense
19
+ fallback={
20
+ <div className="flex justify-center items-center size-full text-sm">
21
+ Loading media...
22
+ </div>
23
+ }
24
+ >
25
+ <MediaGrid projectId={Number(projectId)} />
26
+ </Suspense>
27
+ </div>
28
+ <div className="w-1/2 relative overflow-auto">
29
+ {/* <ChatList messages={messages} /> */}
30
+ {/* <ChatScrollAnchor trackVisibility={isLoading} /> */}
31
+ Chat
32
+ </div>
33
+ </div>
34
+ </div>
35
+ );
36
+ }
app/{chat → project}/layout.tsx RENAMED
@@ -1,11 +1,13 @@
 
 
1
  interface ChatLayoutProps {
2
  children: React.ReactNode;
3
  }
4
 
5
- export default async function ChatLayout({ children }: ChatLayoutProps) {
6
  return (
7
  <div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
8
- {/* <SidebarDesktop /> */}
9
  <div className="group w-full overflow-auto pl-0 animate-in duration-300 ease-in-out peer-[[data-state=open]]:lg:pl-[250px] peer-[[data-state=open]]:xl:pl-[300px]">
10
  {children}
11
  </div>
 
1
+ import ProjectListSideBar from '@/components/sidebar/ProjectListSideBar';
2
+
3
  interface ChatLayoutProps {
4
  children: React.ReactNode;
5
  }
6
 
7
+ export default async function Layout({ children }: ChatLayoutProps) {
8
  return (
9
  <div className="relative flex h-[calc(100vh_-_theme(spacing.16))] overflow-hidden">
10
+ <ProjectListSideBar />
11
  <div className="group w-full overflow-auto pl-0 animate-in duration-300 ease-in-out peer-[[data-state=open]]:lg:pl-[250px] peer-[[data-state=open]]:xl:pl-[300px]">
12
  {children}
13
  </div>
app/project/page.tsx ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ export default function Page() {
2
+ const content = '<- Select a project from sidebar';
3
+ return (
4
+ <div className="flex flex-col h-[calc(100vh-theme(spacing.16))] items-center justify-center py-10 space-y-2 font-medium">
5
+ {content}
6
+ </div>
7
+ );
8
+ }
app/projects/page.tsx DELETED
File without changes
components/header.tsx CHANGED
@@ -16,7 +16,7 @@ export async function Header() {
16
  return (
17
  <header className="sticky top-0 z-50 flex items-center justify-end w-full h-16 px-8 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
18
  <Button variant="link" asChild className="mr-2">
19
- <Link href="/projects">Projects</Link>
20
  </Button>
21
  <Button variant="link" asChild className="mr-2">
22
  <Link href="/chat">Chat</Link>
 
16
  return (
17
  <header className="sticky top-0 z-50 flex items-center justify-end w-full h-16 px-8 border-b shrink-0 bg-gradient-to-b from-background/10 via-background/50 to-background/80 backdrop-blur-xl">
18
  <Button variant="link" asChild className="mr-2">
19
+ <Link href="/project">Projects</Link>
20
  </Button>
21
  <Button variant="link" asChild className="mr-2">
22
  <Link href="/chat">Chat</Link>
components/project/MediaGrid.tsx ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { fetchProjectMedia } from '@/lib/fetch/clef';
2
+ import MediaTile from './MediaTile';
3
+
4
+ export default async function MediaGrid({ projectId }: { projectId: number }) {
5
+ const mediaList = await fetchProjectMedia({ projectId });
6
+
7
+ return (
8
+ <div className="relative size-full p-6 max-w-3xl mx-auto">
9
+ <div className="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-4">
10
+ {mediaList.map(media => (
11
+ <MediaTile key={media.id} media={media} />
12
+ ))}
13
+ </div>
14
+ </div>
15
+ );
16
+ }
components/project/MediaTile.tsx ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import Image from 'next/image';
5
+ import {
6
+ Tooltip,
7
+ TooltipContent,
8
+ TooltipTrigger,
9
+ } from '@/components/ui/Tooltip';
10
+ import { MediaDetails } from '@/lib/fetch/clef';
11
+
12
+ export interface MediaTileProps {
13
+ media: MediaDetails;
14
+ }
15
+
16
+ const MediaTile: React.FC<MediaTileProps> = ({ media }) => {
17
+ const { url: imageSrc, id, name } = media;
18
+ return (
19
+ <Tooltip>
20
+ <TooltipTrigger asChild>
21
+ <div className="relative rounded-xl overflow-hidden shadow-md cursor-pointer transition-transform hover:scale-105 box-content">
22
+ <Image
23
+ src={imageSrc}
24
+ draggable={false}
25
+ alt="dataset images"
26
+ width={500}
27
+ height={500}
28
+ objectFit="cover"
29
+ className="rounded-xl"
30
+ />
31
+ </div>
32
+ </TooltipTrigger>
33
+ <TooltipContent>{name}</TooltipContent>
34
+ </Tooltip>
35
+ );
36
+ };
37
+
38
+ export default MediaTile;
components/sidebar/ProjectCard.tsx ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client';
2
+
3
+ import { ProjectBaseInfo } from '@/lib/fetch/clef';
4
+ import { format } from 'date-fns';
5
+ import Link from 'next/link';
6
+ import { useParams } from 'next/navigation';
7
+ import { cn } from '@/lib/utils';
8
+
9
+ export interface ProjectCardProps {
10
+ projectInfo: ProjectBaseInfo;
11
+ }
12
+
13
+ const ProjectCard: React.FC<ProjectCardProps> = ({ projectInfo }) => {
14
+ const {
15
+ id,
16
+ name,
17
+ created_at,
18
+ organization: { name: orgName },
19
+ } = projectInfo;
20
+
21
+ const { projectId: projectIdFromParam } = useParams();
22
+
23
+ const formattedDate = format(created_at, 'yyyy-MM-dd');
24
+ return (
25
+ <Link
26
+ className={cn(
27
+ 'p-4 m-2 bg-white l:h-[250px] rounded-xl shadow-md flex items-center border border-transparent hover:border-gray-500 transition-all cursor-pointer',
28
+ Number(projectIdFromParam) === id && 'border-gray-500',
29
+ )}
30
+ href={`/project/${id}`}
31
+ >
32
+ <div>
33
+ <p className="text-xs text-gray-500">{orgName}</p>
34
+ <p className="text-sm font-medium text-black mb-1">{name}</p>
35
+ <p className="text-xs text-gray-500">{formattedDate}</p>
36
+ </div>
37
+ </Link>
38
+ );
39
+ };
40
+
41
+ export default ProjectCard;
components/sidebar/ProjectListSideBar.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { auth } from '@/auth';
2
+ import { fetchRecentProjectList } from '@/lib/fetch/clef';
3
+ import { redirect } from 'next/navigation';
4
+ import { v5 as uuidV5 } from 'uuid';
5
+ import ProjectCard from './ProjectCard';
6
+
7
+ export interface ProjectListSideBarProps {}
8
+
9
+ const ProjectListSideBar: React.FC<ProjectListSideBarProps> = async () => {
10
+ const recentProjects = await fetchRecentProjectList();
11
+ return (
12
+ <div
13
+ data-state="open"
14
+ className="peer absolute inset-y-0 z-30 hidden border-r bg-muted duration-300 ease-in-out translate-x-0 lg:flex lg:w-[250px] xl:w-[300px] h-full flex-col dark:bg-zinc-950 overflow-auto py-2"
15
+ >
16
+ {recentProjects.map(project => (
17
+ <ProjectCard key={project.id} projectInfo={project} />
18
+ ))}
19
+ </div>
20
+ );
21
+ };
22
+
23
+ export default ProjectListSideBar;
lib/fetch/clef.ts ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { auth } from '@/auth';
2
+ import { redirect } from 'next/navigation';
3
+ import { v5 as uuidV5 } from 'uuid';
4
+
5
+ interface ApiResponse<T> {
6
+ code: number; // code from server 0
7
+ message: string;
8
+ data: T;
9
+ }
10
+
11
+ const clefApiBuilder = <Params extends object | void, Resp>(path: string) => {
12
+ return async (params: Params): Promise<Resp> => {
13
+ const session = await auth();
14
+ if (!session?.user?.email) {
15
+ redirect('/');
16
+ }
17
+
18
+ const adminEmail = session.user.email;
19
+ const sessionUser = {
20
+ id: uuidV5(adminEmail, uuidV5.URL),
21
+ orgId: '-1024',
22
+ email: adminEmail,
23
+ username: adminEmail.split('@')[0],
24
+ userRole: 'adminPortal',
25
+ bucket: 'fake_bucket',
26
+ };
27
+
28
+ const baseURL = `https://app.dev.landing.ai/${path}`;
29
+
30
+ // Create a URL object with query params
31
+ const url = new URL(baseURL);
32
+ Object.entries(params ?? {}).forEach(([key, value]) =>
33
+ url.searchParams.append(key, value),
34
+ );
35
+
36
+ const res = await fetch(url.toString(), {
37
+ headers: {
38
+ 'Content-Type': 'application/json',
39
+ sessionuser: JSON.stringify(sessionUser),
40
+ 'X-ROUTE': 'mingruizhang-landing',
41
+ },
42
+ });
43
+
44
+ if (!res.ok) {
45
+ console.error('ERROR: fetch data failure', res.status);
46
+ // This will activate the closest `error.js` Error Boundary
47
+ throw new Error('Failed to fetch data');
48
+ }
49
+ const { data }: ApiResponse<Resp> = await res.json();
50
+ return data;
51
+ };
52
+ };
53
+
54
+ export type ProjectBaseInfo = {
55
+ id: number;
56
+ name: string;
57
+ created_at: Date;
58
+ organization: {
59
+ id: number;
60
+ name: string;
61
+ };
62
+ };
63
+ /**
64
+ * Fetch recent projects from all organizations from past 30 days, excluding
65
+ * 1. test organization such as bdd, cypress
66
+ * 2. internal organization such as landing, landing-ai, orgId=1
67
+ * 3. projects not containing media or only contain sample media
68
+ * @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts
69
+ */
70
+ export const fetchRecentProjectList = clefApiBuilder<void, ProjectBaseInfo[]>(
71
+ 'api/admin/projects/recent',
72
+ );
73
+
74
+ export type MediaDetails = {
75
+ id: number;
76
+ name: string;
77
+ path: string;
78
+ url: string;
79
+ projectId: number;
80
+ thumbnails: [string, string, string];
81
+ properties: {
82
+ width: number;
83
+ height: number;
84
+ format: string;
85
+ orientation: number;
86
+ };
87
+ };
88
+ /**
89
+ * Randomly fetch 10 media from a given project
90
+ * @author https://github.com/landing-ai/landing-platform/blob/mingrui-04-08-meaningful-project/packages/server-clef/src/main_app/controllers/admin/get_admin_meaningful_project_controller.ts
91
+ */
92
+ export const fetchProjectMedia = clefApiBuilder<
93
+ { projectId: number },
94
+ MediaDetails[]
95
+ >('api/admin/project/media');
package.json CHANGED
@@ -13,8 +13,6 @@
13
  "format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache"
14
  },
15
  "dependencies": {
16
- "@radix-ui/react-alert-dialog": "^1.0.5",
17
- "@radix-ui/react-dialog": "^1.0.5",
18
  "@radix-ui/react-dropdown-menu": "^2.0.6",
19
  "@radix-ui/react-label": "^2.0.2",
20
  "@radix-ui/react-select": "^2.0.0",
@@ -28,6 +26,7 @@
28
  "ai": "^2.2.31",
29
  "class-variance-authority": "^0.7.0",
30
  "clsx": "^2.1.0",
 
31
  "focus-trap-react": "^10.2.3",
32
  "framer-motion": "^10.18.0",
33
  "geist": "^1.2.1",
@@ -47,7 +46,8 @@
47
  "react-syntax-highlighter": "^15.5.0",
48
  "react-textarea-autosize": "^8.5.3",
49
  "remark-gfm": "^3.0.1",
50
- "remark-math": "^5.1.1"
 
51
  },
52
  "devDependencies": {
53
  "@tailwindcss/typography": "^0.5.10",
@@ -55,6 +55,7 @@
55
  "@types/react": "^18.2.48",
56
  "@types/react-dom": "^18.2.18",
57
  "@types/react-syntax-highlighter": "^15.5.11",
 
58
  "@typescript-eslint/parser": "^6.19.0",
59
  "autoprefixer": "^10.4.17",
60
  "eslint": "^8.56.0",
 
13
  "format:check": "prettier --check \"{app,lib,components}**/*.{ts,tsx,mdx}\" --cache"
14
  },
15
  "dependencies": {
 
 
16
  "@radix-ui/react-dropdown-menu": "^2.0.6",
17
  "@radix-ui/react-label": "^2.0.2",
18
  "@radix-ui/react-select": "^2.0.0",
 
26
  "ai": "^2.2.31",
27
  "class-variance-authority": "^0.7.0",
28
  "clsx": "^2.1.0",
29
+ "date-fns": "^3.6.0",
30
  "focus-trap-react": "^10.2.3",
31
  "framer-motion": "^10.18.0",
32
  "geist": "^1.2.1",
 
46
  "react-syntax-highlighter": "^15.5.0",
47
  "react-textarea-autosize": "^8.5.3",
48
  "remark-gfm": "^3.0.1",
49
+ "remark-math": "^5.1.1",
50
+ "uuid": "^9.0.1"
51
  },
52
  "devDependencies": {
53
  "@tailwindcss/typography": "^0.5.10",
 
55
  "@types/react": "^18.2.48",
56
  "@types/react-dom": "^18.2.18",
57
  "@types/react-syntax-highlighter": "^15.5.11",
58
+ "@types/uuid": "^9.0.8",
59
  "@typescript-eslint/parser": "^6.19.0",
60
  "autoprefixer": "^10.4.17",
61
  "eslint": "^8.56.0",
pnpm-lock.yaml CHANGED
@@ -4,167 +4,168 @@ settings:
4
  autoInstallPeers: true
5
  excludeLinksFromLockfile: false
6
 
7
- importers:
8
-
9
- .:
10
- dependencies:
11
- '@radix-ui/react-alert-dialog':
12
- specifier: ^1.0.5
13
- version: 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
14
- '@radix-ui/react-dialog':
15
- specifier: ^1.0.5
16
- version: 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
17
- '@radix-ui/react-dropdown-menu':
18
- specifier: ^2.0.6
19
- version: 2.0.6(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
20
- '@radix-ui/react-label':
21
- specifier: ^2.0.2
22
- version: 2.0.2(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
23
- '@radix-ui/react-select':
24
- specifier: ^2.0.0
25
- version: 2.0.0(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
26
- '@radix-ui/react-separator':
27
- specifier: ^1.0.3
28
- version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
29
- '@radix-ui/react-slot':
30
- specifier: ^1.0.2
31
- version: 1.0.2(@types/react@18.2.60)(react@18.2.0)
32
- '@radix-ui/react-switch':
33
- specifier: ^1.0.3
34
- version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
35
- '@radix-ui/react-tooltip':
36
- specifier: ^1.0.7
37
- version: 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
38
- '@vercel/analytics':
39
- specifier: ^1.1.2
40
- version: 1.2.2(next@14.1.0)(react@18.2.0)
41
- '@vercel/kv':
42
- specifier: ^1.0.1
43
- version: 1.0.1
44
- '@vercel/og':
45
- specifier: ^0.6.2
46
- version: 0.6.2
47
- ai:
48
- specifier: ^2.2.31
49
- version: 2.2.36(react@18.2.0)(solid-js@1.8.16)(svelte@4.2.12)(vue@3.4.21)
50
- class-variance-authority:
51
- specifier: ^0.7.0
52
- version: 0.7.0
53
- clsx:
54
- specifier: ^2.1.0
55
- version: 2.1.0
56
- focus-trap-react:
57
- specifier: ^10.2.3
58
- version: 10.2.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0)
59
- framer-motion:
60
- specifier: ^10.18.0
61
- version: 10.18.0(react-dom@18.2.0)(react@18.2.0)
62
- geist:
63
- specifier: ^1.2.1
64
- version: 1.2.2(next@14.1.0)
65
- immer:
66
- specifier: ^10.0.3
67
- version: 10.0.3
68
- jotai:
69
- specifier: ^2.7.0
70
- version: 2.7.0(@types/react@18.2.60)(react@18.2.0)
71
- nanoid:
72
- specifier: ^5.0.4
73
- version: 5.0.6
74
- next:
75
- specifier: 14.1.0
76
- version: 14.1.0(react-dom@18.2.0)(react@18.2.0)
77
- next-auth:
78
- specifier: 5.0.0-beta.16
79
- version: 5.0.0-beta.16(next@14.1.0)(react@18.2.0)
80
- next-themes:
81
- specifier: ^0.2.1
82
- version: 0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
83
- openai:
84
- specifier: ^4.24.7
85
- version: 4.28.0
86
- react:
87
- specifier: ^18.2.0
88
- version: 18.2.0
89
- react-dom:
90
- specifier: ^18.2.0
91
- version: 18.2.0(react@18.2.0)
92
- react-dropzone:
93
- specifier: ^14.2.3
94
- version: 14.2.3(react@18.2.0)
95
- react-hot-toast:
96
- specifier: ^2.4.1
97
- version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0)
98
- react-intersection-observer:
99
- specifier: ^9.5.3
100
- version: 9.8.1(react-dom@18.2.0)(react@18.2.0)
101
- react-markdown:
102
- specifier: ^8.0.7
103
- version: 8.0.7(@types/react@18.2.60)(react@18.2.0)
104
- react-syntax-highlighter:
105
- specifier: ^15.5.0
106
- version: 15.5.0(react@18.2.0)
107
- react-textarea-autosize:
108
- specifier: ^8.5.3
109
- version: 8.5.3(@types/react@18.2.60)(react@18.2.0)
110
- remark-gfm:
111
- specifier: ^3.0.1
112
- version: 3.0.1
113
- remark-math:
114
- specifier: ^5.1.1
115
- version: 5.1.1
116
- devDependencies:
117
- '@tailwindcss/typography':
118
- specifier: ^0.5.10
119
- version: 0.5.10(tailwindcss@3.4.1)
120
- '@types/node':
121
- specifier: ^20.11.5
122
- version: 20.11.20
123
- '@types/react':
124
- specifier: ^18.2.48
125
- version: 18.2.60
126
- '@types/react-dom':
127
- specifier: ^18.2.18
128
- version: 18.2.19
129
- '@types/react-syntax-highlighter':
130
- specifier: ^15.5.11
131
- version: 15.5.11
132
- '@typescript-eslint/parser':
133
- specifier: ^6.19.0
134
- version: 6.21.0(eslint@8.57.0)(typescript@5.3.3)
135
- autoprefixer:
136
- specifier: ^10.4.17
137
- version: 10.4.17(postcss@8.4.35)
138
- eslint:
139
- specifier: ^8.56.0
140
- version: 8.57.0
141
- eslint-config-next:
142
- specifier: 14.1.0
143
- version: 14.1.0(eslint@8.57.0)(typescript@5.3.3)
144
- eslint-config-prettier:
145
- specifier: ^9.1.0
146
- version: 9.1.0(eslint@8.57.0)
147
- eslint-plugin-tailwindcss:
148
- specifier: ^3.14.0
149
- version: 3.14.3(tailwindcss@3.4.1)
150
- postcss:
151
- specifier: ^8.4.33
152
- version: 8.4.35
153
- prettier:
154
- specifier: ^3.2.4
155
- version: 3.2.5
156
- tailwind-merge:
157
- specifier: ^2.2.0
158
- version: 2.2.1
159
- tailwindcss:
160
- specifier: ^3.4.1
161
- version: 3.4.1
162
- tailwindcss-animate:
163
- specifier: ^1.0.7
164
- version: 1.0.7(tailwindcss@3.4.1)
165
- typescript:
166
- specifier: ^5.3.3
167
- version: 5.3.3
 
168
 
169
  packages:
170
 
@@ -393,6 +394,7 @@ packages:
393
  dependencies:
394
  '@jridgewell/resolve-uri': 3.1.2
395
  '@jridgewell/sourcemap-codec': 1.4.15
 
396
 
397
  /@jridgewell/trace-mapping@0.3.25:
398
  resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
@@ -536,32 +538,6 @@ packages:
536
  '@babel/runtime': 7.23.9
537
  dev: false
538
 
539
- /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0):
540
- resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==}
541
- peerDependencies:
542
- '@types/react': '*'
543
- '@types/react-dom': '*'
544
- react: ^16.8 || ^17.0 || ^18.0
545
- react-dom: ^16.8 || ^17.0 || ^18.0
546
- peerDependenciesMeta:
547
- '@types/react':
548
- optional: true
549
- '@types/react-dom':
550
- optional: true
551
- dependencies:
552
- '@babel/runtime': 7.23.9
553
- '@radix-ui/primitive': 1.0.1
554
- '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.60)(react@18.2.0)
555
- '@radix-ui/react-context': 1.0.1(@types/react@18.2.60)(react@18.2.0)
556
- '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
557
- '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
558
- '@radix-ui/react-slot': 1.0.2(@types/react@18.2.60)(react@18.2.0)
559
- '@types/react': 18.2.60
560
- '@types/react-dom': 18.2.19
561
- react: 18.2.0
562
- react-dom: 18.2.0(react@18.2.0)
563
- dev: false
564
-
565
  /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0):
566
  resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
567
  peerDependencies:
@@ -635,40 +611,6 @@ packages:
635
  react: 18.2.0
636
  dev: false
637
 
638
- /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0):
639
- resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
640
- peerDependencies:
641
- '@types/react': '*'
642
- '@types/react-dom': '*'
643
- react: ^16.8 || ^17.0 || ^18.0
644
- react-dom: ^16.8 || ^17.0 || ^18.0
645
- peerDependenciesMeta:
646
- '@types/react':
647
- optional: true
648
- '@types/react-dom':
649
- optional: true
650
- dependencies:
651
- '@babel/runtime': 7.23.9
652
- '@radix-ui/primitive': 1.0.1
653
- '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.60)(react@18.2.0)
654
- '@radix-ui/react-context': 1.0.1(@types/react@18.2.60)(react@18.2.0)
655
- '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
656
- '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.60)(react@18.2.0)
657
- '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
658
- '@radix-ui/react-id': 1.0.1(@types/react@18.2.60)(react@18.2.0)
659
- '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
660
- '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
661
- '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
662
- '@radix-ui/react-slot': 1.0.2(@types/react@18.2.60)(react@18.2.0)
663
- '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.60)(react@18.2.0)
664
- '@types/react': 18.2.60
665
- '@types/react-dom': 18.2.19
666
- aria-hidden: 1.2.3
667
- react: 18.2.0
668
- react-dom: 18.2.0(react@18.2.0)
669
- react-remove-scroll: 2.5.5(@types/react@18.2.60)(react@18.2.0)
670
- dev: false
671
-
672
  /@radix-ui/react-direction@1.0.1(@types/react@18.2.60)(react@18.2.0):
673
  resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
674
  peerDependencies:
@@ -1354,6 +1296,10 @@ packages:
1354
  resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
1355
  dev: false
1356
 
 
 
 
 
1357
  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.3.3):
1358
  resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
1359
  engines: {node: ^16.0.0 || >=18.0.0}
@@ -2071,6 +2017,10 @@ packages:
2071
  resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
2072
  dev: true
2073
 
 
 
 
 
2074
  /debug@3.2.7:
2075
  resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
2076
  peerDependencies:
@@ -5097,7 +5047,7 @@ packages:
5097
  dependencies:
5098
  '@ampproject/remapping': 2.3.0
5099
  '@jridgewell/sourcemap-codec': 1.4.15
5100
- '@jridgewell/trace-mapping': 0.3.23
5101
  '@types/estree': 1.0.5
5102
  acorn: 8.11.3
5103
  aria-query: 5.3.0
@@ -5488,6 +5438,11 @@ packages:
5488
  resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
5489
  dev: true
5490
 
 
 
 
 
 
5491
  /uvu@0.5.6:
5492
  resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
5493
  engines: {node: '>=8'}
 
4
  autoInstallPeers: true
5
  excludeLinksFromLockfile: false
6
 
7
+ dependencies:
8
+ '@radix-ui/react-dropdown-menu':
9
+ specifier: ^2.0.6
10
+ version: 2.0.6(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
11
+ '@radix-ui/react-label':
12
+ specifier: ^2.0.2
13
+ version: 2.0.2(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
14
+ '@radix-ui/react-select':
15
+ specifier: ^2.0.0
16
+ version: 2.0.0(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
17
+ '@radix-ui/react-separator':
18
+ specifier: ^1.0.3
19
+ version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
20
+ '@radix-ui/react-slot':
21
+ specifier: ^1.0.2
22
+ version: 1.0.2(@types/react@18.2.60)(react@18.2.0)
23
+ '@radix-ui/react-switch':
24
+ specifier: ^1.0.3
25
+ version: 1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
26
+ '@radix-ui/react-tooltip':
27
+ specifier: ^1.0.7
28
+ version: 1.0.7(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0)
29
+ '@vercel/analytics':
30
+ specifier: ^1.1.2
31
+ version: 1.2.2(next@14.1.0)(react@18.2.0)
32
+ '@vercel/kv':
33
+ specifier: ^1.0.1
34
+ version: 1.0.1
35
+ '@vercel/og':
36
+ specifier: ^0.6.2
37
+ version: 0.6.2
38
+ ai:
39
+ specifier: ^2.2.31
40
+ version: 2.2.36(react@18.2.0)(solid-js@1.8.16)(svelte@4.2.12)(vue@3.4.21)
41
+ class-variance-authority:
42
+ specifier: ^0.7.0
43
+ version: 0.7.0
44
+ clsx:
45
+ specifier: ^2.1.0
46
+ version: 2.1.0
47
+ date-fns:
48
+ specifier: ^3.6.0
49
+ version: 3.6.0
50
+ focus-trap-react:
51
+ specifier: ^10.2.3
52
+ version: 10.2.3(prop-types@15.8.1)(react-dom@18.2.0)(react@18.2.0)
53
+ framer-motion:
54
+ specifier: ^10.18.0
55
+ version: 10.18.0(react-dom@18.2.0)(react@18.2.0)
56
+ geist:
57
+ specifier: ^1.2.1
58
+ version: 1.2.2(next@14.1.0)
59
+ immer:
60
+ specifier: ^10.0.3
61
+ version: 10.0.3
62
+ jotai:
63
+ specifier: ^2.7.0
64
+ version: 2.7.0(@types/react@18.2.60)(react@18.2.0)
65
+ nanoid:
66
+ specifier: ^5.0.4
67
+ version: 5.0.6
68
+ next:
69
+ specifier: 14.1.0
70
+ version: 14.1.0(react-dom@18.2.0)(react@18.2.0)
71
+ next-auth:
72
+ specifier: 5.0.0-beta.16
73
+ version: 5.0.0-beta.16(next@14.1.0)(react@18.2.0)
74
+ next-themes:
75
+ specifier: ^0.2.1
76
+ version: 0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
77
+ openai:
78
+ specifier: ^4.24.7
79
+ version: 4.28.0
80
+ react:
81
+ specifier: ^18.2.0
82
+ version: 18.2.0
83
+ react-dom:
84
+ specifier: ^18.2.0
85
+ version: 18.2.0(react@18.2.0)
86
+ react-dropzone:
87
+ specifier: ^14.2.3
88
+ version: 14.2.3(react@18.2.0)
89
+ react-hot-toast:
90
+ specifier: ^2.4.1
91
+ version: 2.4.1(csstype@3.1.3)(react-dom@18.2.0)(react@18.2.0)
92
+ react-intersection-observer:
93
+ specifier: ^9.5.3
94
+ version: 9.8.1(react-dom@18.2.0)(react@18.2.0)
95
+ react-markdown:
96
+ specifier: ^8.0.7
97
+ version: 8.0.7(@types/react@18.2.60)(react@18.2.0)
98
+ react-syntax-highlighter:
99
+ specifier: ^15.5.0
100
+ version: 15.5.0(react@18.2.0)
101
+ react-textarea-autosize:
102
+ specifier: ^8.5.3
103
+ version: 8.5.3(@types/react@18.2.60)(react@18.2.0)
104
+ remark-gfm:
105
+ specifier: ^3.0.1
106
+ version: 3.0.1
107
+ remark-math:
108
+ specifier: ^5.1.1
109
+ version: 5.1.1
110
+ uuid:
111
+ specifier: ^9.0.1
112
+ version: 9.0.1
113
+
114
+ devDependencies:
115
+ '@tailwindcss/typography':
116
+ specifier: ^0.5.10
117
+ version: 0.5.10(tailwindcss@3.4.1)
118
+ '@types/node':
119
+ specifier: ^20.11.5
120
+ version: 20.11.20
121
+ '@types/react':
122
+ specifier: ^18.2.48
123
+ version: 18.2.60
124
+ '@types/react-dom':
125
+ specifier: ^18.2.18
126
+ version: 18.2.19
127
+ '@types/react-syntax-highlighter':
128
+ specifier: ^15.5.11
129
+ version: 15.5.11
130
+ '@types/uuid':
131
+ specifier: ^9.0.8
132
+ version: 9.0.8
133
+ '@typescript-eslint/parser':
134
+ specifier: ^6.19.0
135
+ version: 6.21.0(eslint@8.57.0)(typescript@5.3.3)
136
+ autoprefixer:
137
+ specifier: ^10.4.17
138
+ version: 10.4.17(postcss@8.4.35)
139
+ eslint:
140
+ specifier: ^8.56.0
141
+ version: 8.57.0
142
+ eslint-config-next:
143
+ specifier: 14.1.0
144
+ version: 14.1.0(eslint@8.57.0)(typescript@5.3.3)
145
+ eslint-config-prettier:
146
+ specifier: ^9.1.0
147
+ version: 9.1.0(eslint@8.57.0)
148
+ eslint-plugin-tailwindcss:
149
+ specifier: ^3.14.0
150
+ version: 3.14.3(tailwindcss@3.4.1)
151
+ postcss:
152
+ specifier: ^8.4.33
153
+ version: 8.4.35
154
+ prettier:
155
+ specifier: ^3.2.4
156
+ version: 3.2.5
157
+ tailwind-merge:
158
+ specifier: ^2.2.0
159
+ version: 2.2.1
160
+ tailwindcss:
161
+ specifier: ^3.4.1
162
+ version: 3.4.1
163
+ tailwindcss-animate:
164
+ specifier: ^1.0.7
165
+ version: 1.0.7(tailwindcss@3.4.1)
166
+ typescript:
167
+ specifier: ^5.3.3
168
+ version: 5.3.3
169
 
170
  packages:
171
 
 
394
  dependencies:
395
  '@jridgewell/resolve-uri': 3.1.2
396
  '@jridgewell/sourcemap-codec': 1.4.15
397
+ dev: true
398
 
399
  /@jridgewell/trace-mapping@0.3.25:
400
  resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
 
538
  '@babel/runtime': 7.23.9
539
  dev: false
540
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
  /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.19)(@types/react@18.2.60)(react-dom@18.2.0)(react@18.2.0):
542
  resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
543
  peerDependencies:
 
611
  react: 18.2.0
612
  dev: false
613
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
614
  /@radix-ui/react-direction@1.0.1(@types/react@18.2.60)(react@18.2.0):
615
  resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
616
  peerDependencies:
 
1296
  resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
1297
  dev: false
1298
 
1299
+ /@types/uuid@9.0.8:
1300
+ resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==}
1301
+ dev: true
1302
+
1303
  /@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.3.3):
1304
  resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
1305
  engines: {node: ^16.0.0 || >=18.0.0}
 
2017
  resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
2018
  dev: true
2019
 
2020
+ /date-fns@3.6.0:
2021
+ resolution: {integrity: sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==}
2022
+ dev: false
2023
+
2024
  /debug@3.2.7:
2025
  resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
2026
  peerDependencies:
 
5047
  dependencies:
5048
  '@ampproject/remapping': 2.3.0
5049
  '@jridgewell/sourcemap-codec': 1.4.15
5050
+ '@jridgewell/trace-mapping': 0.3.25
5051
  '@types/estree': 1.0.5
5052
  acorn: 8.11.3
5053
  aria-query: 5.3.0
 
5438
  resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
5439
  dev: true
5440
 
5441
+ /uuid@9.0.1:
5442
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
5443
+ hasBin: true
5444
+ dev: false
5445
+
5446
  /uvu@0.5.6:
5447
  resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
5448
  engines: {node: '>=8'}