import React from "react"; import { useRouteError, isRouteErrorResponse, Outlet, useLocation, } from "react-router"; import { useTranslation } from "react-i18next"; import { I18nKey } from "#/i18n/declaration"; import i18n from "#/i18n"; import { useConfig } from "#/hooks/query/use-config"; import { Sidebar } from "#/components/features/sidebar/sidebar"; import { SidebarMobileNavProvider } from "#/components/features/sidebar/sidebar-mobile-nav-context"; import { SidebarMobileMenuBar } from "#/components/features/sidebar/sidebar-mobile-menu-bar"; import { useSettings } from "#/hooks/query/use-settings"; import { useEnsureActiveProfile } from "#/hooks/use-ensure-active-profile"; import { useMigrateEnabledSkills } from "#/hooks/use-migrate-enabled-skills"; import { useSyncTelemetryConsent } from "#/hooks/use-sync-telemetry-consent"; import { useSyncAutomationTelemetryConsent } from "#/hooks/use-sync-automation-telemetry-consent"; import { useTelemetryIdentity } from "#/hooks/use-telemetry-identity"; import { LoadingSpinner } from "#/components/shared/loading-spinner"; import { useAppTitle } from "#/hooks/use-app-title"; import { ReactRouterNavigationProvider } from "./react-router-navigation-provider"; import { OnboardingHost } from "#/components/features/onboarding"; import { isOnboardingPreviewActive } from "#/components/features/onboarding/onboarding-preview"; import { CanvasExtensionsRuntimeProvider } from "#/components/features/canvas-extensions/canvas-extensions-runtime"; const EnvironmentSwitchOverlay = React.lazy( () => import("#/components/features/backends/environment-switch-overlay"), ); const AlertBanner = React.lazy(() => import("#/components/features/alerts/alert-banner").then((m) => ({ default: m.AlertBanner, })), ); const CommandMenu = React.lazy(() => import("#/components/features/command-menu/command-menu").then((m) => ({ default: m.CommandMenu, })), ); export function ErrorBoundary() { const error = useRouteError(); const { t } = useTranslation("openhands"); if (isRouteErrorResponse(error)) { return (

{error.status}

{error.statusText}

          {error.data instanceof Object
            ? JSON.stringify(error.data)
            : error.data}
        
); } if (error instanceof Error) { return (

{t(I18nKey.ERROR$GENERIC)}

{error.message}
); } return (

{t(I18nKey.ERROR$UNKNOWN)}

); } export default function MainApp() { const location = useLocation(); const appTitle = useAppTitle(); const { data: settings } = useSettings(); const config = useConfig(); useSyncAutomationTelemetryConsent(); useSyncTelemetryConsent(); useTelemetryIdentity(); // Local-mode policy: keep a profile active so a usable LLM is always selected. useEnsureActiveProfile(); // One-shot move from the catalog deny-list to an explicit allow-list. useMigrateEnabledSkills(); React.useEffect(() => { if (settings?.language) { i18n.changeLanguage(settings.language); } }, [settings?.language]); if (config.isLoading) { return (
); } // Conversation + full-screen panel routes put the mobile menu control in the // chat / panel header; omit the extra top row so we don't duplicate chrome. const hideMobileSidebarMenuBar = /^\/conversations\/[^/]+/.test( location.pathname, ); const showOnboardingPreview = isOnboardingPreviewActive(location.search); return (
{appTitle}
{!hideMobileSidebarMenuBar ? : null} {config.data && (config.data.maintenance_start_time || (config.data.faulty_models && config.data.faulty_models.length > 0) || config.data.error_message) && ( )}
{showOnboardingPreview ? : null}
); }