From 7951221310fc16dddeaac08a3663bba9f93f7c81 Mon Sep 17 00:00:00 2001 From: Akhileshrangani4 Date: Sun, 20 Oct 2024 23:23:04 -0400 Subject: [PATCH 1/8] fix: global buttons and indicators - cmd/ctrl + L works globally now - added the copilot and ai chat button indicators - when aichat is open, the preview/terminal column becomes horizontal --- frontend/components/editor/AIChat/index.tsx | 15 +++++- frontend/components/editor/index.tsx | 55 +++++++++++++++----- frontend/components/editor/sidebar/index.tsx | 37 ++++++++----- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/frontend/components/editor/AIChat/index.tsx b/frontend/components/editor/AIChat/index.tsx index a9778c4..316fa07 100644 --- a/frontend/components/editor/AIChat/index.tsx +++ b/frontend/components/editor/AIChat/index.tsx @@ -4,6 +4,7 @@ import ChatMessage from './ChatMessage'; import ChatInput from './ChatInput'; import ContextDisplay from './ContextDisplay'; import { handleSend, handleStopGeneration } from './lib/chatUtils'; +import { X } from 'lucide-react'; interface Message { role: 'user' | 'assistant'; @@ -11,7 +12,7 @@ interface Message { context?: string; } -export default function AIChat({ activeFileContent, activeFileName }: { activeFileContent: string, activeFileName: string }) { +export default function AIChat({ activeFileContent, activeFileName, onClose }: { activeFileContent: string, activeFileName: string, onClose: () => void }) { const [messages, setMessages] = useState([]); const [input, setInput] = useState(''); const [isGenerating, setIsGenerating] = useState(false); @@ -40,7 +41,17 @@ export default function AIChat({ activeFileContent, activeFileName }: { activeFi
CHAT - {activeFileName} +
+ {activeFileName} +
+ +
{messages.map((message, messageIndex) => ( diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 5ff1a7c..68a3ed5 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -76,6 +76,7 @@ export default function CodeEditor({ // Layout state const [isHorizontalLayout, setIsHorizontalLayout] = useState(false); + const [previousLayout, setPreviousLayout] = useState(false); // AI Chat state const [isAIChatOpen, setIsAIChatOpen] = useState(false); @@ -530,13 +531,19 @@ export default function CodeEditor({ e.preventDefault() setIsAIChatOpen(prev => !prev); } - } - document.addEventListener("keydown", down) - + }; + + document.addEventListener("keydown", down); + + // Added this line to prevent Monaco editor from handling Cmd/Ctrl+L + editorRef?.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyL, () => { + setIsAIChatOpen(prev => !prev); + }); + return () => { document.removeEventListener("keydown", down) } - }, [activeFileId, tabs, debouncedSaveData, setIsAIChatOpen]) + }, [activeFileId, tabs, debouncedSaveData, setIsAIChatOpen, editorRef]) // Liveblocks live collaboration setup effect useEffect(() => { @@ -848,7 +855,24 @@ export default function CodeEditor({ }; const toggleLayout = () => { - setIsHorizontalLayout(prev => !prev); + if (!isAIChatOpen) { + setIsHorizontalLayout(prev => !prev); + } + }; + + // Add an effect to handle layout changes when AI chat is opened/closed + useEffect(() => { + if (isAIChatOpen) { + setPreviousLayout(isHorizontalLayout); + setIsHorizontalLayout(true); + } else { + setIsHorizontalLayout(previousLayout); + } + }, [isAIChatOpen]); + + // Modify the toggleAIChat function + const toggleAIChat = () => { + setIsAIChatOpen(prev => !prev); }; // On disabled access for shared users, show un-interactable loading placeholder + info modal @@ -984,7 +1008,7 @@ export default function CodeEditor({ deletingFolderId={deletingFolderId} /> {/* Outer ResizablePanelGroup for main layout */} - + {/* Left side: Editor and Preview/Terminal */} @@ -1101,7 +1125,13 @@ export default function CodeEditor({ onExpand={() => setIsPreviewCollapsed(false)} >
- - tab.id === activeFileId)?.name || 'No file selected'} - /> + tab.id === activeFileId)?.name || 'No file selected'} + onClose={toggleAIChat} + /> )} @@ -1171,4 +1202,4 @@ const defaultCompilerOptions: monaco.languages.typescript.CompilerOptions = { module: monaco.languages.typescript.ModuleKind.ESNext, moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs, target: monaco.languages.typescript.ScriptTarget.ESNext, -} \ No newline at end of file +} diff --git a/frontend/components/editor/sidebar/index.tsx b/frontend/components/editor/sidebar/index.tsx index 45fa643..8fc706f 100644 --- a/frontend/components/editor/sidebar/index.tsx +++ b/frontend/components/editor/sidebar/index.tsx @@ -4,9 +4,8 @@ import { FilePlus, FolderPlus, Loader2, - MonitorPlay, - Search, Sparkles, + MessageSquareMore, } from "lucide-react"; import SidebarFile from "./file"; import SidebarFolder from "./folder"; @@ -14,14 +13,13 @@ import { Sandbox, TFile, TFolder, TTab } from "@/lib/types"; import { useEffect, useRef, useState } from "react"; import New from "./new"; import { Socket } from "socket.io-client"; -import { Switch } from "@/components/ui/switch"; +import { Button } from "@/components/ui/button"; import { dropTargetForElements, monitorForElements, } from "@atlaskit/pragmatic-drag-and-drop/element/adapter"; -import Button from "@/components/ui/customButton"; - + export default function Sidebar({ sandboxData, files, @@ -105,9 +103,9 @@ export default function Sidebar({ }, []); return ( -
-
-
+
+
+
Explorer
-
- {/* */} +
+ +
); From 2897b908fd8a5126e7ade152988d1a7b4126a1fb Mon Sep 17 00:00:00 2001 From: James Murdza Date: Mon, 21 Oct 2024 13:51:18 -0600 Subject: [PATCH 2/8] chore: add code formatting settings --- .prettierignore | 3 +++ .vscode/settings.json | 8 ++++++++ frontend/.prettierrc | 3 ++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 .prettierignore create mode 100644 .vscode/settings.json diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..4563529 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +backend/ai/** +backend/database/** +backend/storage/** \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..4b81cce --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.codeActionsOnSave": { + "source.fixAll": "explicit", + "source.organizeImports": "explicit" + } +} diff --git a/frontend/.prettierrc b/frontend/.prettierrc index 42830e2..6449bfb 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -1,5 +1,6 @@ { "tabWidth": 2, "semi": false, - "singleQuote": false + "singleQuote": false, + "insertFinalNewline": true } \ No newline at end of file From 305939c15c37de149f1c6f48eaa47df9ecf647c8 Mon Sep 17 00:00:00 2001 From: James Murdza Date: Mon, 21 Oct 2024 13:51:18 -0600 Subject: [PATCH 3/8] chore: enable code formatting for frontend code # Conflicts: # .prettierignore --- .prettierignore | 1 - frontend/.prettierrc | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index 4314ec3..4563529 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ -frontend/** backend/ai/** backend/database/** backend/storage/** \ No newline at end of file diff --git a/frontend/.prettierrc b/frontend/.prettierrc index 42830e2..6449bfb 100644 --- a/frontend/.prettierrc +++ b/frontend/.prettierrc @@ -1,5 +1,6 @@ { "tabWidth": 2, "semi": false, - "singleQuote": false + "singleQuote": false, + "insertFinalNewline": true } \ No newline at end of file From 3f8e27d9693b3879152af21afe8ca0a115fd269f Mon Sep 17 00:00:00 2001 From: Akhilesh Rangani Date: Mon, 21 Oct 2024 13:57:17 -0600 Subject: [PATCH 4/8] chore: format frontend code --- frontend/app/(app)/code/[id]/page.tsx | 36 +-- frontend/app/(app)/dashboard/page.tsx | 6 +- frontend/app/layout.tsx | 22 +- frontend/app/page.tsx | 12 +- frontend/components/dashboard/about.tsx | 7 - frontend/components/dashboard/index.tsx | 27 +- .../components/dashboard/navbar/index.tsx | 6 +- .../components/dashboard/navbar/search.tsx | 19 +- frontend/components/dashboard/newProject.tsx | 28 +-- .../dashboard/projectCard/dropdown.tsx | 28 +-- .../dashboard/projectCard/index.tsx | 10 +- .../dashboard/projectCard/revealEffect.tsx | 152 +++++------ frontend/components/dashboard/projects.tsx | 55 ++-- frontend/components/dashboard/shared.tsx | 28 +-- .../components/editor/AIChat/ChatInput.tsx | 43 ++-- .../components/editor/AIChat/ChatMessage.tsx | 153 +++++++----- .../editor/AIChat/ContextDisplay.tsx | 54 ++-- frontend/components/editor/AIChat/index.tsx | 95 ++++--- .../components/editor/AIChat/lib/chatUtils.ts | 212 +++++++++------- frontend/components/editor/generate.tsx | 12 +- frontend/components/editor/index.tsx | 236 ++++++++++-------- frontend/components/editor/live/avatars.tsx | 12 +- frontend/components/editor/live/cursors.tsx | 5 +- .../components/editor/live/disableModal.tsx | 36 ++- frontend/components/editor/live/room.tsx | 11 +- frontend/components/editor/loading/index.tsx | 6 +- frontend/components/editor/navbar/deploy.tsx | 73 ++++-- frontend/components/editor/navbar/edit.tsx | 65 +++-- frontend/components/editor/navbar/index.tsx | 59 +++-- frontend/components/editor/navbar/run.tsx | 95 +++---- frontend/components/editor/navbar/share.tsx | 12 +- frontend/components/editor/preview/index.tsx | 94 +++---- frontend/components/editor/sidebar/file.tsx | 84 +++---- frontend/components/editor/sidebar/folder.tsx | 16 +- frontend/components/editor/sidebar/index.tsx | 21 +- frontend/components/editor/sidebar/new.tsx | 44 ++-- .../components/editor/terminals/index.tsx | 43 ++-- .../components/editor/terminals/terminal.tsx | 8 +- .../components/editor/terminals/xterm.css | 62 +++-- frontend/components/landing/index.tsx | 2 +- frontend/components/layout/themeProvider.tsx | 1 - frontend/components/ui/LoadingDots.tsx | 35 ++- frontend/components/ui/alert-dialog.tsx | 20 +- frontend/components/ui/button.tsx | 2 +- frontend/components/ui/card.tsx | 2 +- frontend/components/ui/context-menu.tsx | 16 +- frontend/components/ui/customButton.tsx | 3 +- frontend/components/ui/dialog.tsx | 60 ++--- frontend/components/ui/dropdown-menu.tsx | 16 +- frontend/components/ui/form.tsx | 12 +- frontend/components/ui/label.tsx | 2 +- frontend/components/ui/popover.tsx | 4 +- frontend/components/ui/resizable.tsx | 2 +- frontend/components/ui/select.tsx | 14 +- frontend/components/ui/switch.tsx | 2 +- frontend/components/ui/tab.tsx | 30 +-- frontend/components/ui/table.tsx | 6 +- frontend/components/ui/userButton.tsx | 21 +- frontend/context/PreviewContext.tsx | 48 ++-- frontend/context/SocketContext.tsx | 76 +++--- frontend/context/TerminalContext.tsx | 95 +++---- frontend/lib/terminal.ts | 97 +++---- frontend/lib/types.ts | 92 +++---- frontend/lib/utils.ts | 2 +- 64 files changed, 1405 insertions(+), 1242 deletions(-) diff --git a/frontend/app/(app)/code/[id]/page.tsx b/frontend/app/(app)/code/[id]/page.tsx index 9681a5b..d193d39 100644 --- a/frontend/app/(app)/code/[id]/page.tsx +++ b/frontend/app/(app)/code/[id]/page.tsx @@ -1,12 +1,11 @@ -import Navbar from "@/components/editor/navbar" import { Room } from "@/components/editor/live/room" +import Loading from "@/components/editor/loading" +import Navbar from "@/components/editor/navbar" +import { TerminalProvider } from "@/context/TerminalContext" import { Sandbox, User, UsersToSandboxes } from "@/lib/types" import { currentUser } from "@clerk/nextjs" -import { notFound, redirect } from "next/navigation" -import Loading from "@/components/editor/loading" import dynamic from "next/dynamic" -import fs from "fs" -import { TerminalProvider } from "@/context/TerminalContext" +import { notFound, redirect } from "next/navigation" export const revalidate = 0 @@ -89,19 +88,20 @@ export default async function CodePage({ params }: { params: { id: string } }) { return ( <> -
- - - -
- -
-
-
-
+
+ + + +
+ +
+
+
+
) } diff --git a/frontend/app/(app)/dashboard/page.tsx b/frontend/app/(app)/dashboard/page.tsx index 1f29f96..52f8c3f 100644 --- a/frontend/app/(app)/dashboard/page.tsx +++ b/frontend/app/(app)/dashboard/page.tsx @@ -1,8 +1,8 @@ -import { UserButton, currentUser } from "@clerk/nextjs" -import { redirect } from "next/navigation" import Dashboard from "@/components/dashboard" import Navbar from "@/components/dashboard/navbar" -import { Sandbox, User } from "@/lib/types" +import { User } from "@/lib/types" +import { currentUser } from "@clerk/nextjs" +import { redirect } from "next/navigation" export default async function DashboardPage() { const user = await currentUser() diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 70f5791..9448c07 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,13 +1,13 @@ -import type { Metadata } from "next" -import { GeistSans } from "geist/font/sans" -import { GeistMono } from "geist/font/mono" -import "./globals.css" import { ThemeProvider } from "@/components/layout/themeProvider" -import { ClerkProvider } from "@clerk/nextjs" import { Toaster } from "@/components/ui/sonner" +import { PreviewProvider } from "@/context/PreviewContext" +import { SocketProvider } from "@/context/SocketContext" +import { ClerkProvider } from "@clerk/nextjs" import { Analytics } from "@vercel/analytics/react" -import { PreviewProvider } from "@/context/PreviewContext"; -import { SocketProvider } from '@/context/SocketContext' +import { GeistMono } from "geist/font/mono" +import { GeistSans } from "geist/font/sans" +import type { Metadata } from "next" +import "./globals.css" export const metadata: Metadata = { title: "Sandbox", @@ -15,7 +15,7 @@ export const metadata: Metadata = { } export default function RootLayout({ - children + children, }: Readonly<{ children: React.ReactNode }>) { @@ -30,9 +30,7 @@ export default function RootLayout({ disableTransitionOnChange > - - {children} - + {children} @@ -41,4 +39,4 @@ export default function RootLayout({ ) -} \ No newline at end of file +} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 3f99044..8041367 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,13 +1,13 @@ -import { currentUser } from "@clerk/nextjs"; -import { redirect } from "next/navigation"; -import Landing from "@/components/landing"; +import Landing from "@/components/landing" +import { currentUser } from "@clerk/nextjs" +import { redirect } from "next/navigation" export default async function Home() { - const user = await currentUser(); + const user = await currentUser() if (user) { - redirect("/dashboard"); + redirect("/dashboard") } - return ; + return } diff --git a/frontend/components/dashboard/about.tsx b/frontend/components/dashboard/about.tsx index 33b0daa..7accef0 100644 --- a/frontend/components/dashboard/about.tsx +++ b/frontend/components/dashboard/about.tsx @@ -3,16 +3,9 @@ import { Dialog, DialogContent, - DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog" -import Image from "next/image" -import { useState } from "react" - -import { Button } from "../ui/button" -import { ChevronRight } from "lucide-react" export default function AboutModal({ open, diff --git a/frontend/components/dashboard/index.tsx b/frontend/components/dashboard/index.tsx index a7094ec..a1599c7 100644 --- a/frontend/components/dashboard/index.tsx +++ b/frontend/components/dashboard/index.tsx @@ -1,24 +1,16 @@ "use client" -import CustomButton from "@/components/ui/customButton" import { Button } from "@/components/ui/button" -import { - Code2, - FolderDot, - HelpCircle, - Plus, - Settings, - Users, -} from "lucide-react" -import { useEffect, useState } from "react" +import CustomButton from "@/components/ui/customButton" import { Sandbox } from "@/lib/types" +import { Code2, FolderDot, HelpCircle, Plus, Users } from "lucide-react" +import { useRouter, useSearchParams } from "next/navigation" +import { useEffect, useState } from "react" +import { toast } from "sonner" +import AboutModal from "./about" +import NewProjectModal from "./newProject" import DashboardProjects from "./projects" import DashboardSharedWithMe from "./shared" -import NewProjectModal from "./newProject" -import Link from "next/link" -import { useRouter, useSearchParams } from "next/navigation" -import AboutModal from "./about" -import { toast } from "sonner" type TScreen = "projects" | "shared" | "settings" | "search" @@ -49,8 +41,9 @@ export default function Dashboard({ const q = searchParams.get("q") const router = useRouter() - useEffect(() => { // update the dashboard to show a new project - router.refresh() + useEffect(() => { + // update the dashboard to show a new project + router.refresh() }, []) return ( diff --git a/frontend/components/dashboard/navbar/index.tsx b/frontend/components/dashboard/navbar/index.tsx index 8b1f02e..2f983af 100644 --- a/frontend/components/dashboard/navbar/index.tsx +++ b/frontend/components/dashboard/navbar/index.tsx @@ -1,9 +1,9 @@ +import Logo from "@/assets/logo.svg" +import { User } from "@/lib/types" import Image from "next/image" import Link from "next/link" -import Logo from "@/assets/logo.svg" -import DashboardNavbarSearch from "./search" import UserButton from "../../ui/userButton" -import { User } from "@/lib/types" +import DashboardNavbarSearch from "./search" export default function DashboardNavbar({ userData }: { userData: User }) { return ( diff --git a/frontend/components/dashboard/navbar/search.tsx b/frontend/components/dashboard/navbar/search.tsx index f254efe..75f314e 100644 --- a/frontend/components/dashboard/navbar/search.tsx +++ b/frontend/components/dashboard/navbar/search.tsx @@ -1,13 +1,12 @@ -"use client"; +"use client" -import { Input } from "../../ui/input"; -import { Search } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; +import { Search } from "lucide-react" +import { useRouter } from "next/navigation" +import { Input } from "../../ui/input" export default function DashboardNavbarSearch() { // const [search, setSearch] = useState(""); - const router = useRouter(); + const router = useRouter() // useEffect(() => { // const delayDebounceFn = setTimeout(() => { @@ -29,14 +28,14 @@ export default function DashboardNavbarSearch() { // onChange={(e) => setSearch(e.target.value)} onChange={(e) => { if (e.target.value === "") { - router.push(`/dashboard`); - return; + router.push(`/dashboard`) + return } - router.push(`/dashboard?q=${e.target.value}`); + router.push(`/dashboard?q=${e.target.value}`) }} placeholder="Search projects..." className="pl-8" />
- ); + ) } diff --git a/frontend/components/dashboard/newProject.tsx b/frontend/components/dashboard/newProject.tsx index b793fc2..0f1a5d2 100644 --- a/frontend/components/dashboard/newProject.tsx +++ b/frontend/components/dashboard/newProject.tsx @@ -3,16 +3,14 @@ import { Dialog, DialogContent, - DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog" -import Image from "next/image" -import { useState, useCallback, useEffect, useMemo } from "react" -import { set, z } from "zod" import { zodResolver } from "@hookform/resolvers/zod" +import Image from "next/image" +import { useCallback, useEffect, useMemo, useState } from "react" import { useForm } from "react-hook-form" +import { z } from "zod" import { Form, @@ -31,23 +29,17 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select" -import { useUser } from "@clerk/nextjs" import { createSandbox } from "@/lib/actions" -import { useRouter } from "next/navigation" -import { - Loader2, - ChevronRight, - ChevronLeft, - Search, - SlashSquare, -} from "lucide-react" -import { Button } from "../ui/button" import { projectTemplates } from "@/lib/data" +import { useUser } from "@clerk/nextjs" +import { ChevronLeft, ChevronRight, Loader2, Search } from "lucide-react" +import { useRouter } from "next/navigation" +import { Button } from "../ui/button" -import useEmblaCarousel from "embla-carousel-react" -import type { EmblaCarouselType } from "embla-carousel" -import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures" import { cn } from "@/lib/utils" +import type { EmblaCarouselType } from "embla-carousel" +import useEmblaCarousel from "embla-carousel-react" +import { WheelGesturesPlugin } from "embla-carousel-wheel-gestures" const formSchema = z.object({ name: z .string() diff --git a/frontend/components/dashboard/projectCard/dropdown.tsx b/frontend/components/dashboard/projectCard/dropdown.tsx index 24a93f8..522d5bc 100644 --- a/frontend/components/dashboard/projectCard/dropdown.tsx +++ b/frontend/components/dashboard/projectCard/dropdown.tsx @@ -1,30 +1,30 @@ -"use client"; +"use client" -import { Sandbox } from "@/lib/types"; -import { Ellipsis, Globe, Lock, Trash2 } from "lucide-react"; +import { Sandbox } from "@/lib/types" +import { Ellipsis, Globe, Lock, Trash2 } from "lucide-react" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; +} from "@/components/ui/dropdown-menu" export default function ProjectCardDropdown({ sandbox, onVisibilityChange, onDelete, }: { - sandbox: Sandbox; - onVisibilityChange: (sandbox: Sandbox) => void; - onDelete: (sandbox: Sandbox) => void; + sandbox: Sandbox + onVisibilityChange: (sandbox: Sandbox) => void + onDelete: (sandbox: Sandbox) => void }) { return ( { - e.preventDefault(); - e.stopPropagation(); + e.preventDefault() + e.stopPropagation() }} className="h-6 w-6 flex items-center justify-center transition-colors bg-transparent hover:bg-muted-foreground/25 rounded-sm outline-foreground" > @@ -33,8 +33,8 @@ export default function ProjectCardDropdown({ { - e.stopPropagation(); - onVisibilityChange(sandbox); + e.stopPropagation() + onVisibilityChange(sandbox) }} className="cursor-pointer" > @@ -52,8 +52,8 @@ export default function ProjectCardDropdown({ { - e.stopPropagation(); - onDelete(sandbox); + e.stopPropagation() + onDelete(sandbox) }} className="!text-destructive cursor-pointer" > @@ -62,5 +62,5 @@ export default function ProjectCardDropdown({ - ); + ) } diff --git a/frontend/components/dashboard/projectCard/index.tsx b/frontend/components/dashboard/projectCard/index.tsx index 9cc3729..a463e84 100644 --- a/frontend/components/dashboard/projectCard/index.tsx +++ b/frontend/components/dashboard/projectCard/index.tsx @@ -1,14 +1,14 @@ "use client" +import { Card } from "@/components/ui/card" +import { projectTemplates } from "@/lib/data" +import { Sandbox } from "@/lib/types" import { AnimatePresence, motion } from "framer-motion" +import { Clock, Globe, Lock } from "lucide-react" import Image from "next/image" +import { useRouter } from "next/navigation" import { useEffect, useState } from "react" import ProjectCardDropdown from "./dropdown" -import { Clock, Globe, Lock } from "lucide-react" -import { Sandbox } from "@/lib/types" -import { Card } from "@/components/ui/card" -import { useRouter } from "next/navigation" -import { projectTemplates } from "@/lib/data" export default function ProjectCard({ children, diff --git a/frontend/components/dashboard/projectCard/revealEffect.tsx b/frontend/components/dashboard/projectCard/revealEffect.tsx index 3786394..fcda16c 100644 --- a/frontend/components/dashboard/projectCard/revealEffect.tsx +++ b/frontend/components/dashboard/projectCard/revealEffect.tsx @@ -1,8 +1,8 @@ -"use client"; -import { cn } from "@/lib/utils"; -import { Canvas, useFrame, useThree } from "@react-three/fiber"; -import React, { useMemo, useRef } from "react"; -import * as THREE from "three"; +"use client" +import { cn } from "@/lib/utils" +import { Canvas, useFrame, useThree } from "@react-three/fiber" +import React, { useMemo, useRef } from "react" +import * as THREE from "three" export const CanvasRevealEffect = ({ animationSpeed = 0.4, @@ -12,12 +12,12 @@ export const CanvasRevealEffect = ({ dotSize, showGradient = true, }: { - animationSpeed?: number; - opacities?: number[]; - colors?: number[][]; - containerClassName?: string; - dotSize?: number; - showGradient?: boolean; + animationSpeed?: number + opacities?: number[] + colors?: number[][] + containerClassName?: string + dotSize?: number + showGradient?: boolean }) => { return (
@@ -41,16 +41,16 @@ export const CanvasRevealEffect = ({
)}
- ); -}; + ) +} interface DotMatrixProps { - colors?: number[][]; - opacities?: number[]; - totalSize?: number; - dotSize?: number; - shader?: string; - center?: ("x" | "y")[]; + colors?: number[][] + opacities?: number[] + totalSize?: number + dotSize?: number + shader?: string + center?: ("x" | "y")[] } const DotMatrix: React.FC = ({ @@ -69,7 +69,7 @@ const DotMatrix: React.FC = ({ colors[0], colors[0], colors[0], - ]; + ] if (colors.length === 2) { colorsArray = [ colors[0], @@ -78,7 +78,7 @@ const DotMatrix: React.FC = ({ colors[1], colors[1], colors[1], - ]; + ] } else if (colors.length === 3) { colorsArray = [ colors[0], @@ -87,7 +87,7 @@ const DotMatrix: React.FC = ({ colors[1], colors[2], colors[2], - ]; + ] } return { @@ -111,8 +111,8 @@ const DotMatrix: React.FC = ({ value: dotSize, type: "uniform1f", }, - }; - }, [colors, opacities, totalSize, dotSize]); + } + }, [colors, opacities, totalSize, dotSize]) return ( = ({ uniforms={uniforms} maxFps={60} /> - ); -}; + ) +} type Uniforms = { [key: string]: { - value: number[] | number[][] | number; - type: string; - }; -}; + value: number[] | number[][] | number + type: string + } +} const ShaderMaterial = ({ source, uniforms, maxFps = 60, }: { - source: string; - hovered?: boolean; - maxFps?: number; - uniforms: Uniforms; + source: string + hovered?: boolean + maxFps?: number + uniforms: Uniforms }) => { - const { size } = useThree(); - const ref = useRef(); - let lastFrameTime = 0; + const { size } = useThree() + const ref = useRef() + let lastFrameTime = 0 useFrame(({ clock }) => { - if (!ref.current) return; - const timestamp = clock.getElapsedTime(); + if (!ref.current) return + const timestamp = clock.getElapsedTime() if (timestamp - lastFrameTime < 1 / maxFps) { - return; + return } - lastFrameTime = timestamp; + lastFrameTime = timestamp - const material: any = ref.current.material; - const timeLocation = material.uniforms.u_time; - timeLocation.value = timestamp; - }); + const material: any = ref.current.material + const timeLocation = material.uniforms.u_time + timeLocation.value = timestamp + }) const getUniforms = () => { - const preparedUniforms: any = {}; + const preparedUniforms: any = {} for (const uniformName in uniforms) { - const uniform: any = uniforms[uniformName]; + const uniform: any = uniforms[uniformName] switch (uniform.type) { case "uniform1f": - preparedUniforms[uniformName] = { value: uniform.value, type: "1f" }; - break; + preparedUniforms[uniformName] = { value: uniform.value, type: "1f" } + break case "uniform3f": preparedUniforms[uniformName] = { value: new THREE.Vector3().fromArray(uniform.value), type: "3f", - }; - break; + } + break case "uniform1fv": - preparedUniforms[uniformName] = { value: uniform.value, type: "1fv" }; - break; + preparedUniforms[uniformName] = { value: uniform.value, type: "1fv" } + break case "uniform3fv": preparedUniforms[uniformName] = { value: uniform.value.map((v: number[]) => new THREE.Vector3().fromArray(v) ), type: "3fv", - }; - break; + } + break case "uniform2f": preparedUniforms[uniformName] = { value: new THREE.Vector2().fromArray(uniform.value), type: "2f", - }; - break; + } + break default: - console.error(`Invalid uniform type for '${uniformName}'.`); - break; + console.error(`Invalid uniform type for '${uniformName}'.`) + break } } - preparedUniforms["u_time"] = { value: 0, type: "1f" }; + preparedUniforms["u_time"] = { value: 0, type: "1f" } preparedUniforms["u_resolution"] = { value: new THREE.Vector2(size.width * 2, size.height * 2), - }; // Initialize u_resolution - return preparedUniforms; - }; + } // Initialize u_resolution + return preparedUniforms + } // Shader material const material = useMemo(() => { @@ -272,33 +272,33 @@ const ShaderMaterial = ({ blending: THREE.CustomBlending, blendSrc: THREE.SrcAlphaFactor, blendDst: THREE.OneFactor, - }); + }) - return materialObject; - }, [size.width, size.height, source]); + return materialObject + }, [size.width, size.height, source]) return ( - ); -}; + ) +} const Shader: React.FC = ({ source, uniforms, maxFps = 60 }) => { return ( - ); -}; + ) +} interface ShaderProps { - source: string; + source: string uniforms: { [key: string]: { - value: number[] | number[][] | number; - type: string; - }; - }; - maxFps?: number; + value: number[] | number[][] | number + type: string + } + } + maxFps?: number } diff --git a/frontend/components/dashboard/projects.tsx b/frontend/components/dashboard/projects.tsx index 9953e19..7c9705d 100644 --- a/frontend/components/dashboard/projects.tsx +++ b/frontend/components/dashboard/projects.tsx @@ -1,16 +1,12 @@ -"use client"; +"use client" -import { Sandbox } from "@/lib/types"; -import ProjectCard from "./projectCard"; -import Image from "next/image"; -import ProjectCardDropdown from "./projectCard/dropdown"; -import { Clock, Globe, Lock } from "lucide-react"; -import Link from "next/link"; -import { Card } from "../ui/card"; -import { deleteSandbox, updateSandbox } from "@/lib/actions"; -import { toast } from "sonner"; -import { useEffect, useState } from "react"; -import { CanvasRevealEffect } from "./projectCard/revealEffect"; +import { deleteSandbox, updateSandbox } from "@/lib/actions" +import { Sandbox } from "@/lib/types" +import Link from "next/link" +import { useEffect, useState } from "react" +import { toast } from "sonner" +import ProjectCard from "./projectCard" +import { CanvasRevealEffect } from "./projectCard/revealEffect" const colors: { [key: string]: number[][] } = { react: [ @@ -21,38 +17,37 @@ const colors: { [key: string]: number[][] } = { [86, 184, 72], [59, 112, 52], ], -}; +} export default function DashboardProjects({ sandboxes, q, }: { - sandboxes: Sandbox[]; - q: string | null; + sandboxes: Sandbox[] + q: string | null }) { - const [deletingId, setDeletingId] = useState(""); + const [deletingId, setDeletingId] = useState("") const onDelete = async (sandbox: Sandbox) => { - setDeletingId(sandbox.id); - toast(`Project ${sandbox.name} deleted.`); - await deleteSandbox(sandbox.id); - }; + setDeletingId(sandbox.id) + toast(`Project ${sandbox.name} deleted.`) + await deleteSandbox(sandbox.id) + } useEffect(() => { if (deletingId) { - setDeletingId(""); + setDeletingId("") } - }, [sandboxes]); + }, [sandboxes]) const onVisibilityChange = async (sandbox: Sandbox) => { - const newVisibility = - sandbox.visibility === "public" ? "private" : "public"; - toast(`Project ${sandbox.name} is now ${newVisibility}.`); + const newVisibility = sandbox.visibility === "public" ? "private" : "public" + toast(`Project ${sandbox.name} is now ${newVisibility}.`) await updateSandbox({ id: sandbox.id, visibility: newVisibility, - }); - }; + }) + } return (
@@ -65,7 +60,7 @@ export default function DashboardProjects({ {sandboxes.map((sandbox) => { if (q && q.length > 0) { if (!sandbox.name.toLowerCase().includes(q.toLowerCase())) { - return null; + return null } } return ( @@ -93,7 +88,7 @@ export default function DashboardProjects({
- ); + ) })}
) : ( @@ -103,5 +98,5 @@ export default function DashboardProjects({ )}
- ); + ) } diff --git a/frontend/components/dashboard/shared.tsx b/frontend/components/dashboard/shared.tsx index 9e5eb03..d9d4d39 100644 --- a/frontend/components/dashboard/shared.tsx +++ b/frontend/components/dashboard/shared.tsx @@ -1,29 +1,27 @@ -import { Sandbox } from "@/lib/types"; import { Table, TableBody, - TableCaption, TableCell, TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; -import Image from "next/image"; -import Button from "../ui/customButton"; -import { ChevronRight } from "lucide-react"; -import Avatar from "../ui/avatar"; -import Link from "next/link"; +} from "@/components/ui/table" +import { ChevronRight } from "lucide-react" +import Image from "next/image" +import Link from "next/link" +import Avatar from "../ui/avatar" +import Button from "../ui/customButton" export default function DashboardSharedWithMe({ shared, }: { shared: { - id: string; - name: string; - type: "react" | "node"; - author: string; - sharedOn: Date; - }[]; + id: string + name: string + type: "react" | "node" + author: string + sharedOn: Date + }[] }) { return (
@@ -86,5 +84,5 @@ export default function DashboardSharedWithMe({
)}
- ); + ) } diff --git a/frontend/components/editor/AIChat/ChatInput.tsx b/frontend/components/editor/AIChat/ChatInput.tsx index a40e0b5..380b6a4 100644 --- a/frontend/components/editor/AIChat/ChatInput.tsx +++ b/frontend/components/editor/AIChat/ChatInput.tsx @@ -1,36 +1,51 @@ -import React from 'react'; -import { Button } from '../../ui/button'; -import { Send, StopCircle } from 'lucide-react'; +import { Send, StopCircle } from "lucide-react" +import { Button } from "../../ui/button" interface ChatInputProps { - input: string; - setInput: (input: string) => void; - isGenerating: boolean; - handleSend: () => void; - handleStopGeneration: () => void; + input: string + setInput: (input: string) => void + isGenerating: boolean + handleSend: () => void + handleStopGeneration: () => void } -export default function ChatInput({ input, setInput, isGenerating, handleSend, handleStopGeneration }: ChatInputProps) { +export default function ChatInput({ + input, + setInput, + isGenerating, + handleSend, + handleStopGeneration, +}: ChatInputProps) { return (
- setInput(e.target.value)} - onKeyPress={(e) => e.key === 'Enter' && !isGenerating && handleSend()} + onKeyPress={(e) => e.key === "Enter" && !isGenerating && handleSend()} className="flex-grow p-2 border rounded-lg min-w-0 bg-input" placeholder="Type your message..." disabled={isGenerating} /> {isGenerating ? ( - ) : ( - )}
- ); + ) } diff --git a/frontend/components/editor/AIChat/ChatMessage.tsx b/frontend/components/editor/AIChat/ChatMessage.tsx index 7eac365..6b0fa72 100644 --- a/frontend/components/editor/AIChat/ChatMessage.tsx +++ b/frontend/components/editor/AIChat/ChatMessage.tsx @@ -1,25 +1,31 @@ -import React, { useState } from 'react'; -import { Button } from '../../ui/button'; -import { ChevronUp, ChevronDown, Copy, Check, CornerUpLeft } from 'lucide-react'; -import ReactMarkdown from 'react-markdown'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; -import remarkGfm from 'remark-gfm'; -import { copyToClipboard, stringifyContent } from './lib/chatUtils'; +import { Check, ChevronDown, ChevronUp, Copy, CornerUpLeft } from "lucide-react" +import React, { useState } from "react" +import ReactMarkdown from "react-markdown" +import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" +import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism" +import remarkGfm from "remark-gfm" +import { Button } from "../../ui/button" +import { copyToClipboard, stringifyContent } from "./lib/chatUtils" interface MessageProps { message: { - role: 'user' | 'assistant'; - content: string; - context?: string; - }; - setContext: (context: string | null) => void; - setIsContextExpanded: (isExpanded: boolean) => void; + role: "user" | "assistant" + content: string + context?: string + } + setContext: (context: string | null) => void + setIsContextExpanded: (isExpanded: boolean) => void } -export default function ChatMessage({ message, setContext, setIsContextExpanded }: MessageProps) { - const [expandedMessageIndex, setExpandedMessageIndex] = useState(null); - const [copiedText, setCopiedText] = useState(null); +export default function ChatMessage({ + message, + setContext, + setIsContextExpanded, +}: MessageProps) { + const [expandedMessageIndex, setExpandedMessageIndex] = useState< + number | null + >(null) + const [copiedText, setCopiedText] = useState(null) const renderCopyButton = (text: any) => ( - ); + ) const askAboutCode = (code: any) => { - const contextString = stringifyContent(code); - setContext(`Regarding this code:\n${contextString}`); - setIsContextExpanded(false); - }; + const contextString = stringifyContent(code) + setContext(`Regarding this code:\n${contextString}`) + setIsContextExpanded(false) + } const renderMarkdownElement = (props: any) => { - const { node, children } = props; - const content = stringifyContent(children); + const { node, children } = props + const content = stringifyContent(children) return (
@@ -59,22 +65,30 @@ export default function ChatMessage({ message, setContext, setIsContextExpanded
- {React.createElement(node.tagName, { - ...props, - className: `${props.className || ''} hover:bg-transparent rounded p-1 transition-colors` - }, children)} + {React.createElement( + node.tagName, + { + ...props, + className: `${ + props.className || "" + } hover:bg-transparent rounded p-1 transition-colors`, + }, + children + )}
- ); - }; + ) + } return (
-
- {message.role === 'user' && ( +
+ {message.role === "user" && (
{renderCopyButton(message.content)}