import React, { forwardRef, useEffect, useMemo, useRef, type ForwardedRef } from 'react'; import { MixpanelProvider } from '../../analytics/mixpanelContext'; import { SuperagentThemeProvider } from './SuperagentScreen'; import { UserProvider } from '../../user/userContext'; import { routeIdentityKey, shouldNavigateHostBack } from '../../runtime/routeKey'; import { SuperagentRuntimeProvider, type SuperagentRuntimeContextValue } from '../../runtime/runtimeContext'; import { SuperagentHomeScreenView } from './SuperagentHomeScreenView'; import { useSuperagentRuntime } from '../../runtime/useSuperagentRuntime'; import { useSuperagentAttachmentPicker } from '../attachments/useSuperagentAttachmentPicker'; import type { SuperagentHomeScreenHandle, SuperagentHomeScreenProps } from '../../types'; /** * Public entry point for the Superagent mobile experience. * * The shell owns everything the package can produce itself: it runs * `useSuperagentRuntime` (agents, channels, connectors, automations, secrets, * files, the API + realtime clients, and every mutation handler) and the * attachment picker, publishes them through `SuperagentRuntimeProvider`, and * renders the pure `SuperagentHomeScreenView` inside the theme + user + Mixpanel * providers. Descendants read what they need via the `useSuperagent*` domain hooks * rather than receiving drilled props. The host supplies only what it alone can: * the auth-backed `session`, the signed-in `user`, the build `environment`, native * `adapters`, and app-shell navigation wiring. */ export const SuperagentHomeScreen = forwardRef( function SuperagentHomeScreen(props, ref) { // Mount the providers here — above the runtime — so `useSuperagentRuntime` // (and its mutation handlers) run inside `MixpanelProvider` and can report BI // events from their success branches via `useAgentBi()`. return ( ); }, ); function SuperagentHomeScreenContent( { session, user, environment = 'production', adapters, navigationMode, initialRoute, isActive, onOpenAgent, onAgentBack, onRouteChange, onActiveAgentChange, hideConversationHeader, contentTopInset, onViewPlans, forwardedRef, }: SuperagentHomeScreenProps & { forwardedRef: ForwardedRef }, ) { const runtime = useSuperagentRuntime({ session, user, adapters, initialRoute }); // Attachment picking is a native capability: wire it only when the host // provided the adapters, otherwise the composer hides those affordances. const pickerInput = useMemo( () => adapters?.attachments ? { baseUrl: session.baseUrl, getAccessToken: session.getAccessToken, nativeAdapters: adapters.attachments } : undefined, [adapters?.attachments, session.baseUrl, session.getAccessToken], ); const picker = useSuperagentAttachmentPicker(pickerInput); // Surface the resolved active agent (including once its details load) so a // host-owned native nav bar can set its title. Reports null while on the home // list so the title doesn't linger on the default first agent. const titleAgent = runtime.currentRoute.name === 'agent' ? runtime.activeAgent : null; useEffect(() => { onActiveAgentChange?.(titleAgent); }, [titleAgent, onActiveAgentChange]); // Notify the host of every route change from the one place that sees them all: // the runtime's own `currentRoute`. This covers both view-driven navigation // (which flows through `runtime.onRouteChange`) and runtime-driven moves the // host never asked for — e.g. deleting the active agent snaps back to home — so // a host toggling bottom tabs on the home route stays in sync either way. Keyed // on the route identity (not the object/callback) and skipping mount so the host // isn't re-told the route it just supplied. const currentRouteKey = routeIdentityKey(runtime.currentRoute); const routeNotifyMountedRef = useRef(false); useEffect(() => { if (!routeNotifyMountedRef.current) { routeNotifyMountedRef.current = true; return; } onRouteChange?.(runtime.currentRoute); if (shouldNavigateHostBack(runtime.currentRoute, onAgentBack != null)) { onAgentBack?.(); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentRouteKey]); // The single value published to the whole screen tree. Runtime data/handlers, // plus the shell-owned overrides (host navigation, media pickers, plans CTA, // debug flag derived from the build flavor, content inset, tab-active flag). const runtimeValue: SuperagentRuntimeContextValue = { ...runtime, navigationMode, // Forward the runtime's own route (seeded from the host `initialRoute` prop), // so runtime-driven navigation — delete → home, create → new agent — reaches // the view instead of being pinned to the static host prop. initialRoute: runtime.currentRoute, onOpenAgent: onOpenAgent ?? runtime.onOpenAgent, onAgentBack, // View navigation drives the runtime only; the host is notified via the // effect above (which also catches runtime-driven route changes). onRouteChange: runtime.onRouteChange, onViewPlans, showDebugPayloads: environment !== 'production', isActive, contentTopInset, hideConversationHeader, onPickFiles: picker.onPickFiles, onPickPhotos: picker.onPickPhotos, onTakePhoto: picker.onTakePhoto, }; return ( {picker.attachmentPickerModal} ); }