mukaddamzaid commited on
Commit
bb25920
·
1 Parent(s): d83197f

feat: update dependencies and enhance chat and markdown components

Browse files

- Upgraded various dependencies in package.json for improved performance and security, including @daytonaio/sdk, drizzle-orm, framer-motion, and others.
- Introduced a custom CodeBlock component in markdown.tsx for enhanced code rendering with a copy button.
- Refactored chat.tsx to adjust layout and improve user experience.
- Enhanced markdown rendering with better styling and break-word support for improved readability.
- Updated MCP server manager to auto-start disconnected servers and improve error handling.

components/chat.tsx CHANGED
@@ -10,7 +10,6 @@ import { toast } from "sonner";
10
  import { useRouter, useParams } from "next/navigation";
11
  import { getUserId } from "@/lib/user-id";
12
  import { useLocalStorage } from "@/lib/hooks/use-local-storage";
13
- import { STORAGE_KEYS } from "@/lib/constants";
14
  import { useQuery, useQueryClient } from "@tanstack/react-query";
15
  import { convertToUIMessages } from "@/lib/chat-store";
16
  import { type Message as DBMessage } from "@/lib/db/schema";
@@ -153,7 +152,7 @@ export default function Chat() {
153
  const isLoading = status === "streaming" || status === "submitted" || isLoadingChat;
154
 
155
  return (
156
- <div className="h-dvh flex flex-col justify-center w-full max-w-3xl mx-auto px-4 sm:px-6 md:py-4">
157
  {messages.length === 0 && !isLoadingChat ? (
158
  <div className="max-w-xl mx-auto w-full">
159
  <ProjectOverview />
@@ -179,7 +178,7 @@ export default function Chat() {
179
  </div>
180
  <form
181
  onSubmit={handleFormSubmit}
182
- className="mt-2 w-full mx-auto mb-4 sm:mb-auto"
183
  >
184
  <Textarea
185
  selectedModel={selectedModel}
 
10
  import { useRouter, useParams } from "next/navigation";
11
  import { getUserId } from "@/lib/user-id";
12
  import { useLocalStorage } from "@/lib/hooks/use-local-storage";
 
13
  import { useQuery, useQueryClient } from "@tanstack/react-query";
14
  import { convertToUIMessages } from "@/lib/chat-store";
15
  import { type Message as DBMessage } from "@/lib/db/schema";
 
152
  const isLoading = status === "streaming" || status === "submitted" || isLoadingChat;
153
 
154
  return (
155
+ <div className="h-dvh flex flex-col justify-center w-full max-w-[430px] sm:max-w-3xl mx-auto px-4 sm:px-6 py-3">
156
  {messages.length === 0 && !isLoadingChat ? (
157
  <div className="max-w-xl mx-auto w-full">
158
  <ProjectOverview />
 
178
  </div>
179
  <form
180
  onSubmit={handleFormSubmit}
181
+ className="mt-2 w-full mx-auto"
182
  >
183
  <Textarea
184
  selectedModel={selectedModel}
components/markdown.tsx CHANGED
@@ -1,13 +1,69 @@
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
  import Link from "next/link";
3
- import React, { memo } from "react";
4
  import ReactMarkdown, { type Components } from "react-markdown";
5
  import remarkGfm from "remark-gfm";
6
  import { cn } from "@/lib/utils";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  const components: Partial<Components> = {
9
  pre: ({ children, ...props }) => (
10
- <pre className="overflow-x-auto rounded-lg bg-zinc-100 dark:bg-zinc-800/50 black:bg-zinc-800/50 p-2.5 my-1.5 text-sm" {...props}>
11
  {children}
12
  </pre>
13
  ),
@@ -18,36 +74,34 @@ const components: Partial<Components> = {
18
  if (isInline) {
19
  return (
20
  <code
21
- className="px-1 py-0.5 rounded-md bg-zinc-100 dark:bg-zinc-800/50 black:bg-zinc-800/50 text-zinc-700 dark:text-zinc-300 black:text-zinc-300 text-[0.9em] font-mono"
22
  {...props}
23
  >
24
  {children}
25
  </code>
26
  );
27
  }
28
- return (
29
- <code className={cn("block font-mono text-sm", className)} {...props}>
30
- {children}
31
- </code>
32
- );
33
  },
34
  ol: ({ node, children, ...props }) => (
35
- <ol className="list-decimal list-outside ml-4 space-y-0.5 my-1.5" {...props}>
36
  {children}
37
  </ol>
38
  ),
39
  ul: ({ node, children, ...props }) => (
40
- <ul className="list-disc list-outside ml-4 space-y-0.5 my-1.5" {...props}>
41
  {children}
42
  </ul>
43
  ),
44
  li: ({ node, children, ...props }) => (
45
- <li className="leading-normal" {...props}>
46
  {children}
47
  </li>
48
  ),
49
  p: ({ node, children, ...props }) => (
50
- <p className="leading-relaxed my-1" {...props}>
51
  {children}
52
  </p>
53
  ),
@@ -63,7 +117,7 @@ const components: Partial<Components> = {
63
  ),
64
  blockquote: ({ node, children, ...props }) => (
65
  <blockquote
66
- className="border-l-2 border-zinc-200 dark:border-zinc-700 black:border-zinc-700 pl-3 my-1.5 italic text-zinc-600 dark:text-zinc-400 black:text-zinc-400"
67
  {...props}
68
  >
69
  {children}
@@ -72,7 +126,7 @@ const components: Partial<Components> = {
72
  a: ({ node, children, ...props }) => (
73
  // @ts-expect-error error
74
  <Link
75
- className="text-blue-500 hover:underline hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 black:text-blue-400 black:hover:text-blue-300 transition-colors"
76
  target="_blank"
77
  rel="noreferrer"
78
  {...props}
@@ -81,38 +135,38 @@ const components: Partial<Components> = {
81
  </Link>
82
  ),
83
  h1: ({ node, children, ...props }) => (
84
- <h1 className="text-2xl font-semibold mt-3 mb-1.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
85
  {children}
86
  </h1>
87
  ),
88
  h2: ({ node, children, ...props }) => (
89
- <h2 className="text-xl font-semibold mt-2.5 mb-1.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
90
  {children}
91
  </h2>
92
  ),
93
  h3: ({ node, children, ...props }) => (
94
- <h3 className="text-lg font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
95
  {children}
96
  </h3>
97
  ),
98
  h4: ({ node, children, ...props }) => (
99
- <h4 className="text-base font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
100
  {children}
101
  </h4>
102
  ),
103
  h5: ({ node, children, ...props }) => (
104
- <h5 className="text-sm font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
105
  {children}
106
  </h5>
107
  ),
108
  h6: ({ node, children, ...props }) => (
109
- <h6 className="text-xs font-semibold mt-2 mb-0.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200" {...props}>
110
  {children}
111
  </h6>
112
  ),
113
  table: ({ node, children, ...props }) => (
114
- <div className="my-1.5 overflow-x-auto">
115
- <table className="min-w-full divide-y divide-zinc-200 dark:divide-zinc-700 black:divide-zinc-700" {...props}>
116
  {children}
117
  </table>
118
  </div>
 
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
  import Link from "next/link";
3
+ import React, { memo, useState } from "react";
4
  import ReactMarkdown, { type Components } from "react-markdown";
5
  import remarkGfm from "remark-gfm";
6
  import { cn } from "@/lib/utils";
7
+ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
8
+ import { tomorrow, oneLight } from "react-syntax-highlighter/dist/cjs/styles/prism";
9
+ import { Check, Copy } from "lucide-react";
10
+ import { useTheme } from "next-themes";
11
+
12
+ // Code Block component with copy button
13
+ const CodeBlock = ({ className, children }: { className?: string; children: string }) => {
14
+ const [copied, setCopied] = useState(false);
15
+ const match = /language-(\w+)/.exec(className || '');
16
+ const language = match ? match[1] : '';
17
+ const { theme } = useTheme();
18
+
19
+ const handleCopy = () => {
20
+ navigator.clipboard.writeText(children);
21
+ setCopied(true);
22
+ setTimeout(() => setCopied(false), 2000);
23
+ };
24
+
25
+ // Select appropriate theme based on the app's current theme
26
+ const codeStyle = theme === 'light' || theme === 'sunset' ? oneLight : tomorrow;
27
+
28
+ return (
29
+ <div className="relative group rounded-lg overflow-hidden border border-border mb-3 md:mb-4 w-full max-w-full">
30
+ <div className="flex items-center justify-between px-3 md:px-4 py-1.5 bg-muted/50 text-muted-foreground text-xs font-mono">
31
+ <span className="truncate">{language || 'plain text'}</span>
32
+ <button
33
+ onClick={handleCopy}
34
+ className="p-1 hover:text-foreground transition-colors ml-2 flex-shrink-0"
35
+ aria-label="Copy code"
36
+ >
37
+ {copied ? <Check size={14} /> : <Copy size={14} />}
38
+ </button>
39
+ </div>
40
+ <div className="w-full max-w-full overflow-x-auto">
41
+ <SyntaxHighlighter
42
+ language={language}
43
+ style={codeStyle}
44
+ customStyle={{
45
+ margin: 0,
46
+ padding: '0.75rem 1rem',
47
+ fontSize: '0.85em',
48
+ backgroundColor: 'var(--secondary)',
49
+ borderRadius: 0,
50
+ width: '100%',
51
+ minWidth: '100%',
52
+ }}
53
+ PreTag="div"
54
+ wrapLines
55
+ wrapLongLines
56
+ >
57
+ {children}
58
+ </SyntaxHighlighter>
59
+ </div>
60
+ </div>
61
+ );
62
+ };
63
 
64
  const components: Partial<Components> = {
65
  pre: ({ children, ...props }) => (
66
+ <pre className="my-1.5 text-sm w-full max-w-full" {...props}>
67
  {children}
68
  </pre>
69
  ),
 
74
  if (isInline) {
75
  return (
76
  <code
77
+ className="px-1.5 py-0.5 rounded-md bg-secondary/50 text-foreground font-mono text-[0.85em] break-words"
78
  {...props}
79
  >
80
  {children}
81
  </code>
82
  );
83
  }
84
+
85
+ // For code blocks, use our custom CodeBlock component
86
+ return <CodeBlock className={className}>{String(children).trim()}</CodeBlock>;
 
 
87
  },
88
  ol: ({ node, children, ...props }) => (
89
+ <ol className="list-decimal list-outside ml-3 md:ml-4 space-y-0.5 my-1.5 text-sm sm:text-base" {...props}>
90
  {children}
91
  </ol>
92
  ),
93
  ul: ({ node, children, ...props }) => (
94
+ <ul className="list-disc list-outside ml-3 md:ml-4 space-y-0.5 my-1.5 text-sm sm:text-base" {...props}>
95
  {children}
96
  </ul>
97
  ),
98
  li: ({ node, children, ...props }) => (
99
+ <li className="leading-normal break-words" {...props}>
100
  {children}
101
  </li>
102
  ),
103
  p: ({ node, children, ...props }) => (
104
+ <p className="leading-relaxed my-1.5 text-sm sm:text-base break-words" {...props}>
105
  {children}
106
  </p>
107
  ),
 
117
  ),
118
  blockquote: ({ node, children, ...props }) => (
119
  <blockquote
120
+ className="border-l-2 border-zinc-200 dark:border-zinc-700 black:border-zinc-700 pl-2 md:pl-3 my-1.5 italic text-zinc-600 dark:text-zinc-400 black:text-zinc-400 text-sm sm:text-base"
121
  {...props}
122
  >
123
  {children}
 
126
  a: ({ node, children, ...props }) => (
127
  // @ts-expect-error error
128
  <Link
129
+ className="text-blue-500 hover:underline hover:text-blue-600 dark:text-blue-400 dark:hover:text-blue-300 black:text-blue-400 black:hover:text-blue-300 transition-colors break-words"
130
  target="_blank"
131
  rel="noreferrer"
132
  {...props}
 
135
  </Link>
136
  ),
137
  h1: ({ node, children, ...props }) => (
138
+ <h1 className="text-xl md:text-2xl font-semibold mt-3 mb-1.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
139
  {children}
140
  </h1>
141
  ),
142
  h2: ({ node, children, ...props }) => (
143
+ <h2 className="text-lg md:text-xl font-semibold mt-2.5 mb-1.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
144
  {children}
145
  </h2>
146
  ),
147
  h3: ({ node, children, ...props }) => (
148
+ <h3 className="text-base md:text-lg font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
149
  {children}
150
  </h3>
151
  ),
152
  h4: ({ node, children, ...props }) => (
153
+ <h4 className="text-sm md:text-base font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
154
  {children}
155
  </h4>
156
  ),
157
  h5: ({ node, children, ...props }) => (
158
+ <h5 className="text-xs md:text-sm font-semibold mt-2 mb-1 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
159
  {children}
160
  </h5>
161
  ),
162
  h6: ({ node, children, ...props }) => (
163
+ <h6 className="text-xs font-semibold mt-2 mb-0.5 text-zinc-800 dark:text-zinc-200 black:text-zinc-200 break-words" {...props}>
164
  {children}
165
  </h6>
166
  ),
167
  table: ({ node, children, ...props }) => (
168
+ <div className="my-1.5 overflow-x-auto w-full max-w-full">
169
+ <table className="min-w-full divide-y divide-zinc-200 dark:divide-zinc-700 black:divide-zinc-700 text-sm" {...props}>
170
  {children}
171
  </table>
172
  </div>
components/mcp-server-manager.tsx CHANGED
@@ -235,11 +235,29 @@ export const MCPServerManager = ({
235
  toast.success(`Disabled MCP server: ${server.name}`);
236
  }
237
  } else {
238
- // Add to selected servers but DON'T auto-start
239
  onSelectedServersChange([...selectedServers, id]);
240
  const server = servers.find(s => s.id === id);
241
 
242
  if (server) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  toast.success(`Enabled MCP server: ${server.name}`);
244
  }
245
  }
 
235
  toast.success(`Disabled MCP server: ${server.name}`);
236
  }
237
  } else {
238
+ // Add to selected servers
239
  onSelectedServersChange([...selectedServers, id]);
240
  const server = servers.find(s => s.id === id);
241
 
242
  if (server) {
243
+ // Auto-start the server if it's disconnected
244
+ if (!server.status || server.status === 'disconnected' || server.status === 'error') {
245
+ updateServerStatus(server.id, 'connecting');
246
+ startServer(id)
247
+ .then(success => {
248
+ if (success) {
249
+ console.log(`Server ${server.name} successfully connected`);
250
+ } else {
251
+ console.error(`Failed to connect server ${server.name}`);
252
+ }
253
+ })
254
+ .catch(error => {
255
+ console.error(`Error connecting server ${server.name}:`, error);
256
+ updateServerStatus(server.id, 'error',
257
+ `Failed to connect: ${error instanceof Error ? error.message : String(error)}`);
258
+ });
259
+ }
260
+
261
  toast.success(`Enabled MCP server: ${server.name}`);
262
  }
263
  }
lib/mcp-sandbox.ts CHANGED
@@ -9,10 +9,11 @@ export const startMcpSandbox = async ({
9
  envs?: Record<string, string>
10
  }) => {
11
  console.log("Creating sandbox...");
12
-
13
  try {
14
  const daytona = new Daytona();
15
- const sandbox = await daytona.create({
 
16
  resources: {
17
  cpu: 2,
18
  memory: 4,
@@ -23,7 +24,11 @@ export const startMcpSandbox = async ({
23
  envVars: {
24
  ...envs,
25
  },
26
- });
 
 
 
 
27
 
28
  const host = await sandbox.getPreviewLink(3000);
29
  const url = host.url;
@@ -33,7 +38,7 @@ export const startMcpSandbox = async ({
33
 
34
  const sessionId = Math.random().toString(36).substring(2, 30);
35
  await sandbox.process.createSession(sessionId);
36
-
37
  // Handle Python package installation if command is a Python command
38
  const isPythonCommand = cmd.startsWith('python') || cmd.startsWith('python3');
39
  let installResult = null;
@@ -50,7 +55,7 @@ export const startMcpSandbox = async ({
50
  },
51
  1000 * 300 // 5 minutes
52
  );
53
-
54
  console.log("install result", installResult.output);
55
  if (installResult.exitCode) {
56
  console.error(`Failed to install package ${packageName}. Exit code: ${installResult.exitCode}`);
@@ -68,7 +73,7 @@ export const startMcpSandbox = async ({
68
  },
69
  0
70
  );
71
-
72
  console.log("mcp server result", mcpServer.output);
73
 
74
  if (mcpServer.exitCode) {
@@ -112,23 +117,37 @@ class McpSandbox {
112
  if (!this.sandbox || !this.sessionId) {
113
  throw new Error("Sandbox or session not initialized");
114
  }
115
-
116
  const session = await this.sandbox.process.getSession(this.sessionId);
117
  return session;
118
  }
119
 
 
 
 
 
 
 
 
120
  async stop(): Promise<void> {
121
  if (!this.sandbox) {
122
  throw new Error("Sandbox not initialized");
123
  }
124
-
125
  try {
126
- await this.sandbox.delete();
127
  } catch (error) {
128
  console.error("Error stopping sandbox:", error);
129
  throw error;
130
  }
131
  }
 
 
 
 
 
 
 
132
  }
133
 
134
  export type { McpSandbox };
 
9
  envs?: Record<string, string>
10
  }) => {
11
  console.log("Creating sandbox...");
12
+
13
  try {
14
  const daytona = new Daytona();
15
+ const sandbox = await daytona.create(
16
+ {
17
  resources: {
18
  cpu: 2,
19
  memory: 4,
 
24
  envVars: {
25
  ...envs,
26
  },
27
+ },
28
+ {
29
+ timeout: 0,
30
+ }
31
+ );
32
 
33
  const host = await sandbox.getPreviewLink(3000);
34
  const url = host.url;
 
38
 
39
  const sessionId = Math.random().toString(36).substring(2, 30);
40
  await sandbox.process.createSession(sessionId);
41
+
42
  // Handle Python package installation if command is a Python command
43
  const isPythonCommand = cmd.startsWith('python') || cmd.startsWith('python3');
44
  let installResult = null;
 
55
  },
56
  1000 * 300 // 5 minutes
57
  );
58
+
59
  console.log("install result", installResult.output);
60
  if (installResult.exitCode) {
61
  console.error(`Failed to install package ${packageName}. Exit code: ${installResult.exitCode}`);
 
73
  },
74
  0
75
  );
76
+
77
  console.log("mcp server result", mcpServer.output);
78
 
79
  if (mcpServer.exitCode) {
 
117
  if (!this.sandbox || !this.sessionId) {
118
  throw new Error("Sandbox or session not initialized");
119
  }
120
+
121
  const session = await this.sandbox.process.getSession(this.sessionId);
122
  return session;
123
  }
124
 
125
+ async start(): Promise<void> {
126
+ if (!this.sandbox) {
127
+ throw new Error("Sandbox not initialized");
128
+ }
129
+ await this.sandbox.start();
130
+ }
131
+
132
  async stop(): Promise<void> {
133
  if (!this.sandbox) {
134
  throw new Error("Sandbox not initialized");
135
  }
136
+
137
  try {
138
+ await this.sandbox.stop();
139
  } catch (error) {
140
  console.error("Error stopping sandbox:", error);
141
  throw error;
142
  }
143
  }
144
+
145
+ async delete(): Promise<void> {
146
+ if (!this.sandbox) {
147
+ throw new Error("Sandbox not initialized");
148
+ }
149
+ await this.sandbox.delete();
150
+ }
151
  }
152
 
153
  export type { McpSandbox };
package.json CHANGED
@@ -19,7 +19,8 @@
19
  "@ai-sdk/openai": "^1.3.22",
20
  "@ai-sdk/react": "^1.2.12",
21
  "@ai-sdk/xai": "^1.2.16",
22
- "@daytonaio/sdk": "^0.17.0",
 
23
  "@neondatabase/serverless": "^1.0.0",
24
  "@radix-ui/react-accordion": "^1.2.7",
25
  "@radix-ui/react-avatar": "^1.1.6",
@@ -34,42 +35,50 @@
34
  "@radix-ui/react-tooltip": "^1.2.3",
35
  "@tanstack/react-query": "^5.74.4",
36
  "@vercel/analytics": "^1.5.0",
37
- "ai": "^4.3.15",
38
  "class-variance-authority": "^0.7.1",
39
  "clsx": "^2.1.1",
40
- "drizzle-orm": "^0.42.0",
41
  "fast-deep-equal": "^3.1.3",
42
- "framer-motion": "^12.7.4",
43
  "lucide-react": "^0.488.0",
44
- "motion": "^12.7.3",
45
  "nanoid": "^5.1.5",
46
- "next": "^15.3.1",
47
  "next-themes": "^0.4.6",
48
  "or": "^0.2.0",
49
- "pg": "^8.14.1",
50
  "react": "^19.1.0",
51
  "react-dom": "^19.1.0",
52
  "react-markdown": "^10.1.0",
 
53
  "remark-gfm": "^4.0.1",
54
- "sonner": "^2.0.3",
55
  "tailwind-merge": "^3.2.0",
56
  "tailwindcss-animate": "^1.0.7",
 
57
  "zod": "^3.24.2"
58
  },
59
  "devDependencies": {
60
  "@eslint/eslintrc": "^3.3.1",
61
- "@tailwindcss/postcss": "^4.1.4",
62
  "@types/node": "^22.14.1",
63
  "@types/pg": "^8.11.13",
64
  "@types/react": "^19.1.2",
65
  "@types/react-dom": "^19.1.2",
 
66
  "dotenv": "^16.5.0",
67
- "drizzle-kit": "^0.31.0",
68
  "esbuild": ">=0.25.0",
69
  "eslint": "^9.24.0",
70
  "eslint-config-next": "15.3.0",
71
  "pg-pool": "^3.8.0",
72
- "tailwindcss": "^4.1.4",
73
  "typescript": "^5.8.3"
 
 
 
 
 
74
  }
75
  }
 
19
  "@ai-sdk/openai": "^1.3.22",
20
  "@ai-sdk/react": "^1.2.12",
21
  "@ai-sdk/xai": "^1.2.16",
22
+ "@aws-sdk/client-s3": "^3.821.0",
23
+ "@daytonaio/sdk": "^0.19.0",
24
  "@neondatabase/serverless": "^1.0.0",
25
  "@radix-ui/react-accordion": "^1.2.7",
26
  "@radix-ui/react-avatar": "^1.1.6",
 
35
  "@radix-ui/react-tooltip": "^1.2.3",
36
  "@tanstack/react-query": "^5.74.4",
37
  "@vercel/analytics": "^1.5.0",
38
+ "ai": "^4.3.16",
39
  "class-variance-authority": "^0.7.1",
40
  "clsx": "^2.1.1",
41
+ "drizzle-orm": "^0.44.1",
42
  "fast-deep-equal": "^3.1.3",
43
+ "framer-motion": "^12.16.0",
44
  "lucide-react": "^0.488.0",
45
+ "motion": "^12.16.0",
46
  "nanoid": "^5.1.5",
47
+ "next": "^15.3.2",
48
  "next-themes": "^0.4.6",
49
  "or": "^0.2.0",
50
+ "pg": "^8.16.0",
51
  "react": "^19.1.0",
52
  "react-dom": "^19.1.0",
53
  "react-markdown": "^10.1.0",
54
+ "react-syntax-highlighter": "^15.6.1",
55
  "remark-gfm": "^4.0.1",
56
+ "sonner": "^2.0.5",
57
  "tailwind-merge": "^3.2.0",
58
  "tailwindcss-animate": "^1.0.7",
59
+ "tw-animate-css": "^1.3.3",
60
  "zod": "^3.24.2"
61
  },
62
  "devDependencies": {
63
  "@eslint/eslintrc": "^3.3.1",
64
+ "@tailwindcss/postcss": "^4.1.8",
65
  "@types/node": "^22.14.1",
66
  "@types/pg": "^8.11.13",
67
  "@types/react": "^19.1.2",
68
  "@types/react-dom": "^19.1.2",
69
+ "@types/react-syntax-highlighter": "^15.5.13",
70
  "dotenv": "^16.5.0",
71
+ "drizzle-kit": "^0.31.1",
72
  "esbuild": ">=0.25.0",
73
  "eslint": "^9.24.0",
74
  "eslint-config-next": "15.3.0",
75
  "pg-pool": "^3.8.0",
76
+ "tailwindcss": "^4.1.8",
77
  "typescript": "^5.8.3"
78
+ },
79
+ "pnpm": {
80
+ "onlyBuiltDependencies": [
81
+ "@tailwindcss/oxide"
82
+ ]
83
  }
84
  }
pnpm-lock.yaml CHANGED
The diff for this file is too large to render. See raw diff