diff --git a/backend/server/src/index.ts b/backend/server/src/index.ts index 293f802..788c329 100644 --- a/backend/server/src/index.ts +++ b/backend/server/src/index.ts @@ -224,10 +224,11 @@ io.on("connection", async (socket) => { containers[data.sandboxId], sendLoadedEvent ) - await fileManagers[data.sandboxId].initialize() terminalManagers[data.sandboxId] = new TerminalManager( containers[data.sandboxId] ) + console.log(`terminal manager set up for ${data.sandboxId}`) + await fileManagers[data.sandboxId].initialize() } const fileManager = fileManagers[data.sandboxId] @@ -415,6 +416,12 @@ io.on("connection", async (socket) => { socket.on("createTerminal", async (id: string, callback) => { try { await lockManager.acquireLock(data.sandboxId, async () => { + let terminalManager = terminalManagers[data.sandboxId] + if (!terminalManager) { + terminalManager = terminalManagers[data.sandboxId] = + new TerminalManager(containers[data.sandboxId]) + } + await terminalManager.createTerminal(id, (responseString: string) => { socket.emit("terminalResponse", { id, data: responseString }) const port = extractPortNumber(responseString) diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 762a30d..15f1ccf 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -99,6 +99,29 @@ ); /* violet 900 -> bg */ } +.light .gradient-button-bg { + background: radial-gradient( + circle at top, + #262626 0%, + #f5f5f5 50% + ); /* Dark gray -> Light gray */ +} + +.light .gradient-button { + background: radial-gradient( + circle at bottom, + hsl(0, 10%, 25%) -10%, + #9d9d9d 50% + ); /* Light gray -> Almost white */ +} + +.light .gradient-button-bg > div:hover { + background: radial-gradient( + circle at bottom, + hsl(0, 10%, 25%) -10%, + #9d9d9d 80% + ); /* Light gray -> Almost white */ +} .inline-decoration::before { content: "Generate"; color: #525252; diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 9448c07..494079e 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,7 +1,7 @@ -import { ThemeProvider } from "@/components/layout/themeProvider" import { Toaster } from "@/components/ui/sonner" +import { ThemeProvider } from "@/components/ui/theme-provider" import { PreviewProvider } from "@/context/PreviewContext" -import { SocketProvider } from "@/context/SocketContext" +import { SocketProvider } from '@/context/SocketContext' import { ClerkProvider } from "@clerk/nextjs" import { Analytics } from "@vercel/analytics/react" import { GeistMono } from "geist/font/mono" @@ -25,8 +25,7 @@ export default function RootLayout({ diff --git a/frontend/components/dashboard/navbar/index.tsx b/frontend/components/dashboard/navbar/index.tsx index 2f983af..5409236 100644 --- a/frontend/components/dashboard/navbar/index.tsx +++ b/frontend/components/dashboard/navbar/index.tsx @@ -1,4 +1,5 @@ import Logo from "@/assets/logo.svg" +import { ThemeSwitcher } from "@/components/ui/theme-switcher" import { User } from "@/lib/types" import Image from "next/image" import Link from "next/link" @@ -19,6 +20,7 @@ export default function DashboardNavbar({ userData }: { userData: User }) {
+
diff --git a/frontend/components/dashboard/newProject.tsx b/frontend/components/dashboard/newProject.tsx index 0f1a5d2..392db96 100644 --- a/frontend/components/dashboard/newProject.tsx +++ b/frontend/components/dashboard/newProject.tsx @@ -288,7 +288,7 @@ function SearchInput({
diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index 645d6c9..e20a6d0 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -39,6 +39,7 @@ import { Sparkles, TerminalSquare, } from "lucide-react" +import { useTheme } from "next-themes" import React from "react" import { ImperativePanelHandle } from "react-resizable-panels" import { Button } from "../ui/button" @@ -61,7 +62,8 @@ export default function CodeEditor({ }) { //SocketContext functions and effects const { socket, setUserAndSandboxId } = useSocket() - + // theme + const { theme } = useTheme() useEffect(() => { // Ensure userData.id and sandboxData.id are available before attempting to connect if (userData.id && sandboxData.id) { @@ -105,6 +107,7 @@ export default function CodeEditor({ // Editor state const [editorLanguage, setEditorLanguage] = useState("plaintext") + console.log("editor language: ",editorLanguage) const [cursorLine, setCursorLine] = useState(0) const [editorRef, setEditorRef] = useState() @@ -1118,7 +1121,7 @@ export default function CodeEditor({ fixedOverflowWidgets: true, fontFamily: "var(--font-geist-mono)", }} - theme="vs-dark" + theme={theme === "light" ? "vs" : "vs-dark"} value={activeFileContent} /> @@ -1148,7 +1151,7 @@ export default function CodeEditor({ defaultSize={isPreviewCollapsed ? 4 : 20} minSize={25} collapsedSize={isHorizontalLayout ? 20 : 4} - className="p-2 flex flex-col" + className="p-2 flex flex-col gap-2" collapsible onCollapse={() => setIsPreviewCollapsed(true)} onExpand={() => setIsPreviewCollapsed(false)} diff --git a/frontend/components/editor/navbar/index.tsx b/frontend/components/editor/navbar/index.tsx index 0451634..4605dd3 100644 --- a/frontend/components/editor/navbar/index.tsx +++ b/frontend/components/editor/navbar/index.tsx @@ -2,6 +2,7 @@ import Logo from "@/assets/logo.svg" import { Button } from "@/components/ui/button" +import { ThemeSwitcher } from "@/components/ui/theme-switcher" import UserButton from "@/components/ui/userButton" import { Sandbox, User } from "@/lib/types" import { Pencil, Users } from "lucide-react" @@ -79,6 +80,7 @@ export default function Navbar({ ) : null} + diff --git a/frontend/components/editor/terminals/terminal.tsx b/frontend/components/editor/terminals/terminal.tsx index 50bd37d..4790a44 100644 --- a/frontend/components/editor/terminals/terminal.tsx +++ b/frontend/components/editor/terminals/terminal.tsx @@ -6,9 +6,9 @@ import "./xterm.css" import { debounce } from "@/lib/utils" import { Loader2 } from "lucide-react" +import { useTheme } from "next-themes" import { ElementRef, useEffect, useRef } from "react" import { Socket } from "socket.io-client" - export default function EditorTerminal({ socket, id, @@ -22,18 +22,17 @@ export default function EditorTerminal({ setTerm: (term: Terminal) => void visible: boolean }) { - const terminalRef = useRef>(null) + const { theme } = useTheme() + const terminalContainerRef = useRef>(null) const fitAddonRef = useRef(null) useEffect(() => { - if (!terminalRef.current) return + if (!terminalContainerRef.current) return // console.log("new terminal", id, term ? "reusing" : "creating"); const terminal = new Terminal({ cursorBlink: true, - theme: { - background: "#262626", - }, + theme: theme === "light" ? lightTheme : darkTheme, fontFamily: "var(--font-geist-mono)", fontSize: 14, lineHeight: 1.5, @@ -47,14 +46,20 @@ export default function EditorTerminal({ return dispose }, []) + useEffect(() => { + if (term) { + term.options.theme = theme === "light" ? lightTheme : darkTheme + } + }, [theme]) + useEffect(() => { if (!term) return - if (!terminalRef.current) return + if (!terminalContainerRef.current) return if (!fitAddonRef.current) { const fitAddon = new FitAddon() term.loadAddon(fitAddon) - term.open(terminalRef.current) + term.open(terminalContainerRef.current) fitAddon.fit() fitAddonRef.current = fitAddon } @@ -69,7 +74,7 @@ export default function EditorTerminal({ }) const resizeObserver = new ResizeObserver( debounce((entries) => { - if (!fitAddonRef.current || !terminalRef.current) return + if (!fitAddonRef.current || !terminalContainerRef.current) return const entry = entries[0] if (!entry) return @@ -78,8 +83,8 @@ export default function EditorTerminal({ // Only call fit if the size has actually changed if ( - width !== terminalRef.current.offsetWidth || - height !== terminalRef.current.offsetHeight + width !== terminalContainerRef.current.offsetWidth || + height !== terminalContainerRef.current.offsetHeight ) { try { fitAddonRef.current.fit() @@ -91,13 +96,13 @@ export default function EditorTerminal({ ) // start observing for resize - resizeObserver.observe(terminalRef.current) + resizeObserver.observe(terminalContainerRef.current) return () => { disposableOnData.dispose() disposableOnResize.dispose() resizeObserver.disconnect() } - }, [term, terminalRef.current]) + }, [term, terminalContainerRef.current]) useEffect(() => { if (!term) return @@ -116,7 +121,7 @@ export default function EditorTerminal({ return ( <>
@@ -130,3 +135,56 @@ export default function EditorTerminal({ ) } + +const lightTheme = { + foreground: "#2e3436", + background: "#ffffff", + black: "#2e3436", + brightBlack: "#555753", + red: "#cc0000", + brightRed: "#ef2929", + green: "#4e9a06", + brightGreen: "#8ae234", + yellow: "#c4a000", + brightYellow: "#fce94f", + blue: "#3465a4", + brightBlue: "#729fcf", + magenta: "#75507b", + brightMagenta: "#ad7fa8", + cyan: "#06989a", + brightCyan: "#34e2e2", + white: "#d3d7cf", + brightWhite: "#eeeeec", + cursor: "#2e3436", + cursorAccent: "#ffffff", + selectionBackground: "#3465a4", + selectionForeground: "#ffffff", + selectionInactiveBackground: "#264973", +} + +// Dark Theme +const darkTheme = { + foreground: "#f8f8f2", + background: "#0a0a0a", + black: "#21222c", + brightBlack: "#6272a4", + red: "#ff5555", + brightRed: "#ff6e6e", + green: "#50fa7b", + brightGreen: "#69ff94", + yellow: "#f1fa8c", + brightYellow: "#ffffa5", + blue: "#bd93f9", + brightBlue: "#d6acff", + magenta: "#ff79c6", + brightMagenta: "#ff92df", + cyan: "#8be9fd", + brightCyan: "#a4ffff", + white: "#f8f8f2", + brightWhite: "#ffffff", + cursor: "#f8f8f2", + cursorAccent: "#0a0a0a", + selectionBackground: "#264973", + selectionForeground: "#ffffff", + selectionInactiveBackground: "#1a3151", +} diff --git a/frontend/components/landing/index.tsx b/frontend/components/landing/index.tsx index 06e2cb6..e3d8179 100644 --- a/frontend/components/landing/index.tsx +++ b/frontend/components/landing/index.tsx @@ -1,9 +1,10 @@ import Logo from "@/assets/logo.svg" -import XLogo from "@/assets/x.svg" -import Button from "@/components/ui/customButton" +import CustomButton from "@/components/ui/customButton" import { ChevronRight } from "lucide-react" import Image from "next/image" import Link from "next/link" +import { Button } from "../ui/button" +import { ThemeSwitcher } from "../ui/theme-switcher" export default function Landing() { return ( @@ -20,21 +21,37 @@ export default function Landing() { />
- - X Logo - + + +

A Collaborative + AI-Powered Code Environment

-
+

Sandbox is an open-source cloud-based code editing environment with custom AI code autocompletion and real-time collaboration. -

+

- + Go To App -
+
{children}
diff --git a/frontend/components/ui/tab.tsx b/frontend/components/ui/tab.tsx index 0ef38e0..f5bac5c 100644 --- a/frontend/components/ui/tab.tsx +++ b/frontend/components/ui/tab.tsx @@ -28,7 +28,7 @@ export default function Tab({ variant={"secondary"} className={`font-normal select-none ${ selected - ? "bg-neutral-700 hover:bg-neutral-600 text-foreground" + ? "bg-muted-foreground/50 hover:bg-muted-foreground/40 text-foreground" : "text-muted-foreground" }`} > diff --git a/frontend/components/layout/themeProvider.tsx b/frontend/components/ui/theme-provider.tsx similarity index 99% rename from frontend/components/layout/themeProvider.tsx rename to frontend/components/ui/theme-provider.tsx index 19dfaa9..5135dd0 100644 --- a/frontend/components/layout/themeProvider.tsx +++ b/frontend/components/ui/theme-provider.tsx @@ -6,3 +6,4 @@ import { type ThemeProviderProps } from "next-themes/dist/types" export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children} } + \ No newline at end of file diff --git a/frontend/components/ui/theme-switcher.tsx b/frontend/components/ui/theme-switcher.tsx new file mode 100644 index 0000000..9e0bc40 --- /dev/null +++ b/frontend/components/ui/theme-switcher.tsx @@ -0,0 +1,39 @@ +"use client" + +import { Moon, Sun } from "lucide-react" +import { useTheme } from "next-themes" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +export function ThemeSwitcher() { + const { setTheme } = useTheme() + + return ( + + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ) +} diff --git a/frontend/lib/file-extension-to-language.json b/frontend/lib/file-extension-to-language.json index a959737..7aff12b 100644 --- a/frontend/lib/file-extension-to-language.json +++ b/frontend/lib/file-extension-to-language.json @@ -38,6 +38,7 @@ "csl": "xml", "cson": "coffeescript", "csproj": "xml", + "css":"css", "ct": "xml", "ctp": "php", "cxx": "cpp",