bipinkrish commited on
Commit
67d6774
·
1 Parent(s): 92dea51

fix: website kinks

Browse files
package.json CHANGED
@@ -3,7 +3,7 @@
3
  "version": "0.1.0",
4
  "private": true,
5
  "scripts": {
6
- "dev": "next dev",
7
  "build": "next build",
8
  "start": "next start",
9
  "lint": "next lint"
 
3
  "version": "0.1.0",
4
  "private": true,
5
  "scripts": {
6
+ "dev": "mkdir -p src/components/generated && next dev",
7
  "build": "next build",
8
  "start": "next start",
9
  "lint": "next lint"
src/app/globals.css CHANGED
@@ -124,3 +124,41 @@ body {
124
  display: flex;
125
  flex-direction: column;
126
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  display: flex;
125
  flex-direction: column;
126
  }
127
+
128
+ .scrollable {
129
+ max-height: calc(100vh - 4rem);
130
+ }
131
+
132
+ .mt-8 {
133
+ margin-top: 2rem;
134
+ }
135
+
136
+ .chat-scroll > div > div {
137
+ display: flex !important;
138
+ flex-direction: column;
139
+ gap: 1rem;
140
+ }
141
+
142
+ .send-button {
143
+ top: 0.5rem;
144
+ }
145
+
146
+ .prompt-box {
147
+ min-height: 4rem;
148
+ }
149
+
150
+ .you-message {
151
+ margin-left: auto;
152
+ > div {
153
+ flex-flow: row-reverse;
154
+ }
155
+ }
156
+
157
+ .invert {
158
+ filter: invert(1);
159
+ }
160
+
161
+ .selected-card {
162
+ background-color: hsl(var(--primary));
163
+ color: hsl(var(--primary-foreground));
164
+ }
src/app/layout.tsx CHANGED
@@ -18,7 +18,7 @@ export default function RootLayout({
18
  }>) {
19
  return (
20
  <html lang="en" suppressHydrationWarning>
21
- <body className="antialiased">
22
  <SessionProvider>
23
  <SidebarProvider>
24
  <ThemeProvider>
 
18
  }>) {
19
  return (
20
  <html lang="en" suppressHydrationWarning>
21
+ <body className="antialiased" style={{overflow: "hidden"}}>
22
  <SessionProvider>
23
  <SidebarProvider>
24
  <ThemeProvider>
src/webzero/chat-page.tsx CHANGED
@@ -33,21 +33,19 @@ import { SidebarTrigger } from "@/components/ui/sidebar";
33
  import { useSessionContext } from "@/context/SessionContext";
34
 
35
  function CodeMessageCard({
36
- message,
37
  onClick,
38
  }: {
39
- message: Message;
40
  onClick: () => void;
41
  }) {
42
  return (
43
  <div
44
  onClick={onClick}
45
- className="bg-muted/50 p-3 rounded-lg mb-2 cursor-pointer flex items-center gap-2"
46
  >
 
47
  <FileCode2 />
48
- <div className="ml-2">
49
- <div className="font-semibold text-sm">Code File</div>
50
- </div>
51
  </div>
52
  );
53
  }
@@ -116,15 +114,8 @@ export function ChatPage() {
116
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
117
  };
118
 
119
- const {
120
- sessions,
121
- currentSessionId,
122
- createNewSession,
123
- switchSession,
124
- updateSessionMessages,
125
- deleteSession,
126
- renameSession,
127
- } = useSessionContext();
128
 
129
  const currentSession = sessions.find(
130
  (session) => session.id === currentSessionId
@@ -164,7 +155,7 @@ export function ChatPage() {
164
  } else {
165
  setCurrentIteration(null);
166
  }
167
- }, [currentSessionId, currentSession]);
168
 
169
  useEffect(() => {
170
  scrollToBottom();
@@ -205,8 +196,8 @@ export function ChatPage() {
205
  previousDescription: message,
206
  currentCode: response.content,
207
  });
208
- setPreviewState((prev) => ({ ...prev, isOpen: true }));
209
  setSelectedAIMessage(response);
 
210
  } catch (error) {
211
  console.error("Error:", error);
212
  } finally {
@@ -251,15 +242,15 @@ export function ChatPage() {
251
  <header className="h-16 border-b flex items-center justify-between px-4 bg-background z-10">
252
  <div className="flex items-center">
253
  <SidebarTrigger />
254
- <h1 className="text-2xl font-bold ml-4">Web Zero</h1>
255
  </div>
256
- <ThemeToggle />
257
  </header>
258
  <div className="flex-1 flex overflow-hidden">
259
  {!currentSession || currentSession.messages.length === 0 ? (
260
- <div className="flex-1 flex flex-col items-center justify-center p-4">
261
- <h2 className="text-4xl font-bold mb-6 text-center">
262
- Welcome to Web Zero
263
  </h2>
264
  <div className="w-full max-w-md">
265
  <form onSubmit={handleSubmit} className="relative">
@@ -268,12 +259,12 @@ export function ChatPage() {
268
  onChange={(e) => setMessage(e.target.value)}
269
  onKeyDown={handleKeyDown}
270
  placeholder="Ask webzero a question..."
271
- className="min-h-[100px] pr-16 resize-none"
272
  />
273
  <Button
274
  type="submit"
275
  size="icon"
276
- className="absolute bottom-3 right-3"
277
  disabled={isLoading || message.trim() === ""}
278
  >
279
  <Send className="h-4 w-4" />
@@ -286,25 +277,33 @@ export function ChatPage() {
286
  <ResizablePanel
287
  defaultSize={50}
288
  minSize={30}
289
- className={`flex flex-col h-full `}
290
  >
291
  <div className="flex flex-col h-full">
292
- <ScrollArea className="flex-1 p-4">
293
- {currentSession.messages.map((msg) => (
294
- <div key={msg.id} className="mb-4 flex">
295
- <div className="flex items-start gap-3 max-w-[80%]">
296
- <Avatar>
297
- <AvatarImage />
298
- <AvatarFallback>
299
- {msg.from === "user" ? "U" : "AI"}
300
- </AvatarFallback>
301
- </Avatar>
302
- <div className="p-3 rounded-lg bg-muted">
 
 
 
 
 
 
 
 
303
  {msg.from === "user" ? (
304
  <p>{msg.content}</p>
305
  ) : (
306
  <CodeMessageCard
307
- message={msg}
308
  onClick={() => {
309
  setSelectedAIMessage(msg);
310
  setPreviewState((prev) => ({
@@ -335,12 +334,12 @@ export function ChatPage() {
335
  ? "Describe your iteration..."
336
  : "Ask webzero a question..."
337
  }
338
- className="min-h-[100px] pr-16 resize-none"
339
  />
340
  <Button
341
  type="submit"
342
  size="icon"
343
- className="absolute bottom-3 right-3"
344
  disabled={isLoading || message.trim() === ""}
345
  >
346
  <Send className="h-4 w-4" />
@@ -379,9 +378,9 @@ export function ChatPage() {
379
  <Code className="h-4 w-4" /> Code
380
  </TabsTrigger>
381
  </TabsList>
382
- <div className="flex items-center gap-1">
383
  <Button
384
- variant="outline"
385
  size="sm"
386
  onClick={() =>
387
  handleCopyCode(selectedAIMessage?.content || "")
@@ -390,7 +389,7 @@ export function ChatPage() {
390
  <Copy className="h-4 w-4 mr-2" /> Copy
391
  </Button>
392
  <Button
393
- variant="outline"
394
  size="sm"
395
  onClick={() =>
396
  handleDownloadCode(
@@ -412,26 +411,29 @@ export function ChatPage() {
412
  <Maximize2 className="h-4 w-4" />
413
  )}
414
  </Button>
415
- <Button
416
- variant="ghost"
417
- size="icon"
418
- onClick={() => {
419
- setPreviewState((prev) => ({
420
- ...prev,
421
- isOpen: false,
422
- }));
423
- setSelectedAIMessage(null);
424
- }}
425
- >
426
- <X className="h-4 w-4" />
427
- </Button>
 
 
 
428
  </div>
429
  </div>
430
  <TabsContent
431
  value="preview"
432
  className="flex-1 overflow-auto"
433
  >
434
- <ScrollArea className="h-full">
435
  <div className="p-4">
436
  <DynamicFileRenderer
437
  id={selectedAIMessage?.id || ""}
@@ -443,7 +445,7 @@ export function ChatPage() {
443
  value="code"
444
  className="flex-1 overflow-auto"
445
  >
446
- <ScrollArea className="h-full">
447
  <div className="p-4">
448
  <SyntaxHighlighter
449
  language="typescript"
 
33
  import { useSessionContext } from "@/context/SessionContext";
34
 
35
  function CodeMessageCard({
36
+ version,
37
  onClick,
38
  }: {
39
+ version: number;
40
  onClick: () => void;
41
  }) {
42
  return (
43
  <div
44
  onClick={onClick}
45
+ className="bg-muted/50 rounded-lg mb-2 cursor-pointer flex items-center gap-1"
46
  >
47
+ <div className="mr-2 text-sm">Code v{version}</div>
48
  <FileCode2 />
 
 
 
49
  </div>
50
  );
51
  }
 
114
  messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
115
  };
116
 
117
+ const { sessions, currentSessionId, updateSessionMessages } =
118
+ useSessionContext();
 
 
 
 
 
 
 
119
 
120
  const currentSession = sessions.find(
121
  (session) => session.id === currentSessionId
 
155
  } else {
156
  setCurrentIteration(null);
157
  }
158
+ }, [currentSessionId]);
159
 
160
  useEffect(() => {
161
  scrollToBottom();
 
196
  previousDescription: message,
197
  currentCode: response.content,
198
  });
 
199
  setSelectedAIMessage(response);
200
+ setPreviewState((prev) => ({ ...prev, isOpen: true }));
201
  } catch (error) {
202
  console.error("Error:", error);
203
  } finally {
 
242
  <header className="h-16 border-b flex items-center justify-between px-4 bg-background z-10">
243
  <div className="flex items-center">
244
  <SidebarTrigger />
245
+ <ThemeToggle />
246
  </div>
247
+ <h1 className="text-2xl font-bold ml-4">WebZero</h1>
248
  </header>
249
  <div className="flex-1 flex overflow-hidden">
250
  {!currentSession || currentSession.messages.length === 0 ? (
251
+ <div className="flex-1 flex flex-col items-center justify-center p-4 gap-2">
252
+ <h2 className="text-4xl font-bold text-center">
253
+ Welcome to WebZero
254
  </h2>
255
  <div className="w-full max-w-md">
256
  <form onSubmit={handleSubmit} className="relative">
 
259
  onChange={(e) => setMessage(e.target.value)}
260
  onKeyDown={handleKeyDown}
261
  placeholder="Ask webzero a question..."
262
+ className="min-h-[100px] pr-16 resize-none prompt-box"
263
  />
264
  <Button
265
  type="submit"
266
  size="icon"
267
+ className="absolute send-button right-3"
268
  disabled={isLoading || message.trim() === ""}
269
  >
270
  <Send className="h-4 w-4" />
 
277
  <ResizablePanel
278
  defaultSize={50}
279
  minSize={30}
280
+ className={`flex flex-col h-full scrollable`}
281
  >
282
  <div className="flex flex-col h-full">
283
+ <ScrollArea className="flex-1 px-4 mt-4 chat-scroll">
284
+ {currentSession.messages.map((msg, index) => (
285
+ <div
286
+ key={msg.id}
287
+ className={`mb-4 flex ${
288
+ msg.from === "user" ? "you-message" : ""
289
+ }`}
290
+ >
291
+ <div className="flex items-start max-w-[80%] gap-1">
292
+ {msg.from === "ai" && (
293
+ <Avatar>
294
+ <AvatarImage
295
+ className={isDark ? "invert" : ""}
296
+ src="https://raw.githubusercontent.com/webzero-org/website/refs/heads/main/src/app/favicon.svg"
297
+ />
298
+ <AvatarFallback>"AI"</AvatarFallback>
299
+ </Avatar>
300
+ )}
301
+ <div className={`p-3 rounded-lg bg-muted ${(selectedAIMessage?.id == msg.id) ? "selected-card" : ""}`}>
302
  {msg.from === "user" ? (
303
  <p>{msg.content}</p>
304
  ) : (
305
  <CodeMessageCard
306
+ version={(1 + index) / 2}
307
  onClick={() => {
308
  setSelectedAIMessage(msg);
309
  setPreviewState((prev) => ({
 
334
  ? "Describe your iteration..."
335
  : "Ask webzero a question..."
336
  }
337
+ className="min-h-[100px] pr-16 resize-none prompt-box"
338
  />
339
  <Button
340
  type="submit"
341
  size="icon"
342
+ className="absolute send-button right-3"
343
  disabled={isLoading || message.trim() === ""}
344
  >
345
  <Send className="h-4 w-4" />
 
378
  <Code className="h-4 w-4" /> Code
379
  </TabsTrigger>
380
  </TabsList>
381
+ <div className="flex items-center gap-1 p-2">
382
  <Button
383
+ variant="ghost"
384
  size="sm"
385
  onClick={() =>
386
  handleCopyCode(selectedAIMessage?.content || "")
 
389
  <Copy className="h-4 w-4 mr-2" /> Copy
390
  </Button>
391
  <Button
392
+ variant="ghost"
393
  size="sm"
394
  onClick={() =>
395
  handleDownloadCode(
 
411
  <Maximize2 className="h-4 w-4" />
412
  )}
413
  </Button>
414
+ {previewState.isFullscreen && <ThemeToggle />}
415
+ {!previewState.isFullscreen && (
416
+ <Button
417
+ variant="ghost"
418
+ size="icon"
419
+ onClick={() => {
420
+ setPreviewState((prev) => ({
421
+ ...prev,
422
+ isOpen: false,
423
+ }));
424
+ setSelectedAIMessage(null);
425
+ }}
426
+ >
427
+ <X className="h-4 w-4" />
428
+ </Button>
429
+ )}
430
  </div>
431
  </div>
432
  <TabsContent
433
  value="preview"
434
  className="flex-1 overflow-auto"
435
  >
436
+ <ScrollArea className="h-full scrollable">
437
  <div className="p-4">
438
  <DynamicFileRenderer
439
  id={selectedAIMessage?.id || ""}
 
445
  value="code"
446
  className="flex-1 overflow-auto"
447
  >
448
+ <ScrollArea className="h-full scrollable">
449
  <div className="p-4">
450
  <SyntaxHighlighter
451
  language="typescript"
src/webzero/preview.tsx CHANGED
@@ -1,22 +1,35 @@
1
- import React, { Suspense } from "react";
2
- import dynamic from "next/dynamic";
3
 
4
  interface DynamicFileProps {
5
  id: string;
6
  }
7
 
8
  const DynamicFileRenderer: React.FC<DynamicFileProps> = ({ id }) => {
9
- const DynamicComponent = dynamic(
10
- () =>
11
- import(`@/components/generated/${id}`).catch(() => {
12
- console.error(`Failed to load component with id: ${id}`);
13
- return () => <div>Error loading component</div>;
14
- }),
15
- {
16
- loading: () => <div>Loading...</div>,
17
- ssr: false,
18
- }
19
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  return (
22
  <Suspense fallback={<div>Loading...</div>}>
 
1
+ import React, { Suspense, useState, useEffect } from "react";
 
2
 
3
  interface DynamicFileProps {
4
  id: string;
5
  }
6
 
7
  const DynamicFileRenderer: React.FC<DynamicFileProps> = ({ id }) => {
8
+ const [DynamicComponent, setDynamicComponent] =
9
+ useState<React.ComponentType | null>(null);
10
+
11
+ useEffect(() => {
12
+ let failed = false;
13
+ const loadComponent = async (canSetError: boolean) => {
14
+ try {
15
+ const Component = await import(`@/components/generated/${id}`);
16
+ setDynamicComponent(() => Component.default || Component);
17
+ } catch (error) {
18
+ failed = true;
19
+ canSetError &&
20
+ setDynamicComponent(() => () => <div>Error loading component</div>);
21
+ }
22
+ };
23
+ loadComponent(false);
24
+
25
+ setTimeout(() => {
26
+ failed && loadComponent(true);
27
+ }, 2000);
28
+ }, [id]);
29
+
30
+ if (!DynamicComponent) {
31
+ return <div>Loading...</div>;
32
+ }
33
 
34
  return (
35
  <Suspense fallback={<div>Loading...</div>}>