diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..26909c8 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [SkidGod4444] \ No newline at end of file diff --git a/app/(routes)/chat/[slug]/page.tsx b/app/(routes)/chat/[slug]/page.tsx index 23369a1..bad3184 100644 --- a/app/(routes)/chat/[slug]/page.tsx +++ b/app/(routes)/chat/[slug]/page.tsx @@ -1,44 +1,59 @@ "use client"; -import { useState } from 'react'; -import { ChevronLeft, ChevronRight, PaperclipIcon, ArrowUpIcon } from 'lucide-react'; -import { ScrollArea } from "@/components/ui/scroll-area"; -import { Button } from "@/components/ui/button"; -import { CustomTextArea } from "@/components/custom/text.area"; +import { useEffect, useState } from "react"; import { SideBar } from "@/components/custom/sidebar/sidebar"; import useLocalStorage from "@/lib/use.local"; -import { LibSelector } from '@/components/custom/lib.selector'; +import { Message } from "ai/react"; +import { ChatComp } from "@/components/custom/chat/comp"; +import LoadingScreen from "@/components/custom/loading.screen"; -export default function Component() { - const [isCodeCollapsed, setIsCodeCollapsed] = useState(false); - const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage("acter-isCollapsed", false); - - const toggleCodePanel = () => { - setIsCodeCollapsed(!isCodeCollapsed); +interface PageProps { + params: { + slug: string; }; +} - const messages = [ - { id: 1, text: "Hello! How can I help you today?" }, - { id: 2, text: "Can you explain how to use React hooks?" }, - { id: 3, text: "React hooks are functions that let you use state and other React features in functional components..." }, - ]; +export default function ChatPage({ params }: PageProps) { + const [loading, setLoading] = useState(true); + const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage( + "acter-isCollapsed", + false, + ); + const [history, setHistory] = useState([]); - const codeExample = ` -import React, { useState } from 'react'; + const chatID = params.slug.replace(/^\//, ""); -function Counter() { - const [count, setCount] = useState(0); + useEffect(() => { + async function handleSlug() { + setLoading(true); + try { + const response = await fetch("/api/ai/history", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ chatID }), + }); - return ( -
-

You clicked {count} times

- -
- ); -} - `.trim(); + if (response.ok) { + const chatHistory = await response.json(); + setHistory(chatHistory); + } else { + console.error("Failed to fetch chat history"); + } + } catch (error) { + console.error("Error fetching chat history:", error); + } finally { + setLoading(false); + } + } + + handleSlug(); + }, [params.slug, chatID]); + + if (loading) { + return ; + } return (
@@ -49,64 +64,9 @@ function Counter() { /> {/* Left Panel */} -
- - - {messages.map((message) => ( -
- {message.text} -
- ))} -
- +
{/* Input Area */} -
- -
-
- - -
- -
- -
- - {/* Right Panel - Code Viewer */} -
- {!isCodeCollapsed && ( -
-

Code

- -
-                {codeExample}
-              
-
-
- )} +
); diff --git a/app/(routes)/chat/page.tsx b/app/(routes)/chat/page.tsx index 66f20cd..5b8a36a 100644 --- a/app/(routes)/chat/page.tsx +++ b/app/(routes)/chat/page.tsx @@ -1,43 +1,48 @@ "use client"; import { Badge } from "@/components/ui/badge"; -import { MoveUpRight } from "lucide-react"; import useLocalStorage from "@/lib/use.local"; import { useUser } from "@/context/user.context"; -import { OfcLinks } from "@/db/defaults"; import { SideBar } from "@/components/custom/sidebar/sidebar"; -import { useEffect, useState } from "react"; +import { useEffect, useState, useRef } from "react"; import { ChatComp } from "@/components/custom/chat/comp"; +import LoadingScreen from "@/components/custom/loading.screen"; +import { useRouter } from "next/navigation"; export default function ChatPage() { const { user } = useUser(); - const [chatID, setChatID] = useState("null"); + const router = useRouter(); + const [chatID, setChatID] = useState(null); const [isMsg, setMsg] = useState(false); + const chatInitialized = useRef(false); + const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage( + "acter-isCollapsed", + false, + ); + + // Generate chat ID once the user is available useEffect(() => { - const fetchChatID = async () => { + if (user && !chatInitialized.current) { const id = Math.floor(Math.random() * 900000) + 100000; setChatID(id.toString()); - }; - - if (user) { - fetchChatID(); + chatInitialized.current = true; } - }, [user, chatID]); + }, [user]); + // Navigate to chat page when ready useEffect(() => { if (chatID && isMsg) { - window.location.href = `/chat/${chatID}`; + window.history.replaceState(null, "", `/chat/${chatID}`); } - }, [chatID, isMsg]); + }, [chatID, isMsg, router]); - const [isSidebarExpanded, setIsSidebarExpanded] = useLocalStorage( - "acter-isCollapsed", - false - ); + if (!chatID) { + return ; + } return ( -
+
{/* Background */}
@@ -45,31 +50,24 @@ export default function ChatPage() {
{/* Sidebar */} setIsSidebarExpanded(expanded)} - /> + isSidebarExpanded={isSidebarExpanded} + setIsSidebarExpanded={(expanded) => setIsSidebarExpanded(expanded)} + /> {/* Main Content */}
- {/* Header at the top */} + {/* Header */}
Public Beta
-

- Need awesome components to ship? -

-

- Am the one who supports Acternity UI, Magic UI and more libraries. Ask questions, modify component. -

- {/* Input area */} -
+
-
+ {/*
Generate a SaaS pricing calculator @@ -82,11 +80,11 @@ export default function ChatPage() { Write code to implement a min heap -
+
*/}
- {/* Footer at the bottom */} - */}
diff --git a/app/(routes)/page.tsx b/app/(routes)/page.tsx index 66c5f08..7ad6a8b 100644 --- a/app/(routes)/page.tsx +++ b/app/(routes)/page.tsx @@ -2,11 +2,7 @@ import { useState } from "react"; import { Button } from "@/components/ui/button"; -import { - PaperclipIcon, - ArrowUpIcon, - MoveUpRight, -} from "lucide-react"; +import { PaperclipIcon, ArrowUpIcon, MoveUpRight } from "lucide-react"; import { CustomTextArea } from "@/components/custom/text.area"; import { Badge } from "@/components/ui/badge"; import { OsBtn } from "@/components/custom/os.button"; @@ -18,14 +14,13 @@ import { LibSelector } from "@/components/custom/lib.selector"; export default function HomePage() { const [isOpen, setIsOpen] = useState(false); - const handleFormSubmit = async(event: React.FormEvent) => { + const handleFormSubmit = async (event: React.FormEvent) => { event.preventDefault(); setIsOpen(!isOpen); - } + }; return (
- {/* Background */}
@@ -36,7 +31,7 @@ export default function HomePage() { {/* Header at the top */}
{/* Public Beta */} - +
@@ -47,23 +42,27 @@ export default function HomePage() { Need awesome components to ship?

- Am the one who supports Acternity UI, Magic UI and more libraries. Ask questions, modify component. + Am the one who supports Acternity UI, Magic UI and more libraries. + Ask questions, modify component.

{/* Input area */}
-
+
-
- - -
+
+ + +
diff --git a/app/api/ai/history/route.ts b/app/api/ai/history/route.ts new file mode 100644 index 0000000..70e8de4 --- /dev/null +++ b/app/api/ai/history/route.ts @@ -0,0 +1,17 @@ +import { ragChat } from "@/lib/rag"; +import { NextRequest, NextResponse } from "next/server"; + +export const POST = async (req: NextRequest) => { + try { + const { chatID } = await req.json(); + const chatHistory = await ragChat.history.getMessages({ + amount: 100, + sessionId: chatID, + }); + + return NextResponse.json(chatHistory); + } catch (error) { + console.error("Error fetching chat history:", error); + return new NextResponse("Failed to fetch chat history", { status: 500 }); + } +}; diff --git a/app/api/ai/stream/route.ts b/app/api/ai/stream/route.ts index 577e608..ddf0463 100644 --- a/app/api/ai/stream/route.ts +++ b/app/api/ai/stream/route.ts @@ -3,16 +3,18 @@ import { aiUseChatAdapter } from "@upstash/rag-chat/nextjs"; import { ragChat } from "@/lib/rag"; export const POST = async (req: NextRequest) => { - const { messages } = await req.json(); - + const { messages, namespace, chatId } = await req.json(); + const disableRAG = !namespace; const lastMsg = messages[messages.length - 1].content; const res = await ragChat.chat(lastMsg, { - namespace: "acter-db", + namespace: namespace, streaming: true, + sessionId: chatId, historyLength: 100, historyTTL: 604_800, similarityThreshold: 0.7, + disableRAG: disableRAG, }); return aiUseChatAdapter(res); -}; \ No newline at end of file +}; diff --git a/app/api/auth/user/route.ts b/app/api/auth/user/route.ts index 89e4548..f1fc78f 100644 --- a/app/api/auth/user/route.ts +++ b/app/api/auth/user/route.ts @@ -11,6 +11,9 @@ export async function GET() { } } catch (error) { console.error("Error in fetching session:", error); - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); + return NextResponse.json( + { error: "Internal Server Error" }, + { status: 500 }, + ); } } diff --git a/app/api/vectors/route.ts b/app/api/vectors/route.ts index 9768394..f5f904a 100644 --- a/app/api/vectors/route.ts +++ b/app/api/vectors/route.ts @@ -1,5 +1,5 @@ -import { ragChat } from '@/lib/rag'; -import { NextRequest, NextResponse } from 'next/server'; +import { ragChat } from "@/lib/rag"; +import { NextRequest, NextResponse } from "next/server"; export async function POST(req: NextRequest) { try { @@ -9,12 +9,20 @@ export async function POST(req: NextRequest) { type: "html", source: source, config: { chunkOverlap: 50, chunkSize: 200 }, - options: { namespace: namespace, metadata: { title: metadata.title, description: metadata.description } }, + options: { + namespace: namespace, + metadata: { title: metadata.title, description: metadata.description }, + }, }); - return NextResponse.json({ message: "Content added successfully to ragChat context!" }); + return NextResponse.json({ + message: "Content added successfully to ragChat context!", + }); } catch (error) { console.error("Error adding content to ragChat context:", error); - return NextResponse.json({ error: "Internal Server Error" }, { status: 500 }); + return NextResponse.json( + { error: "Internal Server Error" }, + { status: 500 }, + ); } } diff --git a/bun.lockb b/bun.lockb index 7b3beba..3fa119c 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/custom/auth/auth.btns.tsx b/components/custom/auth/auth.btns.tsx index c0352b9..763a347 100644 --- a/components/custom/auth/auth.btns.tsx +++ b/components/custom/auth/auth.btns.tsx @@ -1,23 +1,23 @@ -"use client" -import { signIn } from "next-auth/react" -import { Button } from '@/components/ui/button'; -import { IconBrandGithub, IconBrandGoogle } from '@tabler/icons-react'; -import React from 'react' +"use client"; +import { signIn } from "next-auth/react"; +import { Button } from "@/components/ui/button"; +import { IconBrandGithub, IconBrandGoogle } from "@tabler/icons-react"; +import React from "react"; function LoginWithGoogle() { return ( - ) + ); } function LoginWithGitHub() { - return ( - - ) - } + return ( + + ); +} -export {LoginWithGoogle, LoginWithGitHub}; \ No newline at end of file +export { LoginWithGoogle, LoginWithGitHub }; diff --git a/components/custom/auth/auth.dialog.tsx b/components/custom/auth/auth.dialog.tsx index cc85f7d..ba5e728 100644 --- a/components/custom/auth/auth.dialog.tsx +++ b/components/custom/auth/auth.dialog.tsx @@ -1,43 +1,67 @@ -import React from 'react' +import React from "react"; import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTrigger, - } from "@/components/ui/dialog" -import { Button } from '@/components/ui/button'; -import { LogIn } from 'lucide-react'; -import Image from 'next/image'; -import { LoginWithGitHub, LoginWithGoogle } from './auth.btns'; + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTrigger, +} from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { LogIn } from "lucide-react"; +import Image from "next/image"; +import { LoginWithGitHub, LoginWithGoogle } from "./auth.btns"; - interface AuthDialogProps { - open: boolean; - onOpenChange: (open: boolean) => void; - } +interface AuthDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; +} export default function AuthDialog({ open, onOpenChange }: AuthDialogProps) { return ( - -
- logo -
- - OR - -
+
+ logo +
+ + + OR + + +
- Continue to Acter with any of the given providers. By using Acter you agree to follow our Terms and Policy. + Continue to Acter with any of the given providers. By using Acter + you agree to follow our{" "} + + Terms + {" "} + and{" "} + + Policy + + .
- ) + ); } diff --git a/components/custom/chat/comp.tsx b/components/custom/chat/comp.tsx index eb59a0f..be1c173 100644 --- a/components/custom/chat/comp.tsx +++ b/components/custom/chat/comp.tsx @@ -1,83 +1,103 @@ "use client"; -import { Message, useChat } from 'ai/react'; -import { useEffect, useState } from 'react' +import { Message, useChat } from "ai/react"; +import { useCallback, useEffect, useState, useMemo } from "react"; import { CustomTextArea } from "@/components/custom/text.area"; import { PaperclipIcon, ArrowUpIcon } from "lucide-react"; -import { Button } from '@/components/ui/button'; -import { LibSelector } from '../lib.selector'; +import { Button } from "@/components/ui/button"; +import { LibSelector } from "../lib.selector"; +import MsgsWrapper from "../msgs/msgs.wrapper"; export const ChatComp = ({ - chatId, - history, - onHasMessagesChange, - msgs, - }: { - chatId: number | string; - history?: Message[]; - onHasMessagesChange?: (hasMessages: boolean) => void; - msgs?: (Messages: Message[]) => void; - }) => { - const [space, setSpace] = useState(null); + chatId, + history, + onHasMessagesChange, + msgs, +}: { + chatId: number | string; + history?: Message[]; + onHasMessagesChange?: (hasMessages: boolean) => void; + msgs?: (Messages: Message[]) => void; +}) => { + const [space, setSpace] = useState("default-ui"); - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === "Enter" && !event.shiftKey) { - event.preventDefault(); - const form = document.querySelector("form"); - if (form) { - form.requestSubmit(); - } - } - }; - - document.addEventListener("keydown", handleKeyDown); - - return () => { - document.removeEventListener("keydown", handleKeyDown); - }; - }, []); + // Stable handleSpaceSelect function + const handleSpaceSelect = useCallback((selectedSpaceId: string | null) => { + console.log("Space selected:", selectedSpaceId); + setSpace(selectedSpaceId); + }, []); - const { messages, handleInputChange, handleSubmit, input, isLoading } = + // Memoize API body to avoid unnecessary re-renders + const apiBody = useMemo( + () => ({ + chatId, + namespace: space, + }), + [chatId, space], + ); + + const { messages, handleInputChange, handleSubmit, input, isLoading } = useChat({ api: "/api/ai/stream", - body: { chatId, namespace: space }, + body: apiBody, initialMessages: history, }); - useEffect(() => { - if (messages.length > 0) { - if (onHasMessagesChange) { - onHasMessagesChange(true); - } - if (msgs) { - msgs(messages); + // Add Enter key submission logic + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + const form = document.querySelector("form"); + if (form) { + form.requestSubmit(); } + } + }; + + document.addEventListener("keydown", handleKeyDown); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, []); + + // Track changes to messages + useEffect(() => { + if (messages.length > 0) { + onHasMessagesChange?.(true); + msgs?.(messages); } - }, [ chatId, onHasMessagesChange, messages, msgs]); + }, [messages, onHasMessagesChange, msgs]); - return ( - - -
-
+ return ( +
+ {messages.length > 0 && } + + +
+
- -
- -
- - ) - } + +
+ +
+ +
+ ); +}; diff --git a/components/custom/hero/dragable.cards.tsx b/components/custom/hero/dragable.cards.tsx index 8e336ff..c044e05 100644 --- a/components/custom/hero/dragable.cards.tsx +++ b/components/custom/hero/dragable.cards.tsx @@ -111,11 +111,11 @@ export default function DragableCards() { }} >
- +
diff --git a/components/custom/lib.selector.tsx b/components/custom/lib.selector.tsx index 90569f5..35b1e3e 100644 --- a/components/custom/lib.selector.tsx +++ b/components/custom/lib.selector.tsx @@ -1,10 +1,10 @@ -"use client" +"use client"; -import * as React from "react" -import { Check, ChevronsUpDown } from "lucide-react" +import * as React from "react"; +import { Check, ChevronsUpDown } from "lucide-react"; -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; import { Command, CommandEmpty, @@ -12,12 +12,12 @@ import { CommandInput, CommandItem, CommandList, -} from "@/components/ui/command" +} from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger, -} from "@/components/ui/popover" +} from "@/components/ui/popover"; const frameworks = [ { @@ -31,21 +31,19 @@ const frameworks = [ { value: "default-ui", label: "Default UI", - } -] + }, +]; interface SpaceCompProps { - onSpaceSelect?: (selectedSpaceId: string | null) => void; - } + onSpaceSelect?: (selectedSpaceId: string | null) => void; +} export function LibSelector({ onSpaceSelect }: SpaceCompProps) { - const [open, setOpen] = React.useState(false) - const [value, setValue] = React.useState("default-ui") + const [open, setOpen] = React.useState(false); + const [value, setValue] = React.useState("default-ui"); React.useEffect(() => { - if (onSpaceSelect) { - onSpaceSelect(value); - } + onSpaceSelect?.(value); }, [value, onSpaceSelect]); return ( @@ -74,14 +72,14 @@ export function LibSelector({ onSpaceSelect }: SpaceCompProps) { key={framework.value} value={framework.value} onSelect={(currentValue) => { - setValue(currentValue === value ? "" : currentValue) - setOpen(false) + setValue(currentValue === value ? "" : currentValue); + setOpen(false); }} > {framework.label} @@ -92,5 +90,5 @@ export function LibSelector({ onSpaceSelect }: SpaceCompProps) { - ) + ); } diff --git a/components/custom/loading.screen.tsx b/components/custom/loading.screen.tsx new file mode 100644 index 0000000..1746fcb --- /dev/null +++ b/components/custom/loading.screen.tsx @@ -0,0 +1,15 @@ +import React from "react"; + +const LoadingScreen = () => { + return ( +
+
+

+ Acter is syncing... +

+
+
+ ); +}; + +export default LoadingScreen; diff --git a/components/custom/mdx.comp.tsx b/components/custom/mdx.comp.tsx new file mode 100644 index 0000000..796b5a3 --- /dev/null +++ b/components/custom/mdx.comp.tsx @@ -0,0 +1,57 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; +import { atomDark } from "react-syntax-highlighter/dist/esm/styles/prism"; + +export const MDXComponents = { + // Heading 1 + h1: (props: any) => ( +

+ ), + // Heading 2 + h2: (props: any) => ( +

+ ), + // Paragraph + p: (props: any) => ( +

+ ), + // Code Block + code: ({ children, className }: any) => { + const language = className?.replace("language-", ""); + return ( + + {children} + + ); + }, + // Inline code + inlineCode: ({ children }: any) => ( + {children} + ), + // Link + a: (props: any) => ( + + ), + // List + ul: (props: any) =>

); -} \ No newline at end of file +} diff --git a/components/custom/msgs/msgs.wrapper.tsx b/components/custom/msgs/msgs.wrapper.tsx index 4c0b4fa..540af74 100644 --- a/components/custom/msgs/msgs.wrapper.tsx +++ b/components/custom/msgs/msgs.wrapper.tsx @@ -20,7 +20,7 @@ export default function MsgsWrapper({ msgs }: MsgsProps) { return (
{msgs.map((message, i) => ( ); -} \ No newline at end of file +} diff --git a/components/custom/sidebar/sidebar.tsx b/components/custom/sidebar/sidebar.tsx index e2bc2ac..077207b 100644 --- a/components/custom/sidebar/sidebar.tsx +++ b/components/custom/sidebar/sidebar.tsx @@ -1,10 +1,7 @@ import { Button } from "@/components/ui/button"; import { menuItems } from "@/db/defaults"; import { cn } from "@/lib/utils"; -import { - PanelLeftClose, - PanelLeftOpen, -} from "lucide-react"; +import { PanelLeftClose, PanelLeftOpen } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; @@ -21,28 +18,30 @@ export const SideBar = ({
{/* Header Section */}
{/* Logo */} Acter Logo + src="/assets/acter-logo.jpg" + alt="Acter Logo" + width={30} + height={30} + className="rounded-md cursor-pointer" + /> {/* Toggle Button */} - {isSidebarExpanded && } + {isSidebarExpanded && ( + + )}
{/* Navigation Menu */} @@ -57,7 +56,7 @@ export const SideBar = ({ className={cn( "flex items-center rounded-lg p-2 mx-2 text-sm font-medium hover:bg-accent transition-colors", isSidebarExpanded ? "justify-start px-3" : "justify-center", - item.disabled ? "opacity-50" : "" + item.disabled ? "opacity-50" : "", )} aria-disabled={item.disabled} onClick={(e) => item.disabled && e.preventDefault()} diff --git a/components/ui/aurora-background.tsx b/components/ui/aurora-background.tsx index 7627178..4123fa3 100644 --- a/components/ui/aurora-background.tsx +++ b/components/ui/aurora-background.tsx @@ -18,7 +18,7 @@ export const AuroraBackground = ({
@@ -43,7 +43,7 @@ export const AuroraBackground = ({ absolute -inset-[10px] opacity-50 will-change-transform`, showRadialGradient && - `[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]` + `[mask-image:radial-gradient(ellipse_at_100%_0%,black_10%,var(--transparent)_70%)]`, )} >
diff --git a/components/ui/command.tsx b/components/ui/command.tsx index ade5441..ef49042 100644 --- a/components/ui/command.tsx +++ b/components/ui/command.tsx @@ -1,11 +1,11 @@ -"use client" +"use client"; -import * as React from "react" -import { type DialogProps } from "@radix-ui/react-dialog" -import { Command as CommandPrimitive } from "cmdk" -import { cn } from "@/lib/utils" -import { Dialog, DialogContent } from "@/components/ui/dialog" -import { MagnifyingGlassIcon } from "@radix-ui/react-icons" +import * as React from "react"; +import { type DialogProps } from "@radix-ui/react-dialog"; +import { Command as CommandPrimitive } from "cmdk"; +import { cn } from "@/lib/utils"; +import { Dialog, DialogContent } from "@/components/ui/dialog"; +import { MagnifyingGlassIcon } from "@radix-ui/react-icons"; const Command = React.forwardRef< React.ElementRef, @@ -15,12 +15,12 @@ const Command = React.forwardRef< ref={ref} className={cn( "flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground", - className + className, )} {...props} /> -)) -Command.displayName = CommandPrimitive.displayName +)); +Command.displayName = CommandPrimitive.displayName; const CommandDialog = ({ children, ...props }: DialogProps) => { return ( @@ -31,8 +31,8 @@ const CommandDialog = ({ children, ...props }: DialogProps) => { - ) -} + ); +}; const CommandInput = React.forwardRef< React.ElementRef, @@ -44,14 +44,14 @@ const CommandInput = React.forwardRef< ref={ref} className={cn( "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50", - className + className, )} {...props} />
-)) +)); -CommandInput.displayName = CommandPrimitive.Input.displayName +CommandInput.displayName = CommandPrimitive.Input.displayName; const CommandList = React.forwardRef< React.ElementRef, @@ -62,9 +62,9 @@ const CommandList = React.forwardRef< className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)} {...props} /> -)) +)); -CommandList.displayName = CommandPrimitive.List.displayName +CommandList.displayName = CommandPrimitive.List.displayName; const CommandEmpty = React.forwardRef< React.ElementRef, @@ -75,9 +75,9 @@ const CommandEmpty = React.forwardRef< className="py-6 text-center text-sm" {...props} /> -)) +)); -CommandEmpty.displayName = CommandPrimitive.Empty.displayName +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; const CommandGroup = React.forwardRef< React.ElementRef, @@ -87,13 +87,13 @@ const CommandGroup = React.forwardRef< ref={ref} className={cn( "overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground", - className + className, )} {...props} /> -)) +)); -CommandGroup.displayName = CommandPrimitive.Group.displayName +CommandGroup.displayName = CommandPrimitive.Group.displayName; const CommandSeparator = React.forwardRef< React.ElementRef, @@ -104,8 +104,8 @@ const CommandSeparator = React.forwardRef< className={cn("-mx-1 h-px bg-border", className)} {...props} /> -)) -CommandSeparator.displayName = CommandPrimitive.Separator.displayName +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; const CommandItem = React.forwardRef< React.ElementRef, @@ -115,13 +115,13 @@ const CommandItem = React.forwardRef< ref={ref} className={cn( "relative flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", - className + className, )} {...props} /> -)) +)); -CommandItem.displayName = CommandPrimitive.Item.displayName +CommandItem.displayName = CommandPrimitive.Item.displayName; const CommandShortcut = ({ className, @@ -131,13 +131,13 @@ const CommandShortcut = ({ - ) -} -CommandShortcut.displayName = "CommandShortcut" + ); +}; +CommandShortcut.displayName = "CommandShortcut"; export { Command, @@ -149,4 +149,4 @@ export { CommandItem, CommandShortcut, CommandSeparator, -} +}; diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 95b0d38..3db2a7d 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -1,18 +1,18 @@ -"use client" +"use client"; -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { Cross2Icon } from "@radix-ui/react-icons" +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { Cross2Icon } from "@radix-ui/react-icons"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -const Dialog = DialogPrimitive.Root +const Dialog = DialogPrimitive.Root; -const DialogTrigger = DialogPrimitive.Trigger +const DialogTrigger = DialogPrimitive.Trigger; -const DialogPortal = DialogPrimitive.Portal +const DialogPortal = DialogPrimitive.Portal; -const DialogClose = DialogPrimitive.Close +const DialogClose = DialogPrimitive.Close; const DialogOverlay = React.forwardRef< React.ElementRef, @@ -22,12 +22,12 @@ const DialogOverlay = React.forwardRef< ref={ref} className={cn( "fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0", - className + className, )} {...props} /> -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, @@ -39,7 +39,7 @@ const DialogContent = React.forwardRef< ref={ref} className={cn( "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg", - className + className, )} {...props} > @@ -50,8 +50,8 @@ const DialogContent = React.forwardRef< -)) -DialogContent.displayName = DialogPrimitive.Content.displayName +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; const DialogHeader = ({ className, @@ -60,12 +60,12 @@ const DialogHeader = ({
-) -DialogHeader.displayName = "DialogHeader" +); +DialogHeader.displayName = "DialogHeader"; const DialogFooter = ({ className, @@ -74,12 +74,12 @@ const DialogFooter = ({
-) -DialogFooter.displayName = "DialogFooter" +); +DialogFooter.displayName = "DialogFooter"; const DialogTitle = React.forwardRef< React.ElementRef, @@ -89,12 +89,12 @@ const DialogTitle = React.forwardRef< ref={ref} className={cn( "text-lg font-semibold leading-none tracking-tight", - className + className, )} {...props} /> -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< React.ElementRef, @@ -105,8 +105,8 @@ const DialogDescription = React.forwardRef< className={cn("text-sm text-muted-foreground", className)} {...props} /> -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; export { Dialog, @@ -119,4 +119,4 @@ export { DialogFooter, DialogTitle, DialogDescription, -} +}; diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx index 29c7bd2..bd49501 100644 --- a/components/ui/popover.tsx +++ b/components/ui/popover.tsx @@ -1,15 +1,15 @@ -"use client" +"use client"; -import * as React from "react" -import * as PopoverPrimitive from "@radix-ui/react-popover" +import * as React from "react"; +import * as PopoverPrimitive from "@radix-ui/react-popover"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -const Popover = PopoverPrimitive.Root +const Popover = PopoverPrimitive.Root; -const PopoverTrigger = PopoverPrimitive.Trigger +const PopoverTrigger = PopoverPrimitive.Trigger; -const PopoverAnchor = PopoverPrimitive.Anchor +const PopoverAnchor = PopoverPrimitive.Anchor; const PopoverContent = React.forwardRef< React.ElementRef, @@ -22,12 +22,12 @@ const PopoverContent = React.forwardRef< sideOffset={sideOffset} className={cn( "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", - className + className, )} {...props} /> -)) -PopoverContent.displayName = PopoverPrimitive.Content.displayName +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; -export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor } +export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }; diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx index 9e74821..92a97e4 100644 --- a/components/ui/tooltip.tsx +++ b/components/ui/tooltip.tsx @@ -1,15 +1,15 @@ -"use client" +"use client"; -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" +import * as React from "react"; +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -import { cn } from "@/lib/utils" +import { cn } from "@/lib/utils"; -const TooltipProvider = TooltipPrimitive.Provider +const TooltipProvider = TooltipPrimitive.Provider; -const Tooltip = TooltipPrimitive.Root +const Tooltip = TooltipPrimitive.Root; -const TooltipTrigger = TooltipPrimitive.Trigger +const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipContent = React.forwardRef< React.ElementRef, @@ -20,11 +20,11 @@ const TooltipContent = React.forwardRef< sideOffset={sideOffset} className={cn( "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", - className + className, )} {...props} /> -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/context/user.context.tsx b/context/user.context.tsx index ea70e4d..5f6d891 100644 --- a/context/user.context.tsx +++ b/context/user.context.tsx @@ -1,6 +1,12 @@ "use client"; -import React, { createContext, useContext, useState, useEffect, ReactNode } from "react"; -import { User } from "next-auth"; +import React, { + createContext, + useContext, + useState, + useEffect, + ReactNode, +} from "react"; +import { User } from "next-auth"; interface UserContextType { user: User | null; @@ -12,7 +18,9 @@ const UserContext = createContext({ loading: true, }); -export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) => { +export const UserProvider: React.FC<{ children: ReactNode }> = ({ + children, +}) => { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); diff --git a/db/defaults.ts b/db/defaults.ts index 1742ecb..39b4197 100644 --- a/db/defaults.ts +++ b/db/defaults.ts @@ -1,13 +1,9 @@ -import { - PlusIcon, - BookOpenIcon, - LayoutDashboardIcon, -} from "lucide-react"; +import { PlusIcon, BookOpenIcon, LayoutDashboardIcon } from "lucide-react"; export const ragConfig = { baseUrl: "https://api.deepseek.com", model: "deepseek-ai/deepseek-llm-67b-chat", -} +}; export const OfcLinks = { github: "https://l.devwtf.in/discord", @@ -15,20 +11,25 @@ export const OfcLinks = { instagram: "https://dub.sh/saidev-instagram", sponsor: "https://github.com/sponsors/SkidGod4444", portfolio: "http://devwtf.in", - discord: "https://l.devwtf.in/discord" + discord: "https://l.devwtf.in/discord", }; export const menuItems = [ { label: "New Chat", icon: PlusIcon, href: "/chat", disabled: false }, { label: "Docs", icon: BookOpenIcon, href: "/docs", disabled: true }, - { label: "Dashboard", icon: LayoutDashboardIcon, href: "/dashboard", disabled: true }, + { + label: "Dashboard", + icon: LayoutDashboardIcon, + href: "/dashboard", + disabled: true, + }, ]; export const DATA = { name: "Acter", url: "https://acter.devwtf.in", description: "", - prevImage: "https://i.imgur.com/JwDi96s.png" + prevImage: "https://i.imgur.com/JwDi96s.png", }; export const beforeCode = `function sumArrayTraditional(arr: number[]): number { diff --git a/lib/rag.ts b/lib/rag.ts index 0b13788..5cef62d 100644 --- a/lib/rag.ts +++ b/lib/rag.ts @@ -1,12 +1,14 @@ import { ragConfig } from "@/db/defaults"; import { RAGChat, togetherai } from "@upstash/rag-chat"; +import { redisDB } from "./redis"; export const ragChat = new RAGChat({ model: togetherai(ragConfig.model, { apiKey: process.env.RAG_API_KEY }), + redis: redisDB, promptFn: ({ context, question, chatHistory }) => `You are Acter, an AI assistant created by Saidev Dhal. Your task is to generate complete, high-quality code solutions based on the user's question or prompt. - Use the provided context and chat history to craft the code. + Use the provided context and chat history to craft the code and reply everything in MDX format. Ensure that the generated code incorporates and appropriately uses the components and styles mentioned in the context and explain all the details related to that component. Only create new code without using any component from the context if it is absolutely necessary, and be sure to explain why. If you lack sufficient information in the context or chat history, politely suggest the user provide additional details. @@ -20,7 +22,7 @@ export const ragChat = new RAGChat({ Question: ${question} ------ - Full Code:`, + Full Code in MDX format:`, }); async function AddTXTContext(data: string) { @@ -42,4 +44,4 @@ async function AddWEBContext(src: string) { return true; } -export { AddTXTContext, AddWEBContext } \ No newline at end of file +export { AddTXTContext, AddWEBContext }; diff --git a/lib/redis.ts b/lib/redis.ts new file mode 100644 index 0000000..00c8a7e --- /dev/null +++ b/lib/redis.ts @@ -0,0 +1,3 @@ +import { Redis } from "@upstash/redis"; + +export const redisDB = Redis.fromEnv(); diff --git a/lib/use.local.ts b/lib/use.local.ts index 058fb7f..d649edc 100644 --- a/lib/use.local.ts +++ b/lib/use.local.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from "react"; const useLocalStorage = (key: string, initialValue: string | boolean) => { const [value, setValue] = useState(() => { - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { const storedValue = localStorage.getItem(key); // Return the stored value if it exists, otherwise return the initial value return storedValue !== null @@ -16,14 +16,14 @@ const useLocalStorage = (key: string, initialValue: string | boolean) => { const setLocalStorageValue = (newValue: string | boolean) => { setValue(newValue); - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { localStorage.setItem(key, newValue.toString()); } }; useEffect(() => { const handleStorageChange = () => { - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { const newValue = localStorage.getItem(key); // Update state based on the new value from localStorage setValue( @@ -36,12 +36,12 @@ const useLocalStorage = (key: string, initialValue: string | boolean) => { } }; - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { window.addEventListener("storage", handleStorageChange); } return () => { - if (typeof window !== 'undefined') { + if (typeof window !== "undefined") { window.removeEventListener("storage", handleStorageChange); } }; @@ -50,4 +50,4 @@ const useLocalStorage = (key: string, initialValue: string | boolean) => { return [value, setLocalStorageValue] as const; }; -export default useLocalStorage; \ No newline at end of file +export default useLocalStorage; diff --git a/package.json b/package.json index 7cd131d..cf7b0fe 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ }, "dependencies": { "@auth/prisma-adapter": "^2.5.3", + "@codemirror/lang-javascript": "^6.2.2", "@internationalized/date": "^3.5.6", + "@mdx-js/react": "^3.1.0", "@nextui-org/calendar": "^2.0.12", "@nextui-org/checkbox": "^2.1.5", "@nextui-org/switch": "^2.0.34", @@ -25,6 +27,8 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tooltip": "^1.1.3", "@tabler/icons-react": "^3.19.0", + "@types/react-syntax-highlighter": "^15.5.13", + "@uiw/react-codemirror": "^4.23.7", "@upstash/rag-chat": "^2.0.0", "@upstash/redis": "^1.34.2", "@upstash/vector": "^1.1.7", @@ -42,6 +46,7 @@ "prisma": "^5.20.0", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-syntax-highlighter": "^15.6.1", "shiki": "^1.21.0", "tailwind-merge": "^2.5.3", "tailwindcss-animate": "^1.0.7", diff --git a/tailwind.config.ts b/tailwind.config.ts index f74e309..1c5587f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -1,7 +1,7 @@ import type { Config } from "tailwindcss"; const { - default: flattenColorPalette, - } = require("tailwindcss/lib/util/flattenColorPalette"); + default: flattenColorPalette, +} = require("tailwindcss/lib/util/flattenColorPalette"); const config: Config = { darkMode: ["class"], @@ -11,118 +11,118 @@ const config: Config = { "./app/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { - extend: { - colors: { - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))' - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))' - }, - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))' - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))' - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))' - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))' - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))' - }, - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - chart: { - '1': 'hsl(var(--chart-1))', - '2': 'hsl(var(--chart-2))', - '3': 'hsl(var(--chart-3))', - '4': 'hsl(var(--chart-4))', - '5': 'hsl(var(--chart-5))' - }, - 'color-1': 'hsl(var(--color-1))', - 'color-2': 'hsl(var(--color-2))', - 'color-3': 'hsl(var(--color-3))', - 'color-4': 'hsl(var(--color-4))', - 'color-5': 'hsl(var(--color-5))' - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)' - }, - animation: { - aurora: "aurora 60s linear infinite", - shine: 'shine 2s infinite linear', - 'shiny-text': 'shiny-text 8s infinite', - rainbow: 'rainbow var(--speed, 2s) infinite linear', - gradient: 'gradient 8s linear infinite' - }, - keyframes: { - aurora: { - from: { - backgroundPosition: "50% 50%, 50% 50%", - }, - to: { - backgroundPosition: "350% 50%, 350% 50%", - }, - }, - shine: { - '0%': { - backgroundPosition: '200% 0' - }, - '100%': { - backgroundPosition: '-200% 0' - } - }, - 'shiny-text': { - '0%, 90%, 100%': { - 'background-position': 'calc(-100% - var(--shiny-width)) 0' - }, - '30%, 60%': { - 'background-position': 'calc(100% + var(--shiny-width)) 0' - } - }, - rainbow: { - '0%': { - 'background-position': '0%' - }, - '100%': { - 'background-position': '200%' - } - }, - gradient: { - to: { - backgroundPosition: 'var(--bg-size) 0' - } - } - } - } + extend: { + colors: { + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + chart: { + "1": "hsl(var(--chart-1))", + "2": "hsl(var(--chart-2))", + "3": "hsl(var(--chart-3))", + "4": "hsl(var(--chart-4))", + "5": "hsl(var(--chart-5))", + }, + "color-1": "hsl(var(--color-1))", + "color-2": "hsl(var(--color-2))", + "color-3": "hsl(var(--color-3))", + "color-4": "hsl(var(--color-4))", + "color-5": "hsl(var(--color-5))", + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + animation: { + aurora: "aurora 60s linear infinite", + shine: "shine 2s infinite linear", + "shiny-text": "shiny-text 8s infinite", + rainbow: "rainbow var(--speed, 2s) infinite linear", + gradient: "gradient 8s linear infinite", + }, + keyframes: { + aurora: { + from: { + backgroundPosition: "50% 50%, 50% 50%", + }, + to: { + backgroundPosition: "350% 50%, 350% 50%", + }, + }, + shine: { + "0%": { + backgroundPosition: "200% 0", + }, + "100%": { + backgroundPosition: "-200% 0", + }, + }, + "shiny-text": { + "0%, 90%, 100%": { + "background-position": "calc(-100% - var(--shiny-width)) 0", + }, + "30%, 60%": { + "background-position": "calc(100% + var(--shiny-width)) 0", + }, + }, + rainbow: { + "0%": { + "background-position": "0%", + }, + "100%": { + "background-position": "200%", + }, + }, + gradient: { + to: { + backgroundPosition: "var(--bg-size) 0", + }, + }, + }, + }, }, plugins: [addVariablesForColors, require("tailwindcss-animate")], }; function addVariablesForColors({ addBase, theme }: any) { - const allColors = flattenColorPalette(theme("colors")); - const newVars = Object.fromEntries( - Object.entries(allColors).map(([key, val]) => [`--${key}`, val]) - ); - - addBase({ - ":root": newVars, - }); - } + const allColors = flattenColorPalette(theme("colors")); + const newVars = Object.fromEntries( + Object.entries(allColors).map(([key, val]) => [`--${key}`, val]), + ); + + addBase({ + ":root": newVars, + }); +} export default config;