From 41eca2305ca35095a2ad91eb6ad91abe2c9ca714 Mon Sep 17 00:00:00 2001 From: ngc2207 Date: Tue, 7 Jan 2025 18:53:17 +0800 Subject: [PATCH] feat(playground): add theme provider and toggle, implement chat input and loading components --- package.json | 5 + public/ai.svg | 6 + public/default.svg | 13 +- src/app/api/chat/route.ts | 20 ++ src/app/layout.tsx | 12 +- src/app/playground/components/banner.tsx | 20 ++ src/app/playground/components/button/run.tsx | 21 ++ src/app/playground/components/chat.tsx | 57 +++++ src/app/playground/components/nav.tsx | 28 +++ src/app/playground/components/terminal.tsx | 68 ++++++ src/app/playground/components/tools.tsx | 23 ++ src/app/playground/components/user.tsx | 35 +++ src/app/playground/layout.tsx | 41 ++++ src/components/theme-provider.tsx | 11 + src/components/theme-toggle.tsx | 36 ++++ src/components/ui/breadcrumb.tsx | 115 ++++++++++ src/components/ui/chat/chat-bubble.tsx | 202 ++++++++++++++++++ src/components/ui/chat/chat-input.tsx | 23 ++ src/components/ui/chat/chat-message-list.tsx | 55 +++++ src/components/ui/chat/expandable-chat.tsx | 153 +++++++++++++ .../ui/chat/hooks/useAutoScroll.tsx | 135 ++++++++++++ src/components/ui/chat/message-loading.tsx | 45 ++++ src/components/ui/resizable.tsx | 45 ++++ src/components/ui/tabs.tsx | 36 ++-- src/components/ui/textarea.tsx | 22 ++ src/components/ui/toggle.tsx | 45 ++++ 26 files changed, 1245 insertions(+), 27 deletions(-) create mode 100644 public/ai.svg create mode 100644 src/app/api/chat/route.ts create mode 100644 src/app/playground/components/banner.tsx create mode 100644 src/app/playground/components/button/run.tsx create mode 100644 src/app/playground/components/chat.tsx create mode 100644 src/app/playground/components/nav.tsx create mode 100644 src/app/playground/components/terminal.tsx create mode 100644 src/app/playground/components/tools.tsx create mode 100644 src/app/playground/components/user.tsx create mode 100644 src/app/playground/layout.tsx create mode 100644 src/components/theme-provider.tsx create mode 100644 src/components/theme-toggle.tsx create mode 100644 src/components/ui/breadcrumb.tsx create mode 100644 src/components/ui/chat/chat-bubble.tsx create mode 100644 src/components/ui/chat/chat-input.tsx create mode 100644 src/components/ui/chat/chat-message-list.tsx create mode 100644 src/components/ui/chat/expandable-chat.tsx create mode 100644 src/components/ui/chat/hooks/useAutoScroll.tsx create mode 100644 src/components/ui/chat/message-loading.tsx create mode 100644 src/components/ui/resizable.tsx create mode 100644 src/components/ui/textarea.tsx create mode 100644 src/components/ui/toggle.tsx diff --git a/package.json b/package.json index 630d0b1..5d5ccd9 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "next lint" }, "dependencies": { + "@ai-sdk/openai": "^1.0.13", "@auth/prisma-adapter": "^2.7.4", "@monaco-editor/react": "^4.6.0", "@prisma/client": "^6.1.0", @@ -19,6 +20,8 @@ "@radix-ui/react-slider": "^1.2.2", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-tabs": "^1.1.2", + "@radix-ui/react-toggle": "^1.1.1", + "ai": "^4.0.27", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "devicons-react": "^1.4.0", @@ -29,9 +32,11 @@ "monaco-languageclient": "5.0.1", "next": "15.1.3", "next-auth": "^5.0.0-beta.25", + "next-themes": "^0.4.4", "normalize-url": "~8.0.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-resizable-panels": "^2.1.7", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", "vscode-languageclient": "~8.1.0", diff --git a/public/ai.svg b/public/ai.svg new file mode 100644 index 0000000..ab945b5 --- /dev/null +++ b/public/ai.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/public/default.svg b/public/default.svg index 3b77e42..c8cd993 100644 --- a/public/default.svg +++ b/public/default.svg @@ -1,9 +1,6 @@ - - - - - - - + + + + \ No newline at end of file diff --git a/src/app/api/chat/route.ts b/src/app/api/chat/route.ts new file mode 100644 index 0000000..60ccbb3 --- /dev/null +++ b/src/app/api/chat/route.ts @@ -0,0 +1,20 @@ +import { streamText } from "ai"; +import { createOpenAI } from "@ai-sdk/openai"; + +export const maxDuration = 30; + +const openai = createOpenAI({ + apiKey: process.env.OPENAI_API_KEY || "", + baseURL: process.env.OPENAI_BASE_URL || "", + }); + +export async function POST(req: Request) { + const { messages } = await req.json(); + + const result = streamText({ + model: openai("deepseek-chat"), + messages, + }); + + return result.toDataStreamResponse(); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 34eb3e7..a23ed21 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -2,6 +2,7 @@ import "@/app/globals.css"; import { cn } from "@/lib/utils"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; +import { ThemeProvider } from "@/components/theme-provider"; const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); @@ -21,7 +22,16 @@ export default function RootLayout({ -
{children}
+ +
+ {children} +
+
); diff --git a/src/app/playground/components/banner.tsx b/src/app/playground/components/banner.tsx new file mode 100644 index 0000000..29ff797 --- /dev/null +++ b/src/app/playground/components/banner.tsx @@ -0,0 +1,20 @@ +import { ArrowRight } from "lucide-react"; + +export default function Banner() { + return ( +
+

+ + + Introducing to Judge4c + +

+
+ ); +} diff --git a/src/app/playground/components/button/run.tsx b/src/app/playground/components/button/run.tsx new file mode 100644 index 0000000..d6690a6 --- /dev/null +++ b/src/app/playground/components/button/run.tsx @@ -0,0 +1,21 @@ +import { Play } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +export default function Run() { + return ( +
+ +
+ ); +} diff --git a/src/app/playground/components/chat.tsx b/src/app/playground/components/chat.tsx new file mode 100644 index 0000000..d8d7830 --- /dev/null +++ b/src/app/playground/components/chat.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { useChat } from "ai/react"; +import { Send } from "lucide-react"; +import { + ChatBubble, + ChatBubbleAvatar, + ChatBubbleMessage, +} from "@/components/ui/chat/chat-bubble"; +import { + ExpandableChatHeader, + ExpandableChatBody, + ExpandableChatFooter, +} from "@/components/ui/chat/expandable-chat"; +import { Button } from "@/components/ui/button"; +import { ChatInput } from "@/components/ui/chat/chat-input"; +import { ChatMessageList } from "@/components/ui/chat/chat-message-list"; + +export default function Chat() { + const { messages, input, handleInputChange, handleSubmit } = useChat(); + return ( +
+ +

Chat with AI Assistant ✨

+
+ + + {messages.map((message, index) => ( + + + + {message.content} + + + ))} + + + +
+
+ + +
+
+
+
+ ); +} diff --git a/src/app/playground/components/nav.tsx b/src/app/playground/components/nav.tsx new file mode 100644 index 0000000..ac730b5 --- /dev/null +++ b/src/app/playground/components/nav.tsx @@ -0,0 +1,28 @@ +import { + Breadcrumb, + BreadcrumbItem, + BreadcrumbLink, + BreadcrumbList, + BreadcrumbPage, + BreadcrumbSeparator, +} from "@/components/ui/breadcrumb"; +import { Home } from "lucide-react"; + +export default function Nav() { + return ( + + + + + + + + + Playground + + + + ); +} diff --git a/src/app/playground/components/terminal.tsx b/src/app/playground/components/terminal.tsx new file mode 100644 index 0000000..4a247f2 --- /dev/null +++ b/src/app/playground/components/terminal.tsx @@ -0,0 +1,68 @@ +import { Box, House, PanelsTopLeft } from "lucide-react"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +export default function Terminal() { + return ( + + + + + + + + + +
+ +

+ Content for Tab 1 +

+
+ +

+ Content for Tab 2 +

+
+ +

+ Content for Tab 3 +

+
+
+
+ ); +} diff --git a/src/app/playground/components/tools.tsx b/src/app/playground/components/tools.tsx new file mode 100644 index 0000000..a5fa5cd --- /dev/null +++ b/src/app/playground/components/tools.tsx @@ -0,0 +1,23 @@ +import Run from "./button/run"; +import Nav from "./nav"; +import User from "./user"; + +export default function Tools() { + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/app/playground/components/user.tsx b/src/app/playground/components/user.tsx new file mode 100644 index 0000000..de6a7be --- /dev/null +++ b/src/app/playground/components/user.tsx @@ -0,0 +1,35 @@ +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; + +export default function User() { + return ( +
+ + + KK + + + Verified + + +
+ ); +} diff --git a/src/app/playground/layout.tsx b/src/app/playground/layout.tsx new file mode 100644 index 0000000..2cedf70 --- /dev/null +++ b/src/app/playground/layout.tsx @@ -0,0 +1,41 @@ +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from "@/components/ui/resizable"; +import Tools from "./components/tools"; +import Banner from "./components/banner"; +import Terminal from "./components/terminal"; +import Chat from "./components/chat"; + +export default function PlaygroundLayout() { + return ( +
+ + +
+ + One + + + + + + One + + + + + + + + + + + + + +
+
+ ); +} diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx new file mode 100644 index 0000000..189a2b1 --- /dev/null +++ b/src/components/theme-provider.tsx @@ -0,0 +1,11 @@ +"use client"; + +import * as React from "react"; +import { ThemeProvider as NextThemesProvider } from "next-themes"; + +export function ThemeProvider({ + children, + ...props +}: React.ComponentProps) { + return {children}; +} diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx new file mode 100644 index 0000000..0b7b34e --- /dev/null +++ b/src/components/theme-toggle.tsx @@ -0,0 +1,36 @@ +"use client"; + +import { useTheme } from "next-themes"; +import { Moon, Sun } from "lucide-react"; +import { Toggle } from "@/components/ui/toggle"; + +export default function ButtonDemo() { + const { theme, setTheme } = useTheme(); + + return ( +
+ + setTheme((prev) => (prev === "dark" ? "light" : "dark")) + } + aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`} + > + +
+ ); +} diff --git a/src/components/ui/breadcrumb.tsx b/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000..60e6c96 --- /dev/null +++ b/src/components/ui/breadcrumb.tsx @@ -0,0 +1,115 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { ChevronRight, MoreHorizontal } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<"nav"> & { + separator?: React.ReactNode + } +>(({ ...props }, ref) =>