import * as RadixDialog from '@radix-ui/react-dialog'; import { motion } from 'framer-motion'; import { useState, useEffect, useMemo } from 'react'; import { classNames } from '~/utils/classNames'; import { TabManagement } from './TabManagement'; import { TabTile } from '~/components/settings/shared/TabTile'; import { DialogTitle } from '~/components/ui/Dialog'; import type { TabType, TabVisibilityConfig } from '~/components/settings/settings.types'; import { tabConfigurationStore, resetTabConfiguration, updateTabConfiguration, developerModeStore, setDeveloperMode, } from '~/lib/stores/settings'; import { useStore } from '@nanostores/react'; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import DebugTab from '~/components/settings/debug/DebugTab'; import { EventLogsTab } from '~/components/settings/event-logs/EventLogsTab'; import UpdateTab from '~/components/settings/update/UpdateTab'; import DataTab from '~/components/settings/data/DataTab'; import FeaturesTab from '~/components/settings/features/FeaturesTab'; import NotificationsTab from '~/components/settings/notifications/NotificationsTab'; import SettingsTab from '~/components/settings/settings/SettingsTab'; import ProfileTab from '~/components/settings/profile/ProfileTab'; import ConnectionsTab from '~/components/settings/connections/ConnectionsTab'; import { useUpdateCheck, useFeatures, useNotifications, useConnectionStatus, useDebugStatus } from '~/lib/hooks'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import CloudProvidersTab from '~/components/settings/providers/CloudProvidersTab'; import LocalProvidersTab from '~/components/settings/providers/LocalProvidersTab'; import TaskManagerTab from '~/components/settings/task-manager/TaskManagerTab'; import { Switch } from '~/components/ui/Switch'; interface DraggableTabTileProps { tab: TabVisibilityConfig; index: number; moveTab: (dragIndex: number, hoverIndex: number) => void; onClick: () => void; isActive: boolean; hasUpdate: boolean; statusMessage: string; description: string; isLoading?: boolean; } const TAB_DESCRIPTIONS: Record = { profile: 'Manage your profile and account settings', settings: 'Configure application preferences', notifications: 'View and manage your notifications', features: 'Explore new and upcoming features', data: 'Manage your data and storage', 'cloud-providers': 'Configure cloud AI providers and models', 'local-providers': 'Configure local AI providers and models', connection: 'Check connection status and settings', debug: 'Debug tools and system information', 'event-logs': 'View system events and logs', update: 'Check for updates and release notes', 'task-manager': 'Monitor system resources and processes', }; const DraggableTabTile = ({ tab, index, moveTab, onClick, isActive, hasUpdate, statusMessage, description, isLoading, }: DraggableTabTileProps) => { const [{ isDragging }, drag] = useDrag({ type: 'tab', item: { index }, collect: (monitor) => ({ isDragging: monitor.isDragging(), }), }); const [, drop] = useDrop({ accept: 'tab', hover: (item: { index: number }) => { if (item.index === index) { return; } moveTab(item.index, index); item.index = index; }, }); const dragDropRef = (node: HTMLDivElement | null) => { if (node) { drag(drop(node)); } }; return (
); }; interface DeveloperWindowProps { open: boolean; onClose: () => void; } export const DeveloperWindow = ({ open, onClose }: DeveloperWindowProps) => { const [activeTab, setActiveTab] = useState(null); const [loadingTab, setLoadingTab] = useState(null); const tabConfiguration = useStore(tabConfigurationStore); const [showTabManagement, setShowTabManagement] = useState(false); const developerMode = useStore(developerModeStore); const [profile, setProfile] = useState(() => { const saved = localStorage.getItem('bolt_user_profile'); return saved ? JSON.parse(saved) : { avatar: null, notifications: true }; }); // Handle developer mode change const handleDeveloperModeChange = (checked: boolean) => { setDeveloperMode(checked); if (!checked) { onClose(); } }; // Ensure developer mode is true when window is opened useEffect(() => { if (open) { setDeveloperMode(true); } }, [open]); // Listen for profile changes useEffect(() => { const handleStorageChange = (e: StorageEvent) => { if (e.key === 'bolt_user_profile') { const newProfile = e.newValue ? JSON.parse(e.newValue) : { avatar: null, notifications: true }; setProfile(newProfile); } }; window.addEventListener('storage', handleStorageChange); return () => window.removeEventListener('storage', handleStorageChange); }, []); // Status hooks const { hasUpdate, currentVersion, acknowledgeUpdate } = useUpdateCheck(); const { hasNewFeatures, unviewedFeatures, acknowledgeAllFeatures } = useFeatures(); const { hasUnreadNotifications, unreadNotifications, markAllAsRead } = useNotifications(); const { hasConnectionIssues, currentIssue, acknowledgeIssue } = useConnectionStatus(); const { hasActiveWarnings, activeIssues, acknowledgeAllIssues } = useDebugStatus(); // Ensure tab configuration is properly initialized useEffect(() => { if (!tabConfiguration || !tabConfiguration.userTabs || !tabConfiguration.developerTabs) { console.warn('Tab configuration is invalid in DeveloperWindow, resetting to defaults'); resetTabConfiguration(); } else { // Validate tab configuration structure const isValid = tabConfiguration.userTabs.every( (tab) => tab && typeof tab.id === 'string' && typeof tab.visible === 'boolean' && typeof tab.window === 'string' && typeof tab.order === 'number', ) && tabConfiguration.developerTabs.every( (tab) => tab && typeof tab.id === 'string' && typeof tab.visible === 'boolean' && typeof tab.window === 'string' && typeof tab.order === 'number', ); if (!isValid) { console.warn('Tab configuration is malformed in DeveloperWindow, resetting to defaults'); resetTabConfiguration(); } } }, [tabConfiguration]); const handleBack = () => { if (showTabManagement) { setShowTabManagement(false); } else if (activeTab) { setActiveTab(null); } }; // Only show tabs that are assigned to the developer window AND are visible const visibleDeveloperTabs = useMemo(() => { console.log('Filtering developer tabs with configuration:', tabConfiguration); if (!tabConfiguration?.developerTabs || !Array.isArray(tabConfiguration.developerTabs)) { console.warn('Invalid tab configuration, using empty array'); return []; } return tabConfiguration.developerTabs .filter((tab) => { if (!tab || typeof tab.id !== 'string') { console.warn('Invalid tab entry:', tab); return false; } // Hide notifications tab if notifications are disabled if (tab.id === 'notifications' && !profile.notifications) { console.log('Hiding notifications tab due to disabled notifications'); return false; } // Ensure the tab has the required properties if (typeof tab.visible !== 'boolean' || typeof tab.window !== 'string' || typeof tab.order !== 'number') { console.warn('Tab missing required properties:', tab); return false; } // Only show tabs that are explicitly visible and assigned to the developer window const isVisible = tab.visible && tab.window === 'developer'; console.log(`Tab ${tab.id} visibility:`, isVisible); return isVisible; }) .sort((a: TabVisibilityConfig, b: TabVisibilityConfig) => { const orderA = typeof a.order === 'number' ? a.order : 0; const orderB = typeof b.order === 'number' ? b.order : 0; return orderA - orderB; }); }, [tabConfiguration, profile.notifications]); console.log('Filtered visible developer tabs:', visibleDeveloperTabs); const moveTab = (dragIndex: number, hoverIndex: number) => { const draggedTab = visibleDeveloperTabs[dragIndex]; const targetTab = visibleDeveloperTabs[hoverIndex]; console.log('Moving developer tab:', { draggedTab, targetTab }); // Update the order of the dragged and target tabs const updatedDraggedTab = { ...draggedTab, order: targetTab.order }; const updatedTargetTab = { ...targetTab, order: draggedTab.order }; // Update both tabs in the store updateTabConfiguration(updatedDraggedTab); updateTabConfiguration(updatedTargetTab); }; const handleTabClick = (tabId: TabType) => { // Don't allow clicking notifications tab if disabled if (tabId === 'notifications' && !profile.notifications) { return; } setLoadingTab(tabId); setActiveTab(tabId); // Acknowledge the status based on tab type switch (tabId) { case 'update': acknowledgeUpdate(); break; case 'features': acknowledgeAllFeatures(); break; case 'notifications': markAllAsRead(); break; case 'connection': acknowledgeIssue(); break; case 'debug': acknowledgeAllIssues(); break; } // Clear loading state after a short delay setTimeout(() => { setLoadingTab(null); }, 500); }; const getTabComponent = () => { switch (activeTab) { case 'profile': return ; case 'settings': return ; case 'notifications': return ; case 'features': return ; case 'data': return ; case 'cloud-providers': return ; case 'local-providers': return ; case 'connection': return ; case 'debug': return ; case 'event-logs': return ; case 'update': return ; case 'task-manager': return ; default: return null; } }; const getTabUpdateStatus = (tabId: TabType): boolean => { switch (tabId) { case 'update': return hasUpdate; case 'features': return hasNewFeatures; case 'notifications': return hasUnreadNotifications; case 'connection': return hasConnectionIssues; case 'debug': return hasActiveWarnings; default: return false; } }; const getStatusMessage = (tabId: TabType): string => { switch (tabId) { case 'update': return `New update available (v${currentVersion})`; case 'features': return `${unviewedFeatures.length} new feature${unviewedFeatures.length === 1 ? '' : 's'} to explore`; case 'notifications': return `${unreadNotifications.length} unread notification${unreadNotifications.length === 1 ? '' : 's'}`; case 'connection': return currentIssue === 'disconnected' ? 'Connection lost' : currentIssue === 'high-latency' ? 'High latency detected' : 'Connection issues detected'; case 'debug': { const warnings = activeIssues.filter((i) => i.type === 'warning').length; const errors = activeIssues.filter((i) => i.type === 'error').length; return `${warnings} warning${warnings === 1 ? '' : 's'}, ${errors} error${errors === 1 ? '' : 's'}`; } default: return ''; } }; return (
{/* Header */}
{activeTab || showTabManagement ? ( ) : ( )} {showTabManagement ? 'Tab Management' : activeTab ? 'Developer Tools' : 'Developer Settings'}
{!activeTab && !showTabManagement && ( setShowTabManagement(true)} className="flex items-center space-x-2 px-3 py-1.5 rounded-lg bg-gray-100 dark:bg-gray-800 hover:bg-purple-500/10 dark:hover:bg-purple-500/20 group transition-all duration-200" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} >
Manage Tabs )}
handleTabClick('profile')} >
Profile handleTabClick('settings')} >
Settings {profile.notifications && ( <> handleTabClick('notifications')} >
Notifications {hasUnreadNotifications && ( {unreadNotifications.length} )} )} handleTabClick('task-manager')} >
Task Manager
Close
{/* Content */}
{showTabManagement ? ( ) : activeTab ? ( getTabComponent() ) : (
{visibleDeveloperTabs.map((tab: TabVisibilityConfig, index: number) => ( handleTabClick(tab.id)} isActive={activeTab === tab.id} hasUpdate={getTabUpdateStatus(tab.id)} statusMessage={getStatusMessage(tab.id)} description={TAB_DESCRIPTIONS[tab.id]} isLoading={loadingTab === tab.id} /> ))}
)}
); };