import { createContext, useCallback, useEffect, useMemo, useState, } from "react"; import { Env, Insight, type MCPToolRequest } from "../../.."; /** * Context of the react data */ export const InsightContext = createContext< | { isInitialized: Insight["isInitialized"]; isAuthorized: Insight["isAuthorized"]; isReady: Insight["isReady"]; error: Insight["error"]; system: Insight["system"]; actions: Insight["actions"]; insightId: Insight["insightId"]; tool?: MCPToolRequest; } | undefined >(undefined); interface InsightProviderProps { /** * Content to render with the insight */ children: React.ReactNode; /** * Options to load into the app */ options?: Parameters[0]; /** * Whether to destroy the insight on unmount * Defaults to true */ destroyOnUnmount?: boolean; } export const InsightProvider = (props: InsightProviderProps) => { const { children, options, destroyOnUnmount = true } = props; // create the new insight on load const insight = useMemo(() => { return new Insight(); }, []); const [isInitialized, setIsInitialized] = useState(false); const [isAuthorized, setIsAuthorized] = useState(false); const [isReady, setIsReady] = useState(false); const [error, setError] = useState(null); const [system, setSystem] = useState(null); const [insightId, setInsightId] = useState(""); /** * Sync the insight with react */ const syncInsight = useCallback(() => { setError(insight.error); setSystem(insight.system); setIsAuthorized(insight.isAuthorized); setIsInitialized(insight.isInitialized); setIsReady(insight.isReady); setInsightId(insight.insightId); }, [insight]); const wrappedActions = useMemo(() => { return Object.keys(insight.actions).reduce( (acc, val) => { acc[val] = async (...args: unknown[]) => { // wait for the action to complete const response = await insight.actions[val].apply(null, [ ...args, ]); // sync it syncInsight(); // return the response return response; }; return acc; }, {} as Insight["actions"], ); }, [insight, insight.actions, syncInsight]); // initialize the insight / destroy // biome-ignore lint/correctness/useExhaustiveDependencies: options is checked in the string useEffect(() => { // initialize the insight insight.initialize(options).finally(() => syncInsight()); return () => { if (destroyOnUnmount) { // destroy the insight insight.destroy().finally(() => { // update the state syncInsight(); }); return; } // update the state without destroying syncInsight(); }; }, [insight, JSON.stringify(options), destroyOnUnmount, syncInsight]); return ( {children} ); };