'use client'; import React, { useState, useEffect, useCallback } from 'react'; import { LogOut, FileCode, FilePlus2, SquareTerminal, TvMinimalPlay } from 'lucide-react'; import Cookies from 'js-cookie'; import { usePrivy, useWallets } from '@privy-io/react-auth'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/common/alert-dialog"; import useDnDStore from '@/stores/useDnDStore'; import Avatar, { genConfig } from 'react-nice-avatar'; import { generateDisplayCode, generateExecutionCode } from '@/utils/exportUtils'; import { NICKNAMES } from '@/utils/nicknames'; import SlidingModal from './SlidingModal'; interface UtilBarProps { onTutorialClick: () => void; } const UtilBar = ({ onTutorialClick }: UtilBarProps) => { // State management for UI elements const [showLogoutDialog, setShowLogoutDialog] = useState(false); const [showProfileMenu, setShowProfileMenu] = useState(false); const [showNewFileDialog, setShowNewFileDialog] = useState(false); const [userName, setUserName] = useState('User'); const [avatarConfig, setAvatarConfig] = useState(() => genConfig()); // State management for code execution const [showEditor, setShowEditor] = useState(false); const [output, setOutput] = useState(''); const [error, setError] = useState(null); const [isExecuting, setIsExecuting] = useState(false); // Global state and authentication hooks const { logout: privyLogout } = usePrivy(); const { wallets } = useWallets(); const { nodes, edges, clearNodes } = useDnDStore(); // Initialize random username and avatar on component mount useEffect(() => { const randomIndex = Math.floor(Math.random() * NICKNAMES.length); const selectedName = NICKNAMES[randomIndex]; setUserName(selectedName); setAvatarConfig(genConfig(selectedName)); }, []); // File operations handlers const handleNewFile = useCallback(() => { clearNodes(); setShowNewFileDialog(false); }, [clearNodes]); const handleDownload = useCallback(() => { // Generate masked version of code for download const pythonCode = generateDisplayCode(nodes, edges); const blob = new Blob([pythonCode], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = 'dotflow.py'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); }, [nodes, edges]); // Python code execution handler const handleRunPython = useCallback(async () => { setIsExecuting(true); setError(null); setShowEditor(true); setOutput('Executing...'); // Set initial executing state try { // Generate code with real credentials for execution const executionCode = generateExecutionCode(nodes, edges); const response = await fetch('/api/python', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ code: executionCode }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data: { output: string } = await response.json(); // Sanitize the output to remove sensitive information const sanitizedOutput = data.output .replace(/sk-proj-[a-zA-Z0-9-]+/g, '[API_KEY]') .replace(/asst_[a-zA-Z0-9]+/g, '[ASSISTANT_ID]'); setOutput(sanitizedOutput); } catch (err) { const errorMessage = err instanceof Error ? err.message : 'An unknown error occurred'; setError(errorMessage); setOutput(errorMessage); console.error('Error running Python code:', err); } finally { setIsExecuting(false); } }, [nodes, edges]); // Authentication handlers const handleLogout = async () => { try { // Disconnect all connected wallets for (const wallet of wallets) { try { await wallet.disconnect(); } catch (e) { console.error('Error disconnecting wallet:', e); } } // Clear all authentication related cookies and storage Cookies.remove('privy-authenticated', { path: '/' }); localStorage.removeItem('useremail'); localStorage.removeItem('privy:embedded-wallet:iframe-ready'); localStorage.removeItem('privy:embedded-wallet:ready'); await privyLogout(); window.location.href = '/'; } catch (error) { console.error('Logout error:', error); window.location.href = '/'; } }; // Tooltip component for action buttons const IconWithTooltip: React.FC<{ children: React.ReactNode; tooltip: string }> = ({ children, tooltip }) => (
{children}
{tooltip}
); // Action buttons configuration const actions = [ { onClick: () => setShowNewFileDialog(true), tooltip: "New File", icon: }, { onClick: handleDownload, tooltip: "Export as Python", icon: }, { onClick: handleRunPython, tooltip: "Run", icon: }, { onClick: onTutorialClick, tooltip: "Tutorial", icon: }, ]; return (
{/* Action buttons */}
{actions.map((action, index) => ( {index < actions.length - 1 &&
} ))}
{/* Profile menu */}
{showProfileMenu && (
setShowProfileMenu(true)} onMouseLeave={() => setShowProfileMenu(false)} >
)}
{/* Logout confirmation dialog */} Ready to leave? Logging out will disconnect your wallets and end your current session. You can always log back in later. Stay Logout {/* New file confirmation dialog */} Create New File? This will clear your current work. Are you sure you want to continue? Cancel Continue {/* Code editor modal */} setShowEditor(false)} pythonCode={generateDisplayCode(nodes, edges)} output={error || output} isExecuting={isExecuting} />
); }; export default UtilBar;