import React, { useCallback, useEffect, useState } from 'react'; import ChatInterface from '../../chat/view/ChatInterface'; import FileTree from '../../file-tree/view/FileTree'; import StandaloneShell from '../../standalone-shell/view/StandaloneShell'; import GitPanel from '../../git-panel/view/GitPanel'; import PluginTabContent from '../../plugins/view/PluginTabContent'; import { BrowserUsePanel } from '../../browser-use'; import { ComputerUsePanel } from '../../computer-use'; import type { MainContentProps } from '../types/types'; import { useTaskMaster } from '../../../contexts/TaskMasterContext'; import { usePaletteOpsRegister } from '../../../contexts/PaletteOpsContext'; import { useTasksSettings } from '../../../contexts/TasksSettingsContext'; import { useUiPreferences } from '../../../hooks/useUiPreferences'; import { COMPUTER_USE_MENUS_ENABLED } from '../../../constants/featureFlags'; import { authenticatedFetch } from '../../../utils/api'; import { useEditorSidebar } from '../../code-editor/hooks/useEditorSidebar'; import EditorSidebar from '../../code-editor/view/EditorSidebar'; import type { Project } from '../../../types/app'; import { TaskMasterPanel } from '../../task-master'; import MainContentHeader from './subcomponents/MainContentHeader'; import MainContentStateView from './subcomponents/MainContentStateView'; import ErrorBoundary from './ErrorBoundary'; type TaskMasterContextValue = { currentProject?: Project | null; setCurrentProject?: ((project: Project) => void) | null; }; type TasksSettingsContextValue = { tasksEnabled: boolean; isTaskMasterInstalled: boolean | null; isTaskMasterReady: boolean | null; }; function MainContent({ selectedProject, selectedSession, activeTab, setActiveTab, ws, sendMessage, isMobile, onMenuClick, isLoading, onInputFocusChange, onSessionProcessing, onSessionIdle, processingSessions, onNavigateToSession, onSessionEstablished, onShowSettings, externalMessageUpdate, newSessionTrigger, }: MainContentProps) { const { preferences } = useUiPreferences(); const { autoExpandTools, showRawParameters, showThinking, autoScrollToBottom, sendByCtrlEnter } = preferences; const { currentProject, setCurrentProject } = useTaskMaster() as TaskMasterContextValue; const { tasksEnabled, isTaskMasterInstalled } = useTasksSettings() as TasksSettingsContextValue; const [browserUseEnabled, setBrowserUseEnabled] = useState(false); const [computerUseEnabled, setComputerUseEnabled] = useState(undefined); const shouldShowTasksTab = Boolean(tasksEnabled && isTaskMasterInstalled); const shouldShowBrowserTab = browserUseEnabled; const shouldShowComputerTab = COMPUTER_USE_MENUS_ENABLED && computerUseEnabled === true; const { editingFile, editorWidth, editorExpanded, hasManualWidth, resizeHandleRef, handleFileOpen, handleCloseEditor, handleToggleEditorExpand, handleResizeStart, } = useEditorSidebar({ selectedProject, isMobile, }); useEffect(() => { // Identify projects by DB `projectId`; the TaskMaster context uses the // same identifier to key its internal maps. const selectedProjectId = selectedProject?.projectId; const currentProjectId = currentProject?.projectId; if (selectedProject && selectedProjectId !== currentProjectId) { setCurrentProject?.(selectedProject); } }, [selectedProject, currentProject?.projectId, setCurrentProject]); useEffect(() => { if (!shouldShowTasksTab && activeTab === 'tasks') { setActiveTab('chat'); } }, [shouldShowTasksTab, activeTab, setActiveTab]); const loadBrowserUseSettings = useCallback(async () => { try { const response = await authenticatedFetch('/api/browser-use/settings'); const data = await response.json(); setBrowserUseEnabled(Boolean(response.ok && data?.success !== false && data?.data?.settings?.enabled)); } catch { setBrowserUseEnabled(false); } }, []); useEffect(() => { void loadBrowserUseSettings(); window.addEventListener('browserUseSettingsChanged', loadBrowserUseSettings); return () => window.removeEventListener('browserUseSettingsChanged', loadBrowserUseSettings); }, [loadBrowserUseSettings]); useEffect(() => { if (!shouldShowBrowserTab && activeTab === 'browser') { setActiveTab('chat'); } }, [shouldShowBrowserTab, activeTab, setActiveTab]); const loadComputerUseSettings = useCallback(async () => { try { const [settingsResponse, statusResponse] = await Promise.allSettled([ authenticatedFetch('/api/computer-use/settings'), authenticatedFetch('/api/computer-use/status'), ]); const settingsRes = settingsResponse.status === 'fulfilled' ? settingsResponse.value : null; const statusRes = statusResponse.status === 'fulfilled' ? statusResponse.value : null; const readJson = async (response: Response | null) => { if (!response) return null; try { return await response.json(); } catch { return null; } }; const settingsData = await readJson(settingsRes); const statusData = await readJson(statusRes); const runtime = statusData?.data?.runtime; const settingsUsable = Boolean(settingsRes?.ok && settingsData?.success !== false); const statusUsable = Boolean(statusRes?.ok && statusData?.success !== false); const settingsEnabled = Boolean( settingsUsable && settingsData?.data?.settings?.enabled ); const cloudEnabled = Boolean( statusUsable && runtime === 'cloud' && statusData?.data?.enabled ); if (runtime === 'cloud') { setComputerUseEnabled(cloudEnabled); } else if (settingsUsable) { setComputerUseEnabled(settingsEnabled); } else if (statusUsable) { setComputerUseEnabled(Boolean(statusData?.data?.enabled)); } } catch { // Keep the current tab availability on transient status/settings failures. } }, []); useEffect(() => { void loadComputerUseSettings(); window.addEventListener('computerUseSettingsChanged', loadComputerUseSettings); return () => window.removeEventListener('computerUseSettingsChanged', loadComputerUseSettings); }, [loadComputerUseSettings]); useEffect(() => { if (!shouldShowComputerTab && activeTab === 'computer') { setActiveTab('chat'); } }, [shouldShowComputerTab, activeTab, setActiveTab]); usePaletteOpsRegister({ openFile: (filePath: string) => { setActiveTab('files'); handleFileOpen(filePath); }, }); if (isLoading) { return ; } if (!selectedProject) { return ; } return (
setActiveTab('tasks') : null} />
{activeTab === 'files' && (
)} {activeTab === 'shell' && (
)} {activeTab === 'git' && (
)} {shouldShowTasksTab && } {shouldShowBrowserTab && activeTab === 'browser' && (
)} {shouldShowComputerTab && activeTab === 'computer' && (
)} {activeTab.startsWith('plugin:') && (
)}
); } export default React.memo(MainContent);