Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 74 additions & 28 deletions modules/playground/components/MainPlaygroundPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
AlertCircle,
FolderOpen,
} from "lucide-react";

import { CollaborationAvatars } from "@/modules/playground/components/collaboration-avatars";
import { TemplateFileTree } from "@/modules/playground/components/playground-explorer";
import { usePlayground } from "@/modules/playground/hooks/usePlayground";
Expand All @@ -37,7 +38,7 @@ import { useParams } from "next/navigation";
import WebContainerPreview from "@/modules/webcontainers/components/webcontainer-preview";
import { useWebContainer } from "@/modules/webcontainers/hooks/useWebContainer";
import { useFileExplorer } from "@/modules/playground/hooks/useFileExplorer";

import { fetchCollabToken, getOrCreateYDoc } from "@/lib/yjs";
import {
TemplateFile,
TemplateFolder,
Expand Down Expand Up @@ -77,11 +78,13 @@ const MainPlaygroundPage = ({ initialData, id }: MainPlaygroundPageProps) => {
const [templateData, setTemplateDataState] = useState(parsedTemplate);
const [error] = useState<string | null>(null);
const { saveTemplateData } = usePlayground(id);
const { toggleChat } = useAI();
const [isPreviewVisible, setIsPreviewVisible] = useState(false);
const [showAISettings, setShowAISettings] = useState(false);
const [isCommandPaletteOpen, setIsCommandPaletteOpen] = useState(false);
const [isDeployDialogOpen, setIsDeployDialogOpen] = useState(false);
const [cursorPosition, setCursorPosition] = useState({ line: 1, col: 1 });
const [collaboratorCount, setCollaboratorCount] = useState(0);
const sidebar = useSidebar();

const {
Expand Down Expand Up @@ -113,35 +116,78 @@ const [cursorPosition, setCursorPosition] = useState({ line: 1, col: 1 });


useEffect(() => {
setPlaygroundId(id);
if (templateData && !openFiles.length) {
setTemplateData(templateData);
setPlaygroundId(id);
if (templateData && !openFiles.length) {
setTemplateData(templateData);
}
}, [id, setPlaygroundId, templateData, setTemplateData, openFiles.length]);

// Collaborator tracking
useEffect(() => {
if (!id) return;

let cancelled = false;
let cleanup = () => {};

void (async () => {
try {
const token = await fetchCollabToken(id);
const { provider } = getOrCreateYDoc(id, token);

if (cancelled) return;

const updateCollaborators = () => {
if (cancelled) return;
const states = Array.from(provider.awareness.getStates().values());
const activeUsers = states
.filter((s: any) => s.user)
.map((s: any) => s.user);
const uniqueUsers = Array.from(
new Map(activeUsers.map((u: any) => [u.name, u])).values()
);
setCollaboratorCount(uniqueUsers.length);
};

provider.awareness.on("change", updateCollaborators);
updateCollaborators();

cleanup = () => {
provider.awareness.off("change", updateCollaborators);
};
} catch (error) {
console.error("Failed to track collaborators:", error);
}
}, [id, setPlaygroundId, templateData, setTemplateData, openFiles.length]);
})();

// Auto-open default file when preview is shown if no file is open
useEffect(() => {
if (isPreviewVisible && !activeFileId && templateData) {
const findDefaultFile = (items: any[]): TemplateFile | null => {
for (const item of items) {
if (!("folderName" in item)) {
if (["App.tsx", "App.jsx", "index.tsx", "index.jsx", "index.js", "main.tsx", "main.js", "index.html"].includes(`${item.filename}.${item.fileExtension}`)) {
return item;
}
} else {
const found = findDefaultFile(item.items);
if (found) return found;
return () => {
cancelled = true;
cleanup();
};
}, [id]);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Auto-open default file when preview is shown if no file is open
useEffect(() => {
if (isPreviewVisible && !activeFileId && templateData) {
const findDefaultFile = (items: any[]): TemplateFile | null => {
for (const item of items) {
if (!("folderName" in item)) {
if (["App.tsx", "App.jsx", "index.tsx", "index.jsx", "index.js", "main.tsx", "main.js", "index.html"].includes(`${item.filename}.${item.fileExtension}`)) {
return item;
}
} else {
const found = findDefaultFile(item.items);
if (found) return found;
}
return null;
};

const defaultFile = findDefaultFile(templateData.items);
if (defaultFile) {
openFile(defaultFile);
}
return null;
};

const defaultFile = findDefaultFile(templateData.items);
if (defaultFile) {
openFile(defaultFile);
}
}, [isPreviewVisible, activeFileId, templateData, openFile]);
}
}, [isPreviewVisible, activeFileId, templateData, openFile]);

// Create wrapper functions that pass saveTemplateData
const wrappedHandleAddFile = useCallback(
Expand Down Expand Up @@ -301,7 +347,7 @@ if (!playgroundData && !templateData && !error) {
handleDownloadZip={handleDownloadZip}
setShowAISettings={setShowAISettings}
closeAllFiles={closeAllFiles}
toggleAIChat={() => useAI.getState().toggleChat()}
toggleAIChat={toggleChat}
/>

{/* ==== CONTENT ==== */}
Expand Down Expand Up @@ -366,7 +412,7 @@ if (!playgroundData && !templateData && !error) {
<WelcomeScreen
projectTitle={playgroundData?.title}
onTogglePreview={() => setIsPreviewVisible(true)}
onOpenAI={() => useAI.getState().toggleChat()}
onOpenAI={toggleChat}
onDownload={handleDownloadZip}
onOpenCommandPalette={() => setIsCommandPaletteOpen(true)}
/>
Expand All @@ -378,7 +424,7 @@ if (!playgroundData && !templateData && !error) {
activeFile={activeFile}
cursorPosition={cursorPosition}
containerStatus={containerStatus}
collaboratorCount={0}
collaboratorCount={collaboratorCount}
openFileCount={openFiles.length}
/>
</div>
Expand All @@ -402,7 +448,7 @@ if (!playgroundData && !templateData && !error) {
onSaveAll={handleSaveAll}
onDownload={handleDownloadZip}
onTogglePreview={() => setIsPreviewVisible((prev) => !prev)}
onToggleAI={() => useAI.getState().toggleChat()}
onToggleAI={toggleChat}
onToggleSidebar={() => sidebar.toggleSidebar()}
onOpenSettings={() => setShowAISettings(true)}
onCloseAllFiles={closeAllFiles}
Expand Down