File size: 3,638 Bytes
5a0b87c
 
 
 
 
 
 
5dc2f11
2e54937
8c27738
5a0b87c
 
 
 
8c27738
5a0b87c
 
5dc2f11
e9a73cb
5a0b87c
 
 
 
 
5dc2f11
5a0b87c
 
5dc2f11
 
 
 
 
 
 
 
 
 
 
 
5a0b87c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5dc2f11
2e54937
 
 
 
 
 
 
5a0b87c
5dc2f11
 
 
 
 
 
 
 
 
 
5a0b87c
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
"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";

// Dynamic Dashboard import
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);

  // Apply theme globally
  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    // Cleanup if needed
  }, [theme]);

  // Keyboard shortcut for AI
  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 no specific workspace is requested, render the Firebase-style Dashboard Control Plane
  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" },
      // Hard-code Android true for demo purposes to match VSCodeFrame auto-starts
      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: () => {
        // Force VSCodeFrame to remount to fetch the new ports
        setRefreshKey(k => k + 1);
        return "Rebuild complete!";
      },
      error: "Failed to rebuild workspace."
    });
  };

  // Otherwise, render the dedicated VS Code Server instance mapped to this 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>
  );
}