| "use client"; |
|
|
| import { useState, useEffect } from "react"; |
| import dynamic from "next/dynamic"; |
| import { useSearchParams } from "next/navigation"; |
| import { Toaster, toast } from "sonner"; |
| import { VSCodeFrame } from "@/components/workspace/VSCodeFrame"; |
| import { AIAssistantSidebar } from "@/components/workspace/AIAssistantSidebar"; |
| import { WorkspaceHeader } from "@/components/workspace/WorkspaceHeader"; |
| import type { Session } from "next-auth"; |
|
|
| |
| const Dashboard = dynamic(() => import("@/components/dashboard/Dashboard"), { ssr: false }); |
|
|
| export default function IDEClient({ session }: { session: Session | null }) { |
| const searchParams = useSearchParams(); |
| const workspaceParam = searchParams?.get("workspace"); |
| const [isAiOpen, setIsAiOpen] = useState(false); |
| const [theme] = useState<"dark" | "light">("dark"); |
| const [refreshKey, setRefreshKey] = useState(0); |
|
|
| |
| useEffect(() => { |
| document.documentElement.setAttribute("data-theme", theme); |
| |
| }, [theme]); |
|
|
| |
| useEffect(() => { |
| const handleKeyDown = (e: KeyboardEvent) => { |
| if ((e.ctrlKey || e.metaKey) && e.key === "i") { |
| e.preventDefault(); |
| setIsAiOpen(prev => !prev); |
| } |
| }; |
| window.addEventListener("keydown", handleKeyDown); |
| return () => window.removeEventListener("keydown", handleKeyDown); |
| }, []); |
|
|
| |
| if (!workspaceParam) { |
| return ( |
| <div data-theme={theme} className="h-dvh flex flex-col overflow-hidden bg-(--bg)"> |
| <div className="flex-1 overflow-hidden"> |
| <Dashboard /> |
| </div> |
| <Toaster position="bottom-right" theme={theme} richColors /> |
| </div> |
| ); |
| } |
|
|
| const handleRebuild = async () => { |
| const promise = fetch("/api/workspace", { |
| method: "POST", |
| headers: { "Content-Type": "application/json" }, |
| |
| body: JSON.stringify({ action: "rebuild", id: workspaceParam, image: 'codercom/code-server:latest', withAndroidEmulator: true }) |
| }).then(async res => { |
| const data = await res.json(); |
| if (!data.success) throw new Error(data.error); |
| return data; |
| }); |
|
|
| toast.promise(promise, { |
| loading: "Rebuilding Environment... This may take a minute.", |
| success: () => { |
| |
| setRefreshKey(k => k + 1); |
| return "Rebuild complete!"; |
| }, |
| error: "Failed to rebuild workspace." |
| }); |
| }; |
|
|
| |
| return ( |
| <div data-theme={theme} className="h-dvh w-screen flex flex-col bg-(--bg) overflow-hidden relative"> |
| <WorkspaceHeader |
| workspaceId={workspaceParam} |
| session={session} |
| isAiOpen={isAiOpen} |
| onAiToggle={() => setIsAiOpen(!isAiOpen)} |
| onRebuild={handleRebuild} |
| /> |
| |
| <div className="flex-1 flex relative w-full h-full overflow-hidden"> |
| <main className={`flex-1 relative transition-all duration-300 ${isAiOpen ? "mr-0 md:mr-80 lg:mr-96" : "mr-0"}`}> |
| <VSCodeFrame key={refreshKey} workspaceId={workspaceParam} /> |
| </main> |
| |
| <AIAssistantSidebar |
| workspaceName={workspaceParam} |
| isOpen={isAiOpen} |
| onClose={() => setIsAiOpen(false)} |
| /> |
| </div> |
| |
| <Toaster position="bottom-right" theme={theme} richColors /> |
| </div> |
| ); |
| } |
|
|