You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@mcp-apps-kit/ui-react

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mcp-apps-kit/ui-react - npm Package Compare versions

Comparing version
0.2.3
to
0.2.4
+1
-1
dist/index.cjs

@@ -1,2 +0,2 @@

'use strict';var react=require('react'),ui=require('@mcp-apps-kit/ui'),jsxRuntime=require('react/jsx-runtime');var y=react.createContext(null);function v({children:e,client:t,forceAdapter:o,fallback:s,errorFallback:r}){let[n,l]=react.useState(t??null),[p,f]=react.useState(!t),[g,T]=react.useState(null);return react.useEffect(()=>{if(t){l(t),f(false);return}(async()=>{try{let m=await ui.createClient({forceAdapter:o});l(m),f(!1);}catch(m){T(m instanceof Error?m:new Error(String(m))),f(false);}})();},[t,o]),g&&r?jsxRuntime.jsx(r,{error:g,reset:()=>T(null)}):p&&s?jsxRuntime.jsx(jsxRuntime.Fragment,{children:s}):jsxRuntime.jsx(y.Provider,{value:{client:n,isConnecting:p,error:g},children:e})}function i(){let e=react.useContext(y);if(!e)throw new Error("useAppsContext must be used within an AppsProvider");return e}function U(){let{client:e,isConnecting:t,error:o}=i();if(o)throw o;if(t||!e)throw new Error("Client not ready. Make sure AppsProvider has finished connecting.");return e}function A(){let{client:e}=i(),[t,o]=react.useState(void 0);return react.useEffect(()=>e?e.onToolResult(r=>{o(r);}):void 0,[e]),t}function I(){let{client:e}=i(),[t,o]=react.useState(e?.toolInput);return react.useEffect(()=>e?e.onToolInput(r=>{o(r);}):void 0,[e]),t}function d(){let{client:e}=i(),[t,o]=react.useState(e?.hostContext??{theme:"light",displayMode:"inline",availableDisplayModes:["inline"],viewport:{width:0,height:0},locale:"en-US",platform:"web"});return react.useEffect(()=>e?(o(e.hostContext),e.onHostContextChange(r=>{o(r);})):void 0,[e]),t}function F(e){let{client:t}=i(),[o,s]=react.useState(()=>t?t.getState()??e:e),r=react.useCallback(n=>{s(l=>{let p=typeof n=="function"?n(l):n;return t?.setState(p),p});},[t]);return [o,r]}function E(){let e=d();react.useEffect(()=>{let t=e.styles?.variables;if(!t)return;let o=document.documentElement;for(let[s,r]of Object.entries(t))o.style.setProperty(s,r);return ()=>{for(let s of Object.keys(t))o.style.removeProperty(s);}},[e.styles?.variables]);}function P(e="light",t="dark"){let o=d();react.useEffect(()=>{let{theme:s}=o,r=document.body;return r.classList.remove(e,t),r.classList.add(s==="dark"?t:e),()=>{r.classList.remove(e,t);}},[o.theme,e,t]);}function H(){let e=d(),{client:t}=i(),o=react.useCallback(async s=>{await t?.requestDisplayMode(s);},[t]);return {mode:e.displayMode,availableModes:e.availableDisplayModes,requestMode:o}}function L(){return d().safeAreaInsets??{top:0,right:0,bottom:0,left:0}}function O(e){let{client:t}=i();react.useEffect(()=>t?t.onToolCancelled(e):void 0,[t,e]);}function k(e){let{client:t}=i();react.useEffect(()=>t?t.onTeardown(e):void 0,[t,e]);}function V(){let{client:e}=i(),[t,o]=react.useState({isSupported:false,isUploading:false,error:null,fileId:null});react.useEffect(()=>{o(r=>({...r,isSupported:!!e?.uploadFile}));},[e]);let s=react.useCallback(async r=>{if(!e?.uploadFile)return o(n=>({...n,error:new Error("File upload not supported on this platform")})),null;o(n=>({...n,isUploading:true,error:null}));try{let n=await e.uploadFile(r);return o(l=>({...l,isUploading:!1,fileId:n.fileId})),n}catch(n){let l=n instanceof Error?n:new Error(String(n));return o(p=>({...p,isUploading:false,error:l})),null}},[e]);return {...t,upload:s}}function q(){let{client:e}=i(),[t,o]=react.useState({isSupported:false,isLoading:false,error:null,downloadUrl:null});react.useEffect(()=>{o(r=>({...r,isSupported:!!e?.getFileDownloadUrl}));},[e]);let s=react.useCallback(async r=>{if(!e?.getFileDownloadUrl)return o(n=>({...n,error:new Error("File download not supported on this platform")})),null;o(n=>({...n,isLoading:true,error:null}));try{let n=await e.getFileDownloadUrl(r);return o(l=>({...l,isLoading:!1,downloadUrl:n.downloadUrl})),n.downloadUrl}catch(n){let l=n instanceof Error?n:new Error(String(n));return o(p=>({...p,isLoading:false,error:l})),null}},[e]);return {...t,getDownloadUrl:s}}function N(){let{client:e}=i(),t=react.useRef(null),[o,s]=react.useState(false);react.useEffect(()=>{s(!!e?.notifyIntrinsicHeight);},[e]);let r=react.useCallback(n=>{e?.notifyIntrinsicHeight?.(n);},[e]);return react.useEffect(()=>{if(!e?.notifyIntrinsicHeight||!t.current)return;let n=t.current,l=new ResizeObserver(p=>{for(let f of p){let g=f.contentRect.height;e.notifyIntrinsicHeight?.(g);}});return l.observe(n),e.notifyIntrinsicHeight(n.offsetHeight),()=>l.disconnect()},[e]),{isSupported:o,containerRef:t,notify:r}}function j(){return d().view}function W(){let{client:e}=i(),[t,o]=react.useState(false),[s,r]=react.useState(false);react.useEffect(()=>{o(!!e?.requestModal);},[e]);let n=react.useCallback(async l=>{if(!e?.requestModal)return null;r(true);try{return await e.requestModal(l)}finally{r(false);}},[e]);return {isSupported:t,isOpen:s,showModal:n}}function z(e){return i(),react.useEffect(()=>{e&&ui.clientDebugLogger.configure(e);},[e]),ui.clientDebugLogger}exports.AppsProvider=v;exports.useAppsClient=U;exports.useDebugLogger=z;exports.useDisplayMode=H;exports.useDocumentTheme=P;exports.useFileDownload=q;exports.useFileUpload=V;exports.useHostContext=d;exports.useHostStyleVariables=E;exports.useIntrinsicHeight=N;exports.useModal=W;exports.useOnTeardown=k;exports.useOnToolCancelled=O;exports.useSafeAreaInsets=L;exports.useToolInput=I;exports.useToolResult=A;exports.useView=j;exports.useWidgetState=F;//# sourceMappingURL=index.cjs.map
'use strict';var react=require('react'),ui=require('@mcp-apps-kit/ui'),jsxRuntime=require('react/jsx-runtime');var C=react.createContext(null);function H({children:e,client:t,forceAdapter:o,fallback:s,errorFallback:r}){let[n,i]=react.useState(t??null),[a,p]=react.useState(!t),[g,T]=react.useState(null);return react.useEffect(()=>{if(t){i(t),p(false);return}(async()=>{try{let b=await ui.createClient({forceAdapter:o});i(b),p(!1);}catch(b){T(b instanceof Error?b:new Error(String(b))),p(false);}})();},[t,o]),g&&r?jsxRuntime.jsx(r,{error:g,reset:()=>T(null)}):a&&s?jsxRuntime.jsx(jsxRuntime.Fragment,{children:s}):jsxRuntime.jsx(C.Provider,{value:{client:n,isConnecting:a,error:g},children:e})}function l(){let e=react.useContext(C);if(!e)throw new Error("useAppsContext must be used within an AppsProvider");return e}function M(){let{client:e,isConnecting:t,error:o}=l();if(o)throw o;if(t||!e)throw new Error("Client not ready. Make sure AppsProvider has finished connecting.");return e}function A(){let{client:e}=l(),[t,o]=react.useState(void 0);return react.useEffect(()=>e?e.onToolResult(r=>{o(r);}):void 0,[e]),t}function U(){let{client:e}=l(),[t,o]=react.useState(e?.toolInput);return react.useEffect(()=>e?e.onToolInput(r=>{o(r);}):void 0,[e]),t}function f(){let{client:e}=l(),[t,o]=react.useState(e?.hostContext??{theme:"light",displayMode:"inline",availableDisplayModes:["inline"],viewport:{width:0,height:0},locale:"en-US",platform:"web"});return react.useEffect(()=>e?(o(e.hostContext),e.onHostContextChange(r=>{o(r);})):void 0,[e]),t}function I(e){let{client:t}=l(),[o,s]=react.useState(()=>t?t.getState()??e:e),r=react.useCallback(n=>{s(i=>{let a=typeof n=="function"?n(i):n;return t?.setState(a),a});},[t]);return [o,r]}function P(){let e=f();react.useEffect(()=>{let t=e.styles?.variables;if(!t)return;let o=document.documentElement;for(let[s,r]of Object.entries(t))o.style.setProperty(s,r);return ()=>{for(let s of Object.keys(t))o.style.removeProperty(s);}},[e.styles?.variables]);}function E(e="light",t="dark"){let o=f();react.useEffect(()=>{let{theme:s}=o,r=document.body;return r.classList.remove(e,t),r.classList.add(s==="dark"?t:e),()=>{r.classList.remove(e,t);}},[o.theme,e,t]);}function F(){let e=f(),{client:t}=l(),o=react.useCallback(async s=>{await t?.requestDisplayMode(s);},[t]);return {mode:e.displayMode,availableModes:e.availableDisplayModes,requestMode:o}}function L(){return f().safeAreaInsets??{top:0,right:0,bottom:0,left:0}}function O(e){let{client:t}=l();react.useEffect(()=>t?t.onToolCancelled(e):void 0,[t,e]);}function V(e){let{client:t}=l();react.useEffect(()=>t?t.onTeardown(e):void 0,[t,e]);}function k(e){let{client:t}=l();react.useEffect(()=>t?t.onToolInputPartial(e):void 0,[t,e]);}function z(){let{client:e}=l(),[t,o]=react.useState(e?.getHostCapabilities());return react.useEffect(()=>{e&&o(e.getHostCapabilities());},[e]),t}function q(){let{client:e}=l(),[t,o]=react.useState(e?.getHostVersion());return react.useEffect(()=>{e&&o(e.getHostVersion());},[e]),t}function N(){let{client:e}=l(),t=react.useRef(null);return react.useEffect(()=>{if(!e||typeof ResizeObserver>"u")return;let o=t.current;if(!o)return;let s=new ResizeObserver(n=>{for(let i of n){let{width:a,height:p}=i.contentRect;e.sendSizeChanged({width:Math.round(a),height:Math.round(p)});}});s.observe(o);let r=o.getBoundingClientRect();return e.sendSizeChanged({width:Math.round(r.width),height:Math.round(r.height)}),()=>s.disconnect()},[e]),t}function j(){let{client:e}=l(),[t,o]=react.useState({isSupported:false,isUploading:false,error:null,fileId:null});react.useEffect(()=>{o(r=>({...r,isSupported:!!e?.uploadFile}));},[e]);let s=react.useCallback(async r=>{if(!e?.uploadFile)return o(n=>({...n,error:new Error("File upload not supported on this platform")})),null;o(n=>({...n,isUploading:true,error:null}));try{let n=await e.uploadFile(r);return o(i=>({...i,isUploading:!1,fileId:n.fileId})),n}catch(n){let i=n instanceof Error?n:new Error(String(n));return o(a=>({...a,isUploading:false,error:i})),null}},[e]);return {...t,upload:s}}function B(){let{client:e}=l(),[t,o]=react.useState({isSupported:false,isLoading:false,error:null,downloadUrl:null});react.useEffect(()=>{o(r=>({...r,isSupported:!!e?.getFileDownloadUrl}));},[e]);let s=react.useCallback(async r=>{if(!e?.getFileDownloadUrl)return o(n=>({...n,error:new Error("File download not supported on this platform")})),null;o(n=>({...n,isLoading:true,error:null}));try{let n=await e.getFileDownloadUrl(r);return o(i=>({...i,isLoading:!1,downloadUrl:n.downloadUrl})),n.downloadUrl}catch(n){let i=n instanceof Error?n:new Error(String(n));return o(a=>({...a,isLoading:false,error:i})),null}},[e]);return {...t,getDownloadUrl:s}}function W(){let{client:e}=l(),t=react.useRef(null),[o,s]=react.useState(false);react.useEffect(()=>{s(!!e?.notifyIntrinsicHeight);},[e]);let r=react.useCallback(n=>{e?.notifyIntrinsicHeight?.(n);},[e]);return react.useEffect(()=>{if(!e?.notifyIntrinsicHeight||!t.current)return;let n=t.current,i=new ResizeObserver(a=>{for(let p of a){let g=p.contentRect.height;e.notifyIntrinsicHeight?.(g);}});return i.observe(n),e.notifyIntrinsicHeight(n.offsetHeight),()=>i.disconnect()},[e]),{isSupported:o,containerRef:t,notify:r}}function J(){return f().view}function X(){let{client:e}=l(),[t,o]=react.useState(false),[s,r]=react.useState(false);react.useEffect(()=>{o(!!e?.requestModal);},[e]);let n=react.useCallback(async i=>{if(!e?.requestModal)return null;r(true);try{return await e.requestModal(i)}finally{r(false);}},[e]);return {isSupported:t,isOpen:s,showModal:n}}function G(e){return l(),react.useEffect(()=>{e&&ui.clientDebugLogger.configure(e);},[e]),ui.clientDebugLogger}exports.AppsProvider=H;exports.useAppsClient=M;exports.useDebugLogger=G;exports.useDisplayMode=F;exports.useDocumentTheme=E;exports.useFileDownload=B;exports.useFileUpload=j;exports.useHostCapabilities=z;exports.useHostContext=f;exports.useHostStyleVariables=P;exports.useHostVersion=q;exports.useIntrinsicHeight=W;exports.useModal=X;exports.useOnTeardown=V;exports.useOnToolCancelled=O;exports.useOnToolInputPartial=k;exports.useSafeAreaInsets=L;exports.useSizeChangedNotifications=N;exports.useToolInput=U;exports.useToolResult=A;exports.useView=J;exports.useWidgetState=I;//# sourceMappingURL=index.cjs.map
//# sourceMappingURL=index.cjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["AppsContext","createContext","AppsProvider","children","providedClient","_forceAdapter","fallback","ErrorFallback","client","setClient","useState","isConnecting","setIsConnecting","error","setError","useEffect","newClient","createClient","err","jsx","Fragment","useAppsContext","context","useContext","useAppsClient","useToolResult","result","setResult","newResult","useToolInput","input","setInput","newInput","useHostContext","setContext","newContext","useWidgetState","defaultValue","state","setStateInternal","setState","useCallback","newState","prev","next","useHostStyleVariables","variables","root","key","value","useDocumentTheme","lightClass","darkClass","theme","body","useDisplayMode","requestMode","mode","useSafeAreaInsets","useOnToolCancelled","handler","useOnTeardown","useFileUpload","upload","file","useFileDownload","getDownloadUrl","fileId","useIntrinsicHeight","containerRef","useRef","isSupported","setIsSupported","notify","height","element","observer","entries","entry","useView","useModal","isOpen","setIsOpen","showModal","options","useDebugLogger","config","clientDebugLogger"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * React context and provider for @mcp-apps-kit/ui-react\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport type { AppsClient, ToolDefs } from \"@mcp-apps-kit/ui\";\nimport { createClient } from \"@mcp-apps-kit/ui\";\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\ninterface AppsContextValue<T extends ToolDefs = ToolDefs> {\n client: AppsClient<T> | null;\n isConnecting: boolean;\n error: Error | null;\n}\n\nconst AppsContext = createContext<AppsContextValue | null>(null);\n\n// =============================================================================\n// PROVIDER PROPS\n// =============================================================================\n\n/**\n * Props for AppsProvider\n */\nexport interface AppsProviderProps<T extends ToolDefs = ToolDefs> {\n /** Child components */\n children: ReactNode;\n\n /**\n * Pre-created client instance (optional)\n * If not provided, creates client automatically\n */\n client?: AppsClient<T>;\n\n /**\n * Force a specific protocol adapter\n */\n forceAdapter?: \"mcp\" | \"openai\" | \"mock\";\n\n /**\n * Fallback UI while client is connecting\n */\n fallback?: ReactNode;\n\n /**\n * Error boundary fallback\n */\n errorFallback?: ComponentType<{ error: Error; reset: () => void }>;\n}\n\n// =============================================================================\n// PROVIDER\n// =============================================================================\n\n/**\n * Context provider for mcp-apps-kit React integration\n *\n * Wraps your app and provides the client instance to all hooks.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AppsProvider fallback={<Loading />}>\n * <MyWidget />\n * </AppsProvider>\n * );\n * }\n * ```\n */\nexport function AppsProvider<T extends ToolDefs = ToolDefs>({\n children,\n client: providedClient,\n forceAdapter: _forceAdapter,\n fallback,\n errorFallback: ErrorFallback,\n}: AppsProviderProps<T>): React.JSX.Element {\n const [client, setClient] = useState<AppsClient<T> | null>(providedClient ?? null);\n const [isConnecting, setIsConnecting] = useState(!providedClient);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (providedClient) {\n setClient(providedClient);\n setIsConnecting(false);\n return;\n }\n\n // Initialize client with auto-detection or forced adapter\n const initClient = async () => {\n try {\n const newClient = await createClient<T>({\n forceAdapter: _forceAdapter,\n });\n setClient(newClient);\n setIsConnecting(false);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsConnecting(false);\n }\n };\n\n void initClient();\n }, [providedClient, _forceAdapter]);\n\n if (error && ErrorFallback) {\n return <ErrorFallback error={error} reset={() => setError(null)} />;\n }\n\n if (isConnecting && fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <AppsContext.Provider value={{ client, isConnecting, error }}>{children}</AppsContext.Provider>\n );\n}\n\n// =============================================================================\n// INTERNAL HOOK\n// =============================================================================\n\n/**\n * Internal hook to access the context\n * @internal\n */\nexport function useAppsContext<T extends ToolDefs = ToolDefs>(): AppsContextValue<T> {\n const context = useContext(AppsContext);\n if (!context) {\n throw new Error(\"useAppsContext must be used within an AppsProvider\");\n }\n return context as AppsContextValue<T>;\n}\n","/**\n * React hooks for @mcp-apps-kit/ui-react\n */\n\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport type {\n AppsClient,\n HostContext,\n ToolDefs,\n ToolResult,\n ClientDebugConfig,\n ModalOptions,\n ModalResult,\n} from \"@mcp-apps-kit/ui\";\nimport { clientDebugLogger, type ClientDebugLogger } from \"@mcp-apps-kit/ui\";\nimport { useAppsContext } from \"./context\";\n\n// =============================================================================\n// CORE HOOKS\n// =============================================================================\n\n/**\n * Access the typed client instance\n *\n * @returns Client instance\n * @throws Error if used outside AppsProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useAppsClient<typeof app.tools>();\n *\n * const handleClick = async () => {\n * await client.callTool(\"myTool\", { arg: \"value\" });\n * };\n *\n * return <button onClick={handleClick}>Call Tool</button>;\n * }\n * ```\n */\nexport function useAppsClient<T extends ToolDefs = ToolDefs>(): AppsClient<T> {\n const { client, isConnecting, error } = useAppsContext<T>();\n\n if (error) {\n throw error;\n }\n\n if (isConnecting || !client) {\n throw new Error(\"Client not ready. Make sure AppsProvider has finished connecting.\");\n }\n\n return client;\n}\n\n/**\n * Access current tool result with automatic re-renders\n *\n * @returns Current tool result or undefined\n *\n * @example\n * ```tsx\n * function ResultDisplay() {\n * const result = useToolResult<typeof app.tools>();\n *\n * if (!result?.myTool) {\n * return <div>No results yet</div>;\n * }\n *\n * return <div>{result.myTool.message}</div>;\n * }\n * ```\n */\nexport function useToolResult<T extends ToolDefs = ToolDefs>(): ToolResult<T> | undefined {\n const { client } = useAppsContext<T>();\n const [result, setResult] = useState<ToolResult<T> | undefined>(undefined);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolResult((newResult) => {\n setResult(newResult as ToolResult<T>);\n });\n\n return unsubscribe;\n }, [client]);\n\n return result;\n}\n\n/**\n * Access current tool input\n *\n * @returns Current tool input or undefined\n *\n * @example\n * ```tsx\n * function InputDisplay() {\n * const input = useToolInput();\n * return <pre>{JSON.stringify(input, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useToolInput(): Record<string, unknown> | undefined {\n const { client } = useAppsContext();\n const [input, setInput] = useState<Record<string, unknown> | undefined>(client?.toolInput);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInput((newInput) => {\n setInput(newInput);\n });\n\n return unsubscribe;\n }, [client]);\n\n return input;\n}\n\n/**\n * Access host context with automatic re-renders on changes\n *\n * @returns Current host context\n *\n * @example\n * ```tsx\n * function ThemedComponent() {\n * const context = useHostContext();\n *\n * return (\n * <div className={context.theme}>\n * Display: {context.displayMode}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostContext(): HostContext {\n const { client } = useAppsContext();\n const [context, setContext] = useState<HostContext>(\n client?.hostContext ?? {\n theme: \"light\",\n displayMode: \"inline\",\n availableDisplayModes: [\"inline\"],\n viewport: { width: 0, height: 0 },\n locale: \"en-US\",\n platform: \"web\",\n }\n );\n\n useEffect(() => {\n if (!client) return;\n\n setContext(client.hostContext);\n\n const unsubscribe = client.onHostContextChange((newContext) => {\n setContext(newContext);\n });\n\n return unsubscribe;\n }, [client]);\n\n return context;\n}\n\n/**\n * Persisted widget state with automatic sync\n *\n * Works like useState but persists across widget reloads.\n * On ChatGPT: Session-scoped persistence\n * On MCP Apps: Silent no-op (returns default, setState is ignored)\n *\n * @param defaultValue - Initial state value\n * @returns [state, setState] tuple\n *\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount] = useWidgetState(0);\n *\n * return (\n * <button onClick={() => setCount(c => c + 1)}>\n * Count: {count}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWidgetState<S>(defaultValue: S): [S, (newState: S | ((prev: S) => S)) => void] {\n const { client } = useAppsContext();\n\n const [state, setStateInternal] = useState<S>(() => {\n if (!client) return defaultValue;\n const stored = client.getState<S>();\n return stored ?? defaultValue;\n });\n\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setStateInternal((prev) => {\n const next = typeof newState === \"function\" ? (newState as (prev: S) => S)(prev) : newState;\n\n // Persist to client (silent no-op on MCP Apps)\n client?.setState(next);\n\n return next;\n });\n },\n [client]\n );\n\n return [state, setState];\n}\n\n// =============================================================================\n// UTILITY HOOKS\n// =============================================================================\n\n/**\n * Apply host CSS variables to document root\n *\n * Call this once in your root component to apply host theming.\n *\n * @example\n * ```tsx\n * function App() {\n * useHostStyleVariables();\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useHostStyleVariables(): void {\n const context = useHostContext();\n\n useEffect(() => {\n const variables = context.styles?.variables;\n if (!variables) return;\n\n const root = document.documentElement;\n for (const [key, value] of Object.entries(variables)) {\n root.style.setProperty(key, value);\n }\n\n // Cleanup\n return () => {\n for (const key of Object.keys(variables)) {\n root.style.removeProperty(key);\n }\n };\n }, [context.styles?.variables]);\n}\n\n/**\n * Apply theme class to document body\n *\n * @param lightClass - Class name for light theme (default: \"light\")\n * @param darkClass - Class name for dark theme (default: \"dark\")\n *\n * @example\n * ```tsx\n * function App() {\n * useDocumentTheme(\"theme-light\", \"theme-dark\");\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useDocumentTheme(lightClass = \"light\", darkClass = \"dark\"): void {\n const context = useHostContext();\n\n useEffect(() => {\n const { theme } = context;\n const body = document.body;\n\n body.classList.remove(lightClass, darkClass);\n body.classList.add(theme === \"dark\" ? darkClass : lightClass);\n\n return () => {\n body.classList.remove(lightClass, darkClass);\n };\n }, [context.theme, lightClass, darkClass]);\n}\n\n/**\n * Access and manage display mode\n *\n * @returns Display mode state and controls\n *\n * @example\n * ```tsx\n * function DisplayModeToggle() {\n * const { mode, availableModes, requestMode } = useDisplayMode();\n *\n * return (\n * <select value={mode} onChange={e => requestMode(e.target.value)}>\n * {availableModes.map(m => (\n * <option key={m} value={m}>{m}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useDisplayMode(): {\n mode: string;\n availableModes: string[];\n requestMode: (mode: \"inline\" | \"fullscreen\" | \"pip\") => Promise<void>;\n} {\n const context = useHostContext();\n const { client } = useAppsContext();\n\n const requestMode = useCallback(\n async (mode: \"inline\" | \"fullscreen\" | \"pip\") => {\n await client?.requestDisplayMode(mode);\n },\n [client]\n );\n\n return {\n mode: context.displayMode,\n availableModes: context.availableDisplayModes,\n requestMode,\n };\n}\n\n/**\n * Access safe area insets for mobile layouts\n *\n * @returns Safe area insets or default zeros\n *\n * @example\n * ```tsx\n * function SafeContent() {\n * const insets = useSafeAreaInsets();\n *\n * return (\n * <div style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>\n * Content\n * </div>\n * );\n * }\n * ```\n */\nexport function useSafeAreaInsets(): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const context = useHostContext();\n\n return (\n context.safeAreaInsets ?? {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }\n );\n}\n\n// =============================================================================\n// EVENT HOOKS\n// =============================================================================\n\n/**\n * Subscribe to tool cancellation\n *\n * @param handler - Callback when tool is cancelled\n *\n * @example\n * ```tsx\n * function CancellableOperation() {\n * const [cancelled, setCancelled] = useState(false);\n *\n * useOnToolCancelled((reason) => {\n * setCancelled(true);\n * console.log(\"Cancelled:\", reason);\n * });\n *\n * return cancelled ? <div>Cancelled</div> : <div>Running...</div>;\n * }\n * ```\n */\nexport function useOnToolCancelled(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolCancelled(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to teardown events\n *\n * @param handler - Callback when widget is torn down\n *\n * @example\n * ```tsx\n * function CleanupComponent() {\n * useOnTeardown((reason) => {\n * console.log(\"Tearing down:\", reason);\n * // Cleanup resources\n * });\n *\n * return <div>Widget</div>;\n * }\n * ```\n */\nexport function useOnTeardown(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onTeardown(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n// =============================================================================\n// FILE OPERATION HOOKS\n// =============================================================================\n\n/**\n * File upload result\n */\nexport interface FileUploadResult {\n /** Uploaded file ID */\n fileId: string;\n}\n\n/**\n * File upload state\n */\nexport interface UseFileUploadState {\n /** Whether file upload is supported on this platform */\n isSupported: boolean;\n /** Whether upload is in progress */\n isUploading: boolean;\n /** Upload error if any */\n error: Error | null;\n /** Last uploaded file ID */\n fileId: string | null;\n}\n\n/**\n * Hook for uploading files (ChatGPT only)\n *\n * Provides a function to upload files and tracks upload state.\n * On unsupported platforms, isSupported will be false.\n *\n * Supported file types: PNG, JPEG, WebP images\n *\n * @returns Upload function and state\n *\n * @example\n * ```tsx\n * function ImageUploader() {\n * const { upload, isSupported, isUploading, error, fileId } = useFileUpload();\n *\n * if (!isSupported) {\n * return <div>File upload not supported on this platform</div>;\n * }\n *\n * const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0];\n * if (file) {\n * const result = await upload(file);\n * console.log(\"Uploaded:\", result?.fileId);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"image/*\" onChange={handleFileChange} />\n * {isUploading && <span>Uploading...</span>}\n * {error && <span>Error: {error.message}</span>}\n * {fileId && <span>Uploaded: {fileId}</span>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFileUpload(): UseFileUploadState & {\n upload: (file: File) => Promise<FileUploadResult | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<UseFileUploadState>({\n isSupported: false,\n isUploading: false,\n error: null,\n fileId: null,\n });\n\n // Check if upload is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.uploadFile,\n }));\n }, [client]);\n\n const upload = useCallback(\n async (file: File): Promise<FileUploadResult | null> => {\n if (!client?.uploadFile) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File upload not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isUploading: true, error: null }));\n\n try {\n const result = await client.uploadFile(file);\n setState((prev) => ({\n ...prev,\n isUploading: false,\n fileId: result.fileId,\n }));\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isUploading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, upload };\n}\n\n/**\n * Hook for getting file download URLs (ChatGPT only)\n *\n * Provides a function to get temporary download URLs for uploaded files.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Download URL function and state\n *\n * @example\n * ```tsx\n * function FileDownloader({ fileId }: { fileId: string }) {\n * const { getDownloadUrl, isSupported, isLoading, error, downloadUrl } = useFileDownload();\n *\n * useEffect(() => {\n * if (fileId && isSupported) {\n * getDownloadUrl(fileId);\n * }\n * }, [fileId, isSupported, getDownloadUrl]);\n *\n * if (!isSupported) return <div>Not supported</div>;\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (downloadUrl) return <img src={downloadUrl} alt=\"Uploaded file\" />;\n *\n * return null;\n * }\n * ```\n */\nexport function useFileDownload(): {\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n getDownloadUrl: (fileId: string) => Promise<string | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<{\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n }>({\n isSupported: false,\n isLoading: false,\n error: null,\n downloadUrl: null,\n });\n\n // Check if download URL is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.getFileDownloadUrl,\n }));\n }, [client]);\n\n const getDownloadUrl = useCallback(\n async (fileId: string): Promise<string | null> => {\n if (!client?.getFileDownloadUrl) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File download not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n try {\n const result = await client.getFileDownloadUrl(fileId);\n setState((prev) => ({\n ...prev,\n isLoading: false,\n downloadUrl: result.downloadUrl,\n }));\n return result.downloadUrl;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, getDownloadUrl };\n}\n\n// =============================================================================\n// LAYOUT HOOKS\n// =============================================================================\n\n/**\n * Hook to notify host of widget's intrinsic height (ChatGPT only)\n *\n * Automatically reports height changes to prevent scroll clipping.\n * Returns a ref to attach to your root element for automatic height tracking,\n * or a manual notify function for custom height reporting.\n *\n * @returns Ref for auto-tracking and manual notify function\n *\n * @example\n * ```tsx\n * // Automatic height tracking\n * function AutoHeightWidget() {\n * const { containerRef, isSupported } = useIntrinsicHeight();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change height...</p>\n * </div>\n * );\n * }\n *\n * // Manual height notification\n * function ManualHeightWidget() {\n * const { notify, isSupported } = useIntrinsicHeight();\n *\n * useEffect(() => {\n * // Notify after content loads\n * notify(500);\n * }, [notify]);\n *\n * return <div style={{ height: 500 }}>Fixed height content</div>;\n * }\n * ```\n */\nexport function useIntrinsicHeight(): {\n /** Whether intrinsic height notification is supported */\n isSupported: boolean;\n /** Ref to attach to container for automatic height tracking */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Manually notify host of height */\n notify: (height: number) => void;\n} {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.notifyIntrinsicHeight);\n }, [client]);\n\n // Manual notify function\n const notify = useCallback(\n (height: number) => {\n client?.notifyIntrinsicHeight?.(height);\n },\n [client]\n );\n\n // Auto-track height with ResizeObserver\n useEffect(() => {\n if (!client?.notifyIntrinsicHeight || !containerRef.current) return;\n\n const element = containerRef.current;\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const height = entry.contentRect.height;\n client.notifyIntrinsicHeight?.(height);\n }\n });\n\n observer.observe(element);\n\n // Report initial height\n client.notifyIntrinsicHeight(element.offsetHeight);\n\n return () => observer.disconnect();\n }, [client]);\n\n return { isSupported, containerRef, notify };\n}\n\n/**\n * Hook to access the current view identifier (ChatGPT only)\n *\n * Useful for multi-view widgets that need to know which view is active.\n *\n * @returns Current view identifier or undefined\n *\n * @example\n * ```tsx\n * function MultiViewWidget() {\n * const view = useView();\n *\n * switch (view) {\n * case \"settings\":\n * return <SettingsView />;\n * case \"details\":\n * return <DetailsView />;\n * default:\n * return <MainView />;\n * }\n * }\n * ```\n */\nexport function useView(): string | undefined {\n const context = useHostContext();\n return context.view;\n}\n\n// =============================================================================\n// MODAL HOOKS\n// =============================================================================\n\n/**\n * Hook for showing host-owned modal dialogs (ChatGPT only)\n *\n * Spawns native ChatGPT modals for confirmations, inputs, etc.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Modal function and state\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { showModal, isSupported, isOpen } = useModal();\n *\n * const handleDelete = async () => {\n * const result = await showModal({\n * title: \"Confirm Delete\",\n * body: \"Are you sure you want to delete this item?\",\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Delete\", variant: \"destructive\", value: \"delete\" },\n * ],\n * });\n *\n * if (result?.action === \"delete\") {\n * // Perform delete\n * }\n * };\n *\n * if (!isSupported) {\n * return <button onClick={() => window.confirm(\"Delete?\")}>Delete</button>;\n * }\n *\n * return (\n * <button onClick={handleDelete} disabled={isOpen}>\n * Delete\n * </button>\n * );\n * }\n *\n * // Input modal example\n * function RenameButton() {\n * const { showModal } = useModal();\n *\n * const handleRename = async () => {\n * const result = await showModal({\n * title: \"Rename Item\",\n * input: {\n * type: \"text\",\n * placeholder: \"Enter new name\",\n * defaultValue: \"Current Name\",\n * },\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Rename\", variant: \"primary\", value: \"rename\" },\n * ],\n * });\n *\n * if (result?.action === \"rename\" && result.inputValue) {\n * console.log(\"New name:\", result.inputValue);\n * }\n * };\n *\n * return <button onClick={handleRename}>Rename</button>;\n * }\n * ```\n */\nexport function useModal(): {\n /** Whether modal API is supported */\n isSupported: boolean;\n /** Whether a modal is currently open */\n isOpen: boolean;\n /** Show a modal dialog */\n showModal: (options: ModalOptions) => Promise<ModalResult | null>;\n} {\n const { client } = useAppsContext();\n const [isSupported, setIsSupported] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.requestModal);\n }, [client]);\n\n const showModal = useCallback(\n async (options: ModalOptions): Promise<ModalResult | null> => {\n if (!client?.requestModal) {\n return null;\n }\n\n setIsOpen(true);\n try {\n const result = await client.requestModal(options);\n return result;\n } finally {\n setIsOpen(false);\n }\n },\n [client]\n );\n\n return { isSupported, isOpen, showModal };\n}\n\n// =============================================================================\n// DEBUG LOGGING HOOKS\n// =============================================================================\n\n/**\n * Access the debug logger with automatic adapter injection\n *\n * The adapter is automatically configured when AppsProvider connects.\n * Use this hook to access the logger and optionally configure it.\n *\n * @param config - Optional configuration to apply\n * @returns The configured client debug logger\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const logger = useDebugLogger({ enabled: true, level: \"debug\" });\n *\n * const handleClick = () => {\n * logger.info(\"Button clicked\", { timestamp: Date.now() });\n * };\n *\n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n */\nexport function useDebugLogger(config?: Partial<ClientDebugConfig>): ClientDebugLogger {\n // Verify we're inside AppsProvider (adapter is set by createClient)\n useAppsContext();\n\n // Apply configuration when it changes\n useEffect(() => {\n if (config) {\n clientDebugLogger.configure(config);\n }\n }, [config]);\n\n return clientDebugLogger;\n}\n"]}
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["AppsContext","createContext","AppsProvider","children","providedClient","_forceAdapter","fallback","ErrorFallback","client","setClient","useState","isConnecting","setIsConnecting","error","setError","useEffect","newClient","createClient","err","jsx","Fragment","useAppsContext","context","useContext","useAppsClient","useToolResult","result","setResult","newResult","useToolInput","input","setInput","newInput","useHostContext","setContext","newContext","useWidgetState","defaultValue","state","setStateInternal","setState","useCallback","newState","prev","next","useHostStyleVariables","variables","root","key","value","useDocumentTheme","lightClass","darkClass","theme","body","useDisplayMode","requestMode","mode","useSafeAreaInsets","useOnToolCancelled","handler","useOnTeardown","useOnToolInputPartial","useHostCapabilities","capabilities","setCapabilities","useHostVersion","version","setVersion","useSizeChangedNotifications","containerRef","useRef","element","observer","entries","entry","width","height","rect","useFileUpload","upload","file","useFileDownload","getDownloadUrl","fileId","useIntrinsicHeight","isSupported","setIsSupported","notify","useView","useModal","isOpen","setIsOpen","showModal","options","useDebugLogger","config","clientDebugLogger"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * React context and provider for @mcp-apps-kit/ui-react\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport type { AppsClient, ToolDefs } from \"@mcp-apps-kit/ui\";\nimport { createClient } from \"@mcp-apps-kit/ui\";\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\ninterface AppsContextValue<T extends ToolDefs = ToolDefs> {\n client: AppsClient<T> | null;\n isConnecting: boolean;\n error: Error | null;\n}\n\nconst AppsContext = createContext<AppsContextValue | null>(null);\n\n// =============================================================================\n// PROVIDER PROPS\n// =============================================================================\n\n/**\n * Props for AppsProvider\n */\nexport interface AppsProviderProps<T extends ToolDefs = ToolDefs> {\n /** Child components */\n children: ReactNode;\n\n /**\n * Pre-created client instance (optional)\n * If not provided, creates client automatically\n */\n client?: AppsClient<T>;\n\n /**\n * Force a specific protocol adapter\n */\n forceAdapter?: \"mcp\" | \"openai\" | \"mock\";\n\n /**\n * Fallback UI while client is connecting\n */\n fallback?: ReactNode;\n\n /**\n * Error boundary fallback\n */\n errorFallback?: ComponentType<{ error: Error; reset: () => void }>;\n}\n\n// =============================================================================\n// PROVIDER\n// =============================================================================\n\n/**\n * Context provider for mcp-apps-kit React integration\n *\n * Wraps your app and provides the client instance to all hooks.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AppsProvider fallback={<Loading />}>\n * <MyWidget />\n * </AppsProvider>\n * );\n * }\n * ```\n */\nexport function AppsProvider<T extends ToolDefs = ToolDefs>({\n children,\n client: providedClient,\n forceAdapter: _forceAdapter,\n fallback,\n errorFallback: ErrorFallback,\n}: AppsProviderProps<T>): React.JSX.Element {\n const [client, setClient] = useState<AppsClient<T> | null>(providedClient ?? null);\n const [isConnecting, setIsConnecting] = useState(!providedClient);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (providedClient) {\n setClient(providedClient);\n setIsConnecting(false);\n return;\n }\n\n // Initialize client with auto-detection or forced adapter\n const initClient = async () => {\n try {\n const newClient = await createClient<T>({\n forceAdapter: _forceAdapter,\n });\n setClient(newClient);\n setIsConnecting(false);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsConnecting(false);\n }\n };\n\n void initClient();\n }, [providedClient, _forceAdapter]);\n\n if (error && ErrorFallback) {\n return <ErrorFallback error={error} reset={() => setError(null)} />;\n }\n\n if (isConnecting && fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <AppsContext.Provider value={{ client, isConnecting, error }}>{children}</AppsContext.Provider>\n );\n}\n\n// =============================================================================\n// INTERNAL HOOK\n// =============================================================================\n\n/**\n * Internal hook to access the context\n * @internal\n */\nexport function useAppsContext<T extends ToolDefs = ToolDefs>(): AppsContextValue<T> {\n const context = useContext(AppsContext);\n if (!context) {\n throw new Error(\"useAppsContext must be used within an AppsProvider\");\n }\n return context as AppsContextValue<T>;\n}\n","/**\n * React hooks for @mcp-apps-kit/ui-react\n */\n\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport type {\n AppsClient,\n HostContext,\n ToolDefs,\n ToolResult,\n ClientDebugConfig,\n ModalOptions,\n ModalResult,\n HostCapabilities,\n HostVersion,\n} from \"@mcp-apps-kit/ui\";\nimport { clientDebugLogger, type ClientDebugLogger } from \"@mcp-apps-kit/ui\";\nimport { useAppsContext } from \"./context\";\n\n// =============================================================================\n// CORE HOOKS\n// =============================================================================\n\n/**\n * Access the typed client instance\n *\n * @returns Client instance\n * @throws Error if used outside AppsProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useAppsClient<typeof app.tools>();\n *\n * const handleClick = async () => {\n * await client.callTool(\"myTool\", { arg: \"value\" });\n * };\n *\n * return <button onClick={handleClick}>Call Tool</button>;\n * }\n * ```\n */\nexport function useAppsClient<T extends ToolDefs = ToolDefs>(): AppsClient<T> {\n const { client, isConnecting, error } = useAppsContext<T>();\n\n if (error) {\n throw error;\n }\n\n if (isConnecting || !client) {\n throw new Error(\"Client not ready. Make sure AppsProvider has finished connecting.\");\n }\n\n return client;\n}\n\n/**\n * Access current tool result with automatic re-renders\n *\n * @returns Current tool result or undefined\n *\n * @example\n * ```tsx\n * function ResultDisplay() {\n * const result = useToolResult<typeof app.tools>();\n *\n * if (!result?.myTool) {\n * return <div>No results yet</div>;\n * }\n *\n * return <div>{result.myTool.message}</div>;\n * }\n * ```\n */\nexport function useToolResult<T extends ToolDefs = ToolDefs>(): ToolResult<T> | undefined {\n const { client } = useAppsContext<T>();\n const [result, setResult] = useState<ToolResult<T> | undefined>(undefined);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolResult((newResult) => {\n setResult(newResult as ToolResult<T>);\n });\n\n return unsubscribe;\n }, [client]);\n\n return result;\n}\n\n/**\n * Access current tool input\n *\n * @returns Current tool input or undefined\n *\n * @example\n * ```tsx\n * function InputDisplay() {\n * const input = useToolInput();\n * return <pre>{JSON.stringify(input, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useToolInput(): Record<string, unknown> | undefined {\n const { client } = useAppsContext();\n const [input, setInput] = useState<Record<string, unknown> | undefined>(client?.toolInput);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInput((newInput) => {\n setInput(newInput);\n });\n\n return unsubscribe;\n }, [client]);\n\n return input;\n}\n\n/**\n * Access host context with automatic re-renders on changes\n *\n * @returns Current host context\n *\n * @example\n * ```tsx\n * function ThemedComponent() {\n * const context = useHostContext();\n *\n * return (\n * <div className={context.theme}>\n * Display: {context.displayMode}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostContext(): HostContext {\n const { client } = useAppsContext();\n const [context, setContext] = useState<HostContext>(\n client?.hostContext ?? {\n theme: \"light\",\n displayMode: \"inline\",\n availableDisplayModes: [\"inline\"],\n viewport: { width: 0, height: 0 },\n locale: \"en-US\",\n platform: \"web\",\n }\n );\n\n useEffect(() => {\n if (!client) return;\n\n setContext(client.hostContext);\n\n const unsubscribe = client.onHostContextChange((newContext) => {\n setContext(newContext);\n });\n\n return unsubscribe;\n }, [client]);\n\n return context;\n}\n\n/**\n * Persisted widget state with automatic sync\n *\n * Works like useState but persists across widget reloads.\n * On ChatGPT: Session-scoped persistence\n * On MCP Apps: Silent no-op (returns default, setState is ignored)\n *\n * @param defaultValue - Initial state value\n * @returns [state, setState] tuple\n *\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount] = useWidgetState(0);\n *\n * return (\n * <button onClick={() => setCount(c => c + 1)}>\n * Count: {count}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWidgetState<S>(defaultValue: S): [S, (newState: S | ((prev: S) => S)) => void] {\n const { client } = useAppsContext();\n\n const [state, setStateInternal] = useState<S>(() => {\n if (!client) return defaultValue;\n const stored = client.getState<S>();\n return stored ?? defaultValue;\n });\n\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setStateInternal((prev) => {\n const next = typeof newState === \"function\" ? (newState as (prev: S) => S)(prev) : newState;\n\n // Persist to client (silent no-op on MCP Apps)\n client?.setState(next);\n\n return next;\n });\n },\n [client]\n );\n\n return [state, setState];\n}\n\n// =============================================================================\n// UTILITY HOOKS\n// =============================================================================\n\n/**\n * Apply host CSS variables to document root\n *\n * Call this once in your root component to apply host theming.\n *\n * @example\n * ```tsx\n * function App() {\n * useHostStyleVariables();\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useHostStyleVariables(): void {\n const context = useHostContext();\n\n useEffect(() => {\n const variables = context.styles?.variables;\n if (!variables) return;\n\n const root = document.documentElement;\n for (const [key, value] of Object.entries(variables)) {\n root.style.setProperty(key, value);\n }\n\n // Cleanup\n return () => {\n for (const key of Object.keys(variables)) {\n root.style.removeProperty(key);\n }\n };\n }, [context.styles?.variables]);\n}\n\n/**\n * Apply theme class to document body\n *\n * @param lightClass - Class name for light theme (default: \"light\")\n * @param darkClass - Class name for dark theme (default: \"dark\")\n *\n * @example\n * ```tsx\n * function App() {\n * useDocumentTheme(\"theme-light\", \"theme-dark\");\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useDocumentTheme(lightClass = \"light\", darkClass = \"dark\"): void {\n const context = useHostContext();\n\n useEffect(() => {\n const { theme } = context;\n const body = document.body;\n\n body.classList.remove(lightClass, darkClass);\n body.classList.add(theme === \"dark\" ? darkClass : lightClass);\n\n return () => {\n body.classList.remove(lightClass, darkClass);\n };\n }, [context.theme, lightClass, darkClass]);\n}\n\n/**\n * Access and manage display mode\n *\n * @returns Display mode state and controls\n *\n * @example\n * ```tsx\n * function DisplayModeToggle() {\n * const { mode, availableModes, requestMode } = useDisplayMode();\n *\n * return (\n * <select value={mode} onChange={e => requestMode(e.target.value)}>\n * {availableModes.map(m => (\n * <option key={m} value={m}>{m}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useDisplayMode(): {\n mode: string;\n availableModes: string[];\n requestMode: (mode: \"inline\" | \"fullscreen\" | \"pip\") => Promise<void>;\n} {\n const context = useHostContext();\n const { client } = useAppsContext();\n\n const requestMode = useCallback(\n async (mode: \"inline\" | \"fullscreen\" | \"pip\") => {\n await client?.requestDisplayMode(mode);\n },\n [client]\n );\n\n return {\n mode: context.displayMode,\n availableModes: context.availableDisplayModes,\n requestMode,\n };\n}\n\n/**\n * Access safe area insets for mobile layouts\n *\n * @returns Safe area insets or default zeros\n *\n * @example\n * ```tsx\n * function SafeContent() {\n * const insets = useSafeAreaInsets();\n *\n * return (\n * <div style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>\n * Content\n * </div>\n * );\n * }\n * ```\n */\nexport function useSafeAreaInsets(): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const context = useHostContext();\n\n return (\n context.safeAreaInsets ?? {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }\n );\n}\n\n// =============================================================================\n// EVENT HOOKS\n// =============================================================================\n\n/**\n * Subscribe to tool cancellation\n *\n * @param handler - Callback when tool is cancelled\n *\n * @example\n * ```tsx\n * function CancellableOperation() {\n * const [cancelled, setCancelled] = useState(false);\n *\n * useOnToolCancelled((reason) => {\n * setCancelled(true);\n * console.log(\"Cancelled:\", reason);\n * });\n *\n * return cancelled ? <div>Cancelled</div> : <div>Running...</div>;\n * }\n * ```\n */\nexport function useOnToolCancelled(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolCancelled(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to teardown events\n *\n * @param handler - Callback when widget is torn down\n *\n * @example\n * ```tsx\n * function CleanupComponent() {\n * useOnTeardown((reason) => {\n * console.log(\"Tearing down:\", reason);\n * // Cleanup resources\n * });\n *\n * return <div>Widget</div>;\n * }\n * ```\n */\nexport function useOnTeardown(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onTeardown(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to partial/streaming tool input\n *\n * Called when the host sends partial tool arguments during streaming.\n * Useful for showing real-time input as the user types or as the model generates.\n *\n * @param handler - Callback for partial input\n *\n * @example\n * ```tsx\n * function StreamingInput() {\n * const [partialInput, setPartialInput] = useState<Record<string, unknown>>({});\n *\n * useOnToolInputPartial((input) => {\n * setPartialInput(input);\n * });\n *\n * return <pre>{JSON.stringify(partialInput, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useOnToolInputPartial(handler: (input: Record<string, unknown>) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInputPartial(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n// =============================================================================\n// HOST INFORMATION HOOKS\n// =============================================================================\n\n/**\n * Access host capabilities\n *\n * Returns the capabilities advertised by the host during handshake.\n * Use this to check if features like logging or server tools are supported.\n *\n * @returns Host capabilities or undefined if not yet connected\n *\n * @example\n * ```tsx\n * function CapabilitiesDisplay() {\n * const capabilities = useHostCapabilities();\n *\n * return (\n * <div>\n * <p>Logging: {capabilities?.logging ? \"Supported\" : \"Not supported\"}</p>\n * <p>Open Links: {capabilities?.openLinks ? \"Supported\" : \"Not supported\"}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostCapabilities(): HostCapabilities | undefined {\n const { client } = useAppsContext();\n const [capabilities, setCapabilities] = useState<HostCapabilities | undefined>(\n client?.getHostCapabilities()\n );\n\n useEffect(() => {\n if (!client) return;\n setCapabilities(client.getHostCapabilities());\n }, [client]);\n\n return capabilities;\n}\n\n/**\n * Access host version information\n *\n * Returns the name and version of the host application.\n *\n * @returns Host version info or undefined if not yet connected\n *\n * @example\n * ```tsx\n * function HostInfo() {\n * const hostVersion = useHostVersion();\n *\n * if (!hostVersion) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * Running on {hostVersion.name} v{hostVersion.version}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostVersion(): HostVersion | undefined {\n const { client } = useAppsContext();\n const [version, setVersion] = useState<HostVersion | undefined>(client?.getHostVersion());\n\n useEffect(() => {\n if (!client) return;\n setVersion(client.getHostVersion());\n }, [client]);\n\n return version;\n}\n\n// =============================================================================\n// SIZE NOTIFICATION HOOKS\n// =============================================================================\n\n/**\n * Hook to set up automatic size change notifications\n *\n * Creates a ResizeObserver that automatically sends size changed\n * notifications to the host when the observed element resizes.\n *\n * @returns Ref to attach to the element to observe\n *\n * @example\n * ```tsx\n * function AutoSizeWidget() {\n * const containerRef = useSizeChangedNotifications();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change size...</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSizeChangedNotifications(): React.RefObject<HTMLElement | null> {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!client || typeof ResizeObserver === \"undefined\") return;\n\n const element = containerRef.current;\n if (!element) return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n void client.sendSizeChanged({\n width: Math.round(width),\n height: Math.round(height),\n });\n }\n });\n\n observer.observe(element);\n\n // Report initial size\n const rect = element.getBoundingClientRect();\n void client.sendSizeChanged({\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n });\n\n return () => observer.disconnect();\n }, [client]);\n\n return containerRef;\n}\n\n// =============================================================================\n// FILE OPERATION HOOKS\n// =============================================================================\n\n/**\n * File upload result\n */\nexport interface FileUploadResult {\n /** Uploaded file ID */\n fileId: string;\n}\n\n/**\n * File upload state\n */\nexport interface UseFileUploadState {\n /** Whether file upload is supported on this platform */\n isSupported: boolean;\n /** Whether upload is in progress */\n isUploading: boolean;\n /** Upload error if any */\n error: Error | null;\n /** Last uploaded file ID */\n fileId: string | null;\n}\n\n/**\n * Hook for uploading files (ChatGPT only)\n *\n * Provides a function to upload files and tracks upload state.\n * On unsupported platforms, isSupported will be false.\n *\n * Supported file types: PNG, JPEG, WebP images\n *\n * @returns Upload function and state\n *\n * @example\n * ```tsx\n * function ImageUploader() {\n * const { upload, isSupported, isUploading, error, fileId } = useFileUpload();\n *\n * if (!isSupported) {\n * return <div>File upload not supported on this platform</div>;\n * }\n *\n * const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0];\n * if (file) {\n * const result = await upload(file);\n * console.log(\"Uploaded:\", result?.fileId);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"image/*\" onChange={handleFileChange} />\n * {isUploading && <span>Uploading...</span>}\n * {error && <span>Error: {error.message}</span>}\n * {fileId && <span>Uploaded: {fileId}</span>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFileUpload(): UseFileUploadState & {\n upload: (file: File) => Promise<FileUploadResult | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<UseFileUploadState>({\n isSupported: false,\n isUploading: false,\n error: null,\n fileId: null,\n });\n\n // Check if upload is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.uploadFile,\n }));\n }, [client]);\n\n const upload = useCallback(\n async (file: File): Promise<FileUploadResult | null> => {\n if (!client?.uploadFile) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File upload not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isUploading: true, error: null }));\n\n try {\n const result = await client.uploadFile(file);\n setState((prev) => ({\n ...prev,\n isUploading: false,\n fileId: result.fileId,\n }));\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isUploading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, upload };\n}\n\n/**\n * Hook for getting file download URLs (ChatGPT only)\n *\n * Provides a function to get temporary download URLs for uploaded files.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Download URL function and state\n *\n * @example\n * ```tsx\n * function FileDownloader({ fileId }: { fileId: string }) {\n * const { getDownloadUrl, isSupported, isLoading, error, downloadUrl } = useFileDownload();\n *\n * useEffect(() => {\n * if (fileId && isSupported) {\n * getDownloadUrl(fileId);\n * }\n * }, [fileId, isSupported, getDownloadUrl]);\n *\n * if (!isSupported) return <div>Not supported</div>;\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (downloadUrl) return <img src={downloadUrl} alt=\"Uploaded file\" />;\n *\n * return null;\n * }\n * ```\n */\nexport function useFileDownload(): {\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n getDownloadUrl: (fileId: string) => Promise<string | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<{\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n }>({\n isSupported: false,\n isLoading: false,\n error: null,\n downloadUrl: null,\n });\n\n // Check if download URL is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.getFileDownloadUrl,\n }));\n }, [client]);\n\n const getDownloadUrl = useCallback(\n async (fileId: string): Promise<string | null> => {\n if (!client?.getFileDownloadUrl) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File download not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n try {\n const result = await client.getFileDownloadUrl(fileId);\n setState((prev) => ({\n ...prev,\n isLoading: false,\n downloadUrl: result.downloadUrl,\n }));\n return result.downloadUrl;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, getDownloadUrl };\n}\n\n// =============================================================================\n// LAYOUT HOOKS\n// =============================================================================\n\n/**\n * Hook to notify host of widget's intrinsic height (ChatGPT only)\n *\n * Automatically reports height changes to prevent scroll clipping.\n * Returns a ref to attach to your root element for automatic height tracking,\n * or a manual notify function for custom height reporting.\n *\n * @returns Ref for auto-tracking and manual notify function\n *\n * @example\n * ```tsx\n * // Automatic height tracking\n * function AutoHeightWidget() {\n * const { containerRef, isSupported } = useIntrinsicHeight();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change height...</p>\n * </div>\n * );\n * }\n *\n * // Manual height notification\n * function ManualHeightWidget() {\n * const { notify, isSupported } = useIntrinsicHeight();\n *\n * useEffect(() => {\n * // Notify after content loads\n * notify(500);\n * }, [notify]);\n *\n * return <div style={{ height: 500 }}>Fixed height content</div>;\n * }\n * ```\n */\nexport function useIntrinsicHeight(): {\n /** Whether intrinsic height notification is supported */\n isSupported: boolean;\n /** Ref to attach to container for automatic height tracking */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Manually notify host of height */\n notify: (height: number) => void;\n} {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.notifyIntrinsicHeight);\n }, [client]);\n\n // Manual notify function\n const notify = useCallback(\n (height: number) => {\n client?.notifyIntrinsicHeight?.(height);\n },\n [client]\n );\n\n // Auto-track height with ResizeObserver\n useEffect(() => {\n if (!client?.notifyIntrinsicHeight || !containerRef.current) return;\n\n const element = containerRef.current;\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const height = entry.contentRect.height;\n client.notifyIntrinsicHeight?.(height);\n }\n });\n\n observer.observe(element);\n\n // Report initial height\n client.notifyIntrinsicHeight(element.offsetHeight);\n\n return () => observer.disconnect();\n }, [client]);\n\n return { isSupported, containerRef, notify };\n}\n\n/**\n * Hook to access the current view identifier (ChatGPT only)\n *\n * Useful for multi-view widgets that need to know which view is active.\n *\n * @returns Current view identifier or undefined\n *\n * @example\n * ```tsx\n * function MultiViewWidget() {\n * const view = useView();\n *\n * switch (view) {\n * case \"settings\":\n * return <SettingsView />;\n * case \"details\":\n * return <DetailsView />;\n * default:\n * return <MainView />;\n * }\n * }\n * ```\n */\nexport function useView(): string | undefined {\n const context = useHostContext();\n return context.view;\n}\n\n// =============================================================================\n// MODAL HOOKS\n// =============================================================================\n\n/**\n * Hook for showing host-owned modal dialogs (ChatGPT only)\n *\n * Spawns native ChatGPT modals for confirmations, inputs, etc.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Modal function and state\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { showModal, isSupported, isOpen } = useModal();\n *\n * const handleDelete = async () => {\n * const result = await showModal({\n * title: \"Confirm Delete\",\n * body: \"Are you sure you want to delete this item?\",\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Delete\", variant: \"destructive\", value: \"delete\" },\n * ],\n * });\n *\n * if (result?.action === \"delete\") {\n * // Perform delete\n * }\n * };\n *\n * if (!isSupported) {\n * return <button onClick={() => window.confirm(\"Delete?\")}>Delete</button>;\n * }\n *\n * return (\n * <button onClick={handleDelete} disabled={isOpen}>\n * Delete\n * </button>\n * );\n * }\n *\n * // Input modal example\n * function RenameButton() {\n * const { showModal } = useModal();\n *\n * const handleRename = async () => {\n * const result = await showModal({\n * title: \"Rename Item\",\n * input: {\n * type: \"text\",\n * placeholder: \"Enter new name\",\n * defaultValue: \"Current Name\",\n * },\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Rename\", variant: \"primary\", value: \"rename\" },\n * ],\n * });\n *\n * if (result?.action === \"rename\" && result.inputValue) {\n * console.log(\"New name:\", result.inputValue);\n * }\n * };\n *\n * return <button onClick={handleRename}>Rename</button>;\n * }\n * ```\n */\nexport function useModal(): {\n /** Whether modal API is supported */\n isSupported: boolean;\n /** Whether a modal is currently open */\n isOpen: boolean;\n /** Show a modal dialog */\n showModal: (options: ModalOptions) => Promise<ModalResult | null>;\n} {\n const { client } = useAppsContext();\n const [isSupported, setIsSupported] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.requestModal);\n }, [client]);\n\n const showModal = useCallback(\n async (options: ModalOptions): Promise<ModalResult | null> => {\n if (!client?.requestModal) {\n return null;\n }\n\n setIsOpen(true);\n try {\n const result = await client.requestModal(options);\n return result;\n } finally {\n setIsOpen(false);\n }\n },\n [client]\n );\n\n return { isSupported, isOpen, showModal };\n}\n\n// =============================================================================\n// DEBUG LOGGING HOOKS\n// =============================================================================\n\n/**\n * Access the debug logger with automatic adapter injection\n *\n * The adapter is automatically configured when AppsProvider connects.\n * Use this hook to access the logger and optionally configure it.\n *\n * @param config - Optional configuration to apply\n * @returns The configured client debug logger\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const logger = useDebugLogger({ enabled: true, level: \"debug\" });\n *\n * const handleClick = () => {\n * logger.info(\"Button clicked\", { timestamp: Date.now() });\n * };\n *\n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n */\nexport function useDebugLogger(config?: Partial<ClientDebugConfig>): ClientDebugLogger {\n // Verify we're inside AppsProvider (adapter is set by createClient)\n useAppsContext();\n\n // Apply configuration when it changes\n useEffect(() => {\n if (config) {\n clientDebugLogger.configure(config);\n }\n }, [config]);\n\n return clientDebugLogger;\n}\n"]}

@@ -1,3 +0,3 @@

import { ToolDefs, AppsClient, ToolResult, HostContext, ModalOptions, ModalResult, ClientDebugConfig, ClientDebugLogger } from '@mcp-apps-kit/ui';
export { AppsClient, ClientDebugConfig, ClientDebugLogger, HostContext, ModalButton, ModalInput, ModalOptions, ModalResult, ToolResult } from '@mcp-apps-kit/ui';
import { ToolDefs, AppsClient, ToolResult, HostContext, ModalOptions, ModalResult, ClientDebugConfig, ClientDebugLogger, HostCapabilities, HostVersion } from '@mcp-apps-kit/ui';
export { AppCapabilities, AppToolDefinition, AppsClient, CallToolHandler, ClientDebugConfig, ClientDebugLogger, HostCapabilities, HostContext, HostVersion, ListToolsHandler, ModalButton, ModalInput, ModalOptions, ModalResult, SizeChangedParams, ToolResult } from '@mcp-apps-kit/ui';
import React, { ReactNode, ComponentType } from 'react';

@@ -271,2 +271,92 @@

/**
* Subscribe to partial/streaming tool input
*
* Called when the host sends partial tool arguments during streaming.
* Useful for showing real-time input as the user types or as the model generates.
*
* @param handler - Callback for partial input
*
* @example
* ```tsx
* function StreamingInput() {
* const [partialInput, setPartialInput] = useState<Record<string, unknown>>({});
*
* useOnToolInputPartial((input) => {
* setPartialInput(input);
* });
*
* return <pre>{JSON.stringify(partialInput, null, 2)}</pre>;
* }
* ```
*/
declare function useOnToolInputPartial(handler: (input: Record<string, unknown>) => void): void;
/**
* Access host capabilities
*
* Returns the capabilities advertised by the host during handshake.
* Use this to check if features like logging or server tools are supported.
*
* @returns Host capabilities or undefined if not yet connected
*
* @example
* ```tsx
* function CapabilitiesDisplay() {
* const capabilities = useHostCapabilities();
*
* return (
* <div>
* <p>Logging: {capabilities?.logging ? "Supported" : "Not supported"}</p>
* <p>Open Links: {capabilities?.openLinks ? "Supported" : "Not supported"}</p>
* </div>
* );
* }
* ```
*/
declare function useHostCapabilities(): HostCapabilities | undefined;
/**
* Access host version information
*
* Returns the name and version of the host application.
*
* @returns Host version info or undefined if not yet connected
*
* @example
* ```tsx
* function HostInfo() {
* const hostVersion = useHostVersion();
*
* if (!hostVersion) return <div>Loading...</div>;
*
* return (
* <div>
* Running on {hostVersion.name} v{hostVersion.version}
* </div>
* );
* }
* ```
*/
declare function useHostVersion(): HostVersion | undefined;
/**
* Hook to set up automatic size change notifications
*
* Creates a ResizeObserver that automatically sends size changed
* notifications to the host when the observed element resizes.
*
* @returns Ref to attach to the element to observe
*
* @example
* ```tsx
* function AutoSizeWidget() {
* const containerRef = useSizeChangedNotifications();
*
* return (
* <div ref={containerRef}>
* <p>Content that may change size...</p>
* </div>
* );
* }
* ```
*/
declare function useSizeChangedNotifications(): React.RefObject<HTMLElement | null>;
/**
* File upload result

@@ -532,2 +622,2 @@ */

export { AppsProvider, type AppsProviderProps, type FileUploadResult, type UseFileUploadState, useAppsClient, useDebugLogger, useDisplayMode, useDocumentTheme, useFileDownload, useFileUpload, useHostContext, useHostStyleVariables, useIntrinsicHeight, useModal, useOnTeardown, useOnToolCancelled, useSafeAreaInsets, useToolInput, useToolResult, useView, useWidgetState };
export { AppsProvider, type AppsProviderProps, type FileUploadResult, type UseFileUploadState, useAppsClient, useDebugLogger, useDisplayMode, useDocumentTheme, useFileDownload, useFileUpload, useHostCapabilities, useHostContext, useHostStyleVariables, useHostVersion, useIntrinsicHeight, useModal, useOnTeardown, useOnToolCancelled, useOnToolInputPartial, useSafeAreaInsets, useSizeChangedNotifications, useToolInput, useToolResult, useView, useWidgetState };

@@ -1,3 +0,3 @@

import { ToolDefs, AppsClient, ToolResult, HostContext, ModalOptions, ModalResult, ClientDebugConfig, ClientDebugLogger } from '@mcp-apps-kit/ui';
export { AppsClient, ClientDebugConfig, ClientDebugLogger, HostContext, ModalButton, ModalInput, ModalOptions, ModalResult, ToolResult } from '@mcp-apps-kit/ui';
import { ToolDefs, AppsClient, ToolResult, HostContext, ModalOptions, ModalResult, ClientDebugConfig, ClientDebugLogger, HostCapabilities, HostVersion } from '@mcp-apps-kit/ui';
export { AppCapabilities, AppToolDefinition, AppsClient, CallToolHandler, ClientDebugConfig, ClientDebugLogger, HostCapabilities, HostContext, HostVersion, ListToolsHandler, ModalButton, ModalInput, ModalOptions, ModalResult, SizeChangedParams, ToolResult } from '@mcp-apps-kit/ui';
import React, { ReactNode, ComponentType } from 'react';

@@ -271,2 +271,92 @@

/**
* Subscribe to partial/streaming tool input
*
* Called when the host sends partial tool arguments during streaming.
* Useful for showing real-time input as the user types or as the model generates.
*
* @param handler - Callback for partial input
*
* @example
* ```tsx
* function StreamingInput() {
* const [partialInput, setPartialInput] = useState<Record<string, unknown>>({});
*
* useOnToolInputPartial((input) => {
* setPartialInput(input);
* });
*
* return <pre>{JSON.stringify(partialInput, null, 2)}</pre>;
* }
* ```
*/
declare function useOnToolInputPartial(handler: (input: Record<string, unknown>) => void): void;
/**
* Access host capabilities
*
* Returns the capabilities advertised by the host during handshake.
* Use this to check if features like logging or server tools are supported.
*
* @returns Host capabilities or undefined if not yet connected
*
* @example
* ```tsx
* function CapabilitiesDisplay() {
* const capabilities = useHostCapabilities();
*
* return (
* <div>
* <p>Logging: {capabilities?.logging ? "Supported" : "Not supported"}</p>
* <p>Open Links: {capabilities?.openLinks ? "Supported" : "Not supported"}</p>
* </div>
* );
* }
* ```
*/
declare function useHostCapabilities(): HostCapabilities | undefined;
/**
* Access host version information
*
* Returns the name and version of the host application.
*
* @returns Host version info or undefined if not yet connected
*
* @example
* ```tsx
* function HostInfo() {
* const hostVersion = useHostVersion();
*
* if (!hostVersion) return <div>Loading...</div>;
*
* return (
* <div>
* Running on {hostVersion.name} v{hostVersion.version}
* </div>
* );
* }
* ```
*/
declare function useHostVersion(): HostVersion | undefined;
/**
* Hook to set up automatic size change notifications
*
* Creates a ResizeObserver that automatically sends size changed
* notifications to the host when the observed element resizes.
*
* @returns Ref to attach to the element to observe
*
* @example
* ```tsx
* function AutoSizeWidget() {
* const containerRef = useSizeChangedNotifications();
*
* return (
* <div ref={containerRef}>
* <p>Content that may change size...</p>
* </div>
* );
* }
* ```
*/
declare function useSizeChangedNotifications(): React.RefObject<HTMLElement | null>;
/**
* File upload result

@@ -532,2 +622,2 @@ */

export { AppsProvider, type AppsProviderProps, type FileUploadResult, type UseFileUploadState, useAppsClient, useDebugLogger, useDisplayMode, useDocumentTheme, useFileDownload, useFileUpload, useHostContext, useHostStyleVariables, useIntrinsicHeight, useModal, useOnTeardown, useOnToolCancelled, useSafeAreaInsets, useToolInput, useToolResult, useView, useWidgetState };
export { AppsProvider, type AppsProviderProps, type FileUploadResult, type UseFileUploadState, useAppsClient, useDebugLogger, useDisplayMode, useDocumentTheme, useFileDownload, useFileUpload, useHostCapabilities, useHostContext, useHostStyleVariables, useHostVersion, useIntrinsicHeight, useModal, useOnTeardown, useOnToolCancelled, useOnToolInputPartial, useSafeAreaInsets, useSizeChangedNotifications, useToolInput, useToolResult, useView, useWidgetState };

@@ -1,2 +0,2 @@

import {createContext,useState,useEffect,useCallback,useRef,useContext}from'react';import {createClient,clientDebugLogger}from'@mcp-apps-kit/ui';import {jsx,Fragment}from'react/jsx-runtime';var y=createContext(null);function v({children:e,client:t,forceAdapter:o,fallback:s,errorFallback:r}){let[n,l]=useState(t??null),[p,f]=useState(!t),[g,T]=useState(null);return useEffect(()=>{if(t){l(t),f(false);return}(async()=>{try{let m=await createClient({forceAdapter:o});l(m),f(!1);}catch(m){T(m instanceof Error?m:new Error(String(m))),f(false);}})();},[t,o]),g&&r?jsx(r,{error:g,reset:()=>T(null)}):p&&s?jsx(Fragment,{children:s}):jsx(y.Provider,{value:{client:n,isConnecting:p,error:g},children:e})}function i(){let e=useContext(y);if(!e)throw new Error("useAppsContext must be used within an AppsProvider");return e}function U(){let{client:e,isConnecting:t,error:o}=i();if(o)throw o;if(t||!e)throw new Error("Client not ready. Make sure AppsProvider has finished connecting.");return e}function A(){let{client:e}=i(),[t,o]=useState(void 0);return useEffect(()=>e?e.onToolResult(r=>{o(r);}):void 0,[e]),t}function I(){let{client:e}=i(),[t,o]=useState(e?.toolInput);return useEffect(()=>e?e.onToolInput(r=>{o(r);}):void 0,[e]),t}function d(){let{client:e}=i(),[t,o]=useState(e?.hostContext??{theme:"light",displayMode:"inline",availableDisplayModes:["inline"],viewport:{width:0,height:0},locale:"en-US",platform:"web"});return useEffect(()=>e?(o(e.hostContext),e.onHostContextChange(r=>{o(r);})):void 0,[e]),t}function F(e){let{client:t}=i(),[o,s]=useState(()=>t?t.getState()??e:e),r=useCallback(n=>{s(l=>{let p=typeof n=="function"?n(l):n;return t?.setState(p),p});},[t]);return [o,r]}function E(){let e=d();useEffect(()=>{let t=e.styles?.variables;if(!t)return;let o=document.documentElement;for(let[s,r]of Object.entries(t))o.style.setProperty(s,r);return ()=>{for(let s of Object.keys(t))o.style.removeProperty(s);}},[e.styles?.variables]);}function P(e="light",t="dark"){let o=d();useEffect(()=>{let{theme:s}=o,r=document.body;return r.classList.remove(e,t),r.classList.add(s==="dark"?t:e),()=>{r.classList.remove(e,t);}},[o.theme,e,t]);}function H(){let e=d(),{client:t}=i(),o=useCallback(async s=>{await t?.requestDisplayMode(s);},[t]);return {mode:e.displayMode,availableModes:e.availableDisplayModes,requestMode:o}}function L(){return d().safeAreaInsets??{top:0,right:0,bottom:0,left:0}}function O(e){let{client:t}=i();useEffect(()=>t?t.onToolCancelled(e):void 0,[t,e]);}function k(e){let{client:t}=i();useEffect(()=>t?t.onTeardown(e):void 0,[t,e]);}function V(){let{client:e}=i(),[t,o]=useState({isSupported:false,isUploading:false,error:null,fileId:null});useEffect(()=>{o(r=>({...r,isSupported:!!e?.uploadFile}));},[e]);let s=useCallback(async r=>{if(!e?.uploadFile)return o(n=>({...n,error:new Error("File upload not supported on this platform")})),null;o(n=>({...n,isUploading:true,error:null}));try{let n=await e.uploadFile(r);return o(l=>({...l,isUploading:!1,fileId:n.fileId})),n}catch(n){let l=n instanceof Error?n:new Error(String(n));return o(p=>({...p,isUploading:false,error:l})),null}},[e]);return {...t,upload:s}}function q(){let{client:e}=i(),[t,o]=useState({isSupported:false,isLoading:false,error:null,downloadUrl:null});useEffect(()=>{o(r=>({...r,isSupported:!!e?.getFileDownloadUrl}));},[e]);let s=useCallback(async r=>{if(!e?.getFileDownloadUrl)return o(n=>({...n,error:new Error("File download not supported on this platform")})),null;o(n=>({...n,isLoading:true,error:null}));try{let n=await e.getFileDownloadUrl(r);return o(l=>({...l,isLoading:!1,downloadUrl:n.downloadUrl})),n.downloadUrl}catch(n){let l=n instanceof Error?n:new Error(String(n));return o(p=>({...p,isLoading:false,error:l})),null}},[e]);return {...t,getDownloadUrl:s}}function N(){let{client:e}=i(),t=useRef(null),[o,s]=useState(false);useEffect(()=>{s(!!e?.notifyIntrinsicHeight);},[e]);let r=useCallback(n=>{e?.notifyIntrinsicHeight?.(n);},[e]);return useEffect(()=>{if(!e?.notifyIntrinsicHeight||!t.current)return;let n=t.current,l=new ResizeObserver(p=>{for(let f of p){let g=f.contentRect.height;e.notifyIntrinsicHeight?.(g);}});return l.observe(n),e.notifyIntrinsicHeight(n.offsetHeight),()=>l.disconnect()},[e]),{isSupported:o,containerRef:t,notify:r}}function j(){return d().view}function W(){let{client:e}=i(),[t,o]=useState(false),[s,r]=useState(false);useEffect(()=>{o(!!e?.requestModal);},[e]);let n=useCallback(async l=>{if(!e?.requestModal)return null;r(true);try{return await e.requestModal(l)}finally{r(false);}},[e]);return {isSupported:t,isOpen:s,showModal:n}}function z(e){return i(),useEffect(()=>{e&&clientDebugLogger.configure(e);},[e]),clientDebugLogger}export{v as AppsProvider,U as useAppsClient,z as useDebugLogger,H as useDisplayMode,P as useDocumentTheme,q as useFileDownload,V as useFileUpload,d as useHostContext,E as useHostStyleVariables,N as useIntrinsicHeight,W as useModal,k as useOnTeardown,O as useOnToolCancelled,L as useSafeAreaInsets,I as useToolInput,A as useToolResult,j as useView,F as useWidgetState};//# sourceMappingURL=index.js.map
import {createContext,useState,useEffect,useCallback,useRef,useContext}from'react';import {createClient,clientDebugLogger}from'@mcp-apps-kit/ui';import {jsx,Fragment}from'react/jsx-runtime';var C=createContext(null);function H({children:e,client:t,forceAdapter:o,fallback:s,errorFallback:r}){let[n,i]=useState(t??null),[a,p]=useState(!t),[g,T]=useState(null);return useEffect(()=>{if(t){i(t),p(false);return}(async()=>{try{let b=await createClient({forceAdapter:o});i(b),p(!1);}catch(b){T(b instanceof Error?b:new Error(String(b))),p(false);}})();},[t,o]),g&&r?jsx(r,{error:g,reset:()=>T(null)}):a&&s?jsx(Fragment,{children:s}):jsx(C.Provider,{value:{client:n,isConnecting:a,error:g},children:e})}function l(){let e=useContext(C);if(!e)throw new Error("useAppsContext must be used within an AppsProvider");return e}function M(){let{client:e,isConnecting:t,error:o}=l();if(o)throw o;if(t||!e)throw new Error("Client not ready. Make sure AppsProvider has finished connecting.");return e}function A(){let{client:e}=l(),[t,o]=useState(void 0);return useEffect(()=>e?e.onToolResult(r=>{o(r);}):void 0,[e]),t}function U(){let{client:e}=l(),[t,o]=useState(e?.toolInput);return useEffect(()=>e?e.onToolInput(r=>{o(r);}):void 0,[e]),t}function f(){let{client:e}=l(),[t,o]=useState(e?.hostContext??{theme:"light",displayMode:"inline",availableDisplayModes:["inline"],viewport:{width:0,height:0},locale:"en-US",platform:"web"});return useEffect(()=>e?(o(e.hostContext),e.onHostContextChange(r=>{o(r);})):void 0,[e]),t}function I(e){let{client:t}=l(),[o,s]=useState(()=>t?t.getState()??e:e),r=useCallback(n=>{s(i=>{let a=typeof n=="function"?n(i):n;return t?.setState(a),a});},[t]);return [o,r]}function P(){let e=f();useEffect(()=>{let t=e.styles?.variables;if(!t)return;let o=document.documentElement;for(let[s,r]of Object.entries(t))o.style.setProperty(s,r);return ()=>{for(let s of Object.keys(t))o.style.removeProperty(s);}},[e.styles?.variables]);}function E(e="light",t="dark"){let o=f();useEffect(()=>{let{theme:s}=o,r=document.body;return r.classList.remove(e,t),r.classList.add(s==="dark"?t:e),()=>{r.classList.remove(e,t);}},[o.theme,e,t]);}function F(){let e=f(),{client:t}=l(),o=useCallback(async s=>{await t?.requestDisplayMode(s);},[t]);return {mode:e.displayMode,availableModes:e.availableDisplayModes,requestMode:o}}function L(){return f().safeAreaInsets??{top:0,right:0,bottom:0,left:0}}function O(e){let{client:t}=l();useEffect(()=>t?t.onToolCancelled(e):void 0,[t,e]);}function V(e){let{client:t}=l();useEffect(()=>t?t.onTeardown(e):void 0,[t,e]);}function k(e){let{client:t}=l();useEffect(()=>t?t.onToolInputPartial(e):void 0,[t,e]);}function z(){let{client:e}=l(),[t,o]=useState(e?.getHostCapabilities());return useEffect(()=>{e&&o(e.getHostCapabilities());},[e]),t}function q(){let{client:e}=l(),[t,o]=useState(e?.getHostVersion());return useEffect(()=>{e&&o(e.getHostVersion());},[e]),t}function N(){let{client:e}=l(),t=useRef(null);return useEffect(()=>{if(!e||typeof ResizeObserver>"u")return;let o=t.current;if(!o)return;let s=new ResizeObserver(n=>{for(let i of n){let{width:a,height:p}=i.contentRect;e.sendSizeChanged({width:Math.round(a),height:Math.round(p)});}});s.observe(o);let r=o.getBoundingClientRect();return e.sendSizeChanged({width:Math.round(r.width),height:Math.round(r.height)}),()=>s.disconnect()},[e]),t}function j(){let{client:e}=l(),[t,o]=useState({isSupported:false,isUploading:false,error:null,fileId:null});useEffect(()=>{o(r=>({...r,isSupported:!!e?.uploadFile}));},[e]);let s=useCallback(async r=>{if(!e?.uploadFile)return o(n=>({...n,error:new Error("File upload not supported on this platform")})),null;o(n=>({...n,isUploading:true,error:null}));try{let n=await e.uploadFile(r);return o(i=>({...i,isUploading:!1,fileId:n.fileId})),n}catch(n){let i=n instanceof Error?n:new Error(String(n));return o(a=>({...a,isUploading:false,error:i})),null}},[e]);return {...t,upload:s}}function B(){let{client:e}=l(),[t,o]=useState({isSupported:false,isLoading:false,error:null,downloadUrl:null});useEffect(()=>{o(r=>({...r,isSupported:!!e?.getFileDownloadUrl}));},[e]);let s=useCallback(async r=>{if(!e?.getFileDownloadUrl)return o(n=>({...n,error:new Error("File download not supported on this platform")})),null;o(n=>({...n,isLoading:true,error:null}));try{let n=await e.getFileDownloadUrl(r);return o(i=>({...i,isLoading:!1,downloadUrl:n.downloadUrl})),n.downloadUrl}catch(n){let i=n instanceof Error?n:new Error(String(n));return o(a=>({...a,isLoading:false,error:i})),null}},[e]);return {...t,getDownloadUrl:s}}function W(){let{client:e}=l(),t=useRef(null),[o,s]=useState(false);useEffect(()=>{s(!!e?.notifyIntrinsicHeight);},[e]);let r=useCallback(n=>{e?.notifyIntrinsicHeight?.(n);},[e]);return useEffect(()=>{if(!e?.notifyIntrinsicHeight||!t.current)return;let n=t.current,i=new ResizeObserver(a=>{for(let p of a){let g=p.contentRect.height;e.notifyIntrinsicHeight?.(g);}});return i.observe(n),e.notifyIntrinsicHeight(n.offsetHeight),()=>i.disconnect()},[e]),{isSupported:o,containerRef:t,notify:r}}function J(){return f().view}function X(){let{client:e}=l(),[t,o]=useState(false),[s,r]=useState(false);useEffect(()=>{o(!!e?.requestModal);},[e]);let n=useCallback(async i=>{if(!e?.requestModal)return null;r(true);try{return await e.requestModal(i)}finally{r(false);}},[e]);return {isSupported:t,isOpen:s,showModal:n}}function G(e){return l(),useEffect(()=>{e&&clientDebugLogger.configure(e);},[e]),clientDebugLogger}export{H as AppsProvider,M as useAppsClient,G as useDebugLogger,F as useDisplayMode,E as useDocumentTheme,B as useFileDownload,j as useFileUpload,z as useHostCapabilities,f as useHostContext,P as useHostStyleVariables,q as useHostVersion,W as useIntrinsicHeight,X as useModal,V as useOnTeardown,O as useOnToolCancelled,k as useOnToolInputPartial,L as useSafeAreaInsets,N as useSizeChangedNotifications,U as useToolInput,A as useToolResult,J as useView,I as useWidgetState};//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["AppsContext","createContext","AppsProvider","children","providedClient","_forceAdapter","fallback","ErrorFallback","client","setClient","useState","isConnecting","setIsConnecting","error","setError","useEffect","newClient","createClient","err","jsx","Fragment","useAppsContext","context","useContext","useAppsClient","useToolResult","result","setResult","newResult","useToolInput","input","setInput","newInput","useHostContext","setContext","newContext","useWidgetState","defaultValue","state","setStateInternal","setState","useCallback","newState","prev","next","useHostStyleVariables","variables","root","key","value","useDocumentTheme","lightClass","darkClass","theme","body","useDisplayMode","requestMode","mode","useSafeAreaInsets","useOnToolCancelled","handler","useOnTeardown","useFileUpload","upload","file","useFileDownload","getDownloadUrl","fileId","useIntrinsicHeight","containerRef","useRef","isSupported","setIsSupported","notify","height","element","observer","entries","entry","useView","useModal","isOpen","setIsOpen","showModal","options","useDebugLogger","config","clientDebugLogger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * React context and provider for @mcp-apps-kit/ui-react\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport type { AppsClient, ToolDefs } from \"@mcp-apps-kit/ui\";\nimport { createClient } from \"@mcp-apps-kit/ui\";\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\ninterface AppsContextValue<T extends ToolDefs = ToolDefs> {\n client: AppsClient<T> | null;\n isConnecting: boolean;\n error: Error | null;\n}\n\nconst AppsContext = createContext<AppsContextValue | null>(null);\n\n// =============================================================================\n// PROVIDER PROPS\n// =============================================================================\n\n/**\n * Props for AppsProvider\n */\nexport interface AppsProviderProps<T extends ToolDefs = ToolDefs> {\n /** Child components */\n children: ReactNode;\n\n /**\n * Pre-created client instance (optional)\n * If not provided, creates client automatically\n */\n client?: AppsClient<T>;\n\n /**\n * Force a specific protocol adapter\n */\n forceAdapter?: \"mcp\" | \"openai\" | \"mock\";\n\n /**\n * Fallback UI while client is connecting\n */\n fallback?: ReactNode;\n\n /**\n * Error boundary fallback\n */\n errorFallback?: ComponentType<{ error: Error; reset: () => void }>;\n}\n\n// =============================================================================\n// PROVIDER\n// =============================================================================\n\n/**\n * Context provider for mcp-apps-kit React integration\n *\n * Wraps your app and provides the client instance to all hooks.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AppsProvider fallback={<Loading />}>\n * <MyWidget />\n * </AppsProvider>\n * );\n * }\n * ```\n */\nexport function AppsProvider<T extends ToolDefs = ToolDefs>({\n children,\n client: providedClient,\n forceAdapter: _forceAdapter,\n fallback,\n errorFallback: ErrorFallback,\n}: AppsProviderProps<T>): React.JSX.Element {\n const [client, setClient] = useState<AppsClient<T> | null>(providedClient ?? null);\n const [isConnecting, setIsConnecting] = useState(!providedClient);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (providedClient) {\n setClient(providedClient);\n setIsConnecting(false);\n return;\n }\n\n // Initialize client with auto-detection or forced adapter\n const initClient = async () => {\n try {\n const newClient = await createClient<T>({\n forceAdapter: _forceAdapter,\n });\n setClient(newClient);\n setIsConnecting(false);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsConnecting(false);\n }\n };\n\n void initClient();\n }, [providedClient, _forceAdapter]);\n\n if (error && ErrorFallback) {\n return <ErrorFallback error={error} reset={() => setError(null)} />;\n }\n\n if (isConnecting && fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <AppsContext.Provider value={{ client, isConnecting, error }}>{children}</AppsContext.Provider>\n );\n}\n\n// =============================================================================\n// INTERNAL HOOK\n// =============================================================================\n\n/**\n * Internal hook to access the context\n * @internal\n */\nexport function useAppsContext<T extends ToolDefs = ToolDefs>(): AppsContextValue<T> {\n const context = useContext(AppsContext);\n if (!context) {\n throw new Error(\"useAppsContext must be used within an AppsProvider\");\n }\n return context as AppsContextValue<T>;\n}\n","/**\n * React hooks for @mcp-apps-kit/ui-react\n */\n\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport type {\n AppsClient,\n HostContext,\n ToolDefs,\n ToolResult,\n ClientDebugConfig,\n ModalOptions,\n ModalResult,\n} from \"@mcp-apps-kit/ui\";\nimport { clientDebugLogger, type ClientDebugLogger } from \"@mcp-apps-kit/ui\";\nimport { useAppsContext } from \"./context\";\n\n// =============================================================================\n// CORE HOOKS\n// =============================================================================\n\n/**\n * Access the typed client instance\n *\n * @returns Client instance\n * @throws Error if used outside AppsProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useAppsClient<typeof app.tools>();\n *\n * const handleClick = async () => {\n * await client.callTool(\"myTool\", { arg: \"value\" });\n * };\n *\n * return <button onClick={handleClick}>Call Tool</button>;\n * }\n * ```\n */\nexport function useAppsClient<T extends ToolDefs = ToolDefs>(): AppsClient<T> {\n const { client, isConnecting, error } = useAppsContext<T>();\n\n if (error) {\n throw error;\n }\n\n if (isConnecting || !client) {\n throw new Error(\"Client not ready. Make sure AppsProvider has finished connecting.\");\n }\n\n return client;\n}\n\n/**\n * Access current tool result with automatic re-renders\n *\n * @returns Current tool result or undefined\n *\n * @example\n * ```tsx\n * function ResultDisplay() {\n * const result = useToolResult<typeof app.tools>();\n *\n * if (!result?.myTool) {\n * return <div>No results yet</div>;\n * }\n *\n * return <div>{result.myTool.message}</div>;\n * }\n * ```\n */\nexport function useToolResult<T extends ToolDefs = ToolDefs>(): ToolResult<T> | undefined {\n const { client } = useAppsContext<T>();\n const [result, setResult] = useState<ToolResult<T> | undefined>(undefined);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolResult((newResult) => {\n setResult(newResult as ToolResult<T>);\n });\n\n return unsubscribe;\n }, [client]);\n\n return result;\n}\n\n/**\n * Access current tool input\n *\n * @returns Current tool input or undefined\n *\n * @example\n * ```tsx\n * function InputDisplay() {\n * const input = useToolInput();\n * return <pre>{JSON.stringify(input, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useToolInput(): Record<string, unknown> | undefined {\n const { client } = useAppsContext();\n const [input, setInput] = useState<Record<string, unknown> | undefined>(client?.toolInput);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInput((newInput) => {\n setInput(newInput);\n });\n\n return unsubscribe;\n }, [client]);\n\n return input;\n}\n\n/**\n * Access host context with automatic re-renders on changes\n *\n * @returns Current host context\n *\n * @example\n * ```tsx\n * function ThemedComponent() {\n * const context = useHostContext();\n *\n * return (\n * <div className={context.theme}>\n * Display: {context.displayMode}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostContext(): HostContext {\n const { client } = useAppsContext();\n const [context, setContext] = useState<HostContext>(\n client?.hostContext ?? {\n theme: \"light\",\n displayMode: \"inline\",\n availableDisplayModes: [\"inline\"],\n viewport: { width: 0, height: 0 },\n locale: \"en-US\",\n platform: \"web\",\n }\n );\n\n useEffect(() => {\n if (!client) return;\n\n setContext(client.hostContext);\n\n const unsubscribe = client.onHostContextChange((newContext) => {\n setContext(newContext);\n });\n\n return unsubscribe;\n }, [client]);\n\n return context;\n}\n\n/**\n * Persisted widget state with automatic sync\n *\n * Works like useState but persists across widget reloads.\n * On ChatGPT: Session-scoped persistence\n * On MCP Apps: Silent no-op (returns default, setState is ignored)\n *\n * @param defaultValue - Initial state value\n * @returns [state, setState] tuple\n *\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount] = useWidgetState(0);\n *\n * return (\n * <button onClick={() => setCount(c => c + 1)}>\n * Count: {count}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWidgetState<S>(defaultValue: S): [S, (newState: S | ((prev: S) => S)) => void] {\n const { client } = useAppsContext();\n\n const [state, setStateInternal] = useState<S>(() => {\n if (!client) return defaultValue;\n const stored = client.getState<S>();\n return stored ?? defaultValue;\n });\n\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setStateInternal((prev) => {\n const next = typeof newState === \"function\" ? (newState as (prev: S) => S)(prev) : newState;\n\n // Persist to client (silent no-op on MCP Apps)\n client?.setState(next);\n\n return next;\n });\n },\n [client]\n );\n\n return [state, setState];\n}\n\n// =============================================================================\n// UTILITY HOOKS\n// =============================================================================\n\n/**\n * Apply host CSS variables to document root\n *\n * Call this once in your root component to apply host theming.\n *\n * @example\n * ```tsx\n * function App() {\n * useHostStyleVariables();\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useHostStyleVariables(): void {\n const context = useHostContext();\n\n useEffect(() => {\n const variables = context.styles?.variables;\n if (!variables) return;\n\n const root = document.documentElement;\n for (const [key, value] of Object.entries(variables)) {\n root.style.setProperty(key, value);\n }\n\n // Cleanup\n return () => {\n for (const key of Object.keys(variables)) {\n root.style.removeProperty(key);\n }\n };\n }, [context.styles?.variables]);\n}\n\n/**\n * Apply theme class to document body\n *\n * @param lightClass - Class name for light theme (default: \"light\")\n * @param darkClass - Class name for dark theme (default: \"dark\")\n *\n * @example\n * ```tsx\n * function App() {\n * useDocumentTheme(\"theme-light\", \"theme-dark\");\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useDocumentTheme(lightClass = \"light\", darkClass = \"dark\"): void {\n const context = useHostContext();\n\n useEffect(() => {\n const { theme } = context;\n const body = document.body;\n\n body.classList.remove(lightClass, darkClass);\n body.classList.add(theme === \"dark\" ? darkClass : lightClass);\n\n return () => {\n body.classList.remove(lightClass, darkClass);\n };\n }, [context.theme, lightClass, darkClass]);\n}\n\n/**\n * Access and manage display mode\n *\n * @returns Display mode state and controls\n *\n * @example\n * ```tsx\n * function DisplayModeToggle() {\n * const { mode, availableModes, requestMode } = useDisplayMode();\n *\n * return (\n * <select value={mode} onChange={e => requestMode(e.target.value)}>\n * {availableModes.map(m => (\n * <option key={m} value={m}>{m}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useDisplayMode(): {\n mode: string;\n availableModes: string[];\n requestMode: (mode: \"inline\" | \"fullscreen\" | \"pip\") => Promise<void>;\n} {\n const context = useHostContext();\n const { client } = useAppsContext();\n\n const requestMode = useCallback(\n async (mode: \"inline\" | \"fullscreen\" | \"pip\") => {\n await client?.requestDisplayMode(mode);\n },\n [client]\n );\n\n return {\n mode: context.displayMode,\n availableModes: context.availableDisplayModes,\n requestMode,\n };\n}\n\n/**\n * Access safe area insets for mobile layouts\n *\n * @returns Safe area insets or default zeros\n *\n * @example\n * ```tsx\n * function SafeContent() {\n * const insets = useSafeAreaInsets();\n *\n * return (\n * <div style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>\n * Content\n * </div>\n * );\n * }\n * ```\n */\nexport function useSafeAreaInsets(): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const context = useHostContext();\n\n return (\n context.safeAreaInsets ?? {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }\n );\n}\n\n// =============================================================================\n// EVENT HOOKS\n// =============================================================================\n\n/**\n * Subscribe to tool cancellation\n *\n * @param handler - Callback when tool is cancelled\n *\n * @example\n * ```tsx\n * function CancellableOperation() {\n * const [cancelled, setCancelled] = useState(false);\n *\n * useOnToolCancelled((reason) => {\n * setCancelled(true);\n * console.log(\"Cancelled:\", reason);\n * });\n *\n * return cancelled ? <div>Cancelled</div> : <div>Running...</div>;\n * }\n * ```\n */\nexport function useOnToolCancelled(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolCancelled(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to teardown events\n *\n * @param handler - Callback when widget is torn down\n *\n * @example\n * ```tsx\n * function CleanupComponent() {\n * useOnTeardown((reason) => {\n * console.log(\"Tearing down:\", reason);\n * // Cleanup resources\n * });\n *\n * return <div>Widget</div>;\n * }\n * ```\n */\nexport function useOnTeardown(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onTeardown(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n// =============================================================================\n// FILE OPERATION HOOKS\n// =============================================================================\n\n/**\n * File upload result\n */\nexport interface FileUploadResult {\n /** Uploaded file ID */\n fileId: string;\n}\n\n/**\n * File upload state\n */\nexport interface UseFileUploadState {\n /** Whether file upload is supported on this platform */\n isSupported: boolean;\n /** Whether upload is in progress */\n isUploading: boolean;\n /** Upload error if any */\n error: Error | null;\n /** Last uploaded file ID */\n fileId: string | null;\n}\n\n/**\n * Hook for uploading files (ChatGPT only)\n *\n * Provides a function to upload files and tracks upload state.\n * On unsupported platforms, isSupported will be false.\n *\n * Supported file types: PNG, JPEG, WebP images\n *\n * @returns Upload function and state\n *\n * @example\n * ```tsx\n * function ImageUploader() {\n * const { upload, isSupported, isUploading, error, fileId } = useFileUpload();\n *\n * if (!isSupported) {\n * return <div>File upload not supported on this platform</div>;\n * }\n *\n * const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0];\n * if (file) {\n * const result = await upload(file);\n * console.log(\"Uploaded:\", result?.fileId);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"image/*\" onChange={handleFileChange} />\n * {isUploading && <span>Uploading...</span>}\n * {error && <span>Error: {error.message}</span>}\n * {fileId && <span>Uploaded: {fileId}</span>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFileUpload(): UseFileUploadState & {\n upload: (file: File) => Promise<FileUploadResult | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<UseFileUploadState>({\n isSupported: false,\n isUploading: false,\n error: null,\n fileId: null,\n });\n\n // Check if upload is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.uploadFile,\n }));\n }, [client]);\n\n const upload = useCallback(\n async (file: File): Promise<FileUploadResult | null> => {\n if (!client?.uploadFile) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File upload not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isUploading: true, error: null }));\n\n try {\n const result = await client.uploadFile(file);\n setState((prev) => ({\n ...prev,\n isUploading: false,\n fileId: result.fileId,\n }));\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isUploading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, upload };\n}\n\n/**\n * Hook for getting file download URLs (ChatGPT only)\n *\n * Provides a function to get temporary download URLs for uploaded files.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Download URL function and state\n *\n * @example\n * ```tsx\n * function FileDownloader({ fileId }: { fileId: string }) {\n * const { getDownloadUrl, isSupported, isLoading, error, downloadUrl } = useFileDownload();\n *\n * useEffect(() => {\n * if (fileId && isSupported) {\n * getDownloadUrl(fileId);\n * }\n * }, [fileId, isSupported, getDownloadUrl]);\n *\n * if (!isSupported) return <div>Not supported</div>;\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (downloadUrl) return <img src={downloadUrl} alt=\"Uploaded file\" />;\n *\n * return null;\n * }\n * ```\n */\nexport function useFileDownload(): {\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n getDownloadUrl: (fileId: string) => Promise<string | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<{\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n }>({\n isSupported: false,\n isLoading: false,\n error: null,\n downloadUrl: null,\n });\n\n // Check if download URL is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.getFileDownloadUrl,\n }));\n }, [client]);\n\n const getDownloadUrl = useCallback(\n async (fileId: string): Promise<string | null> => {\n if (!client?.getFileDownloadUrl) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File download not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n try {\n const result = await client.getFileDownloadUrl(fileId);\n setState((prev) => ({\n ...prev,\n isLoading: false,\n downloadUrl: result.downloadUrl,\n }));\n return result.downloadUrl;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, getDownloadUrl };\n}\n\n// =============================================================================\n// LAYOUT HOOKS\n// =============================================================================\n\n/**\n * Hook to notify host of widget's intrinsic height (ChatGPT only)\n *\n * Automatically reports height changes to prevent scroll clipping.\n * Returns a ref to attach to your root element for automatic height tracking,\n * or a manual notify function for custom height reporting.\n *\n * @returns Ref for auto-tracking and manual notify function\n *\n * @example\n * ```tsx\n * // Automatic height tracking\n * function AutoHeightWidget() {\n * const { containerRef, isSupported } = useIntrinsicHeight();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change height...</p>\n * </div>\n * );\n * }\n *\n * // Manual height notification\n * function ManualHeightWidget() {\n * const { notify, isSupported } = useIntrinsicHeight();\n *\n * useEffect(() => {\n * // Notify after content loads\n * notify(500);\n * }, [notify]);\n *\n * return <div style={{ height: 500 }}>Fixed height content</div>;\n * }\n * ```\n */\nexport function useIntrinsicHeight(): {\n /** Whether intrinsic height notification is supported */\n isSupported: boolean;\n /** Ref to attach to container for automatic height tracking */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Manually notify host of height */\n notify: (height: number) => void;\n} {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.notifyIntrinsicHeight);\n }, [client]);\n\n // Manual notify function\n const notify = useCallback(\n (height: number) => {\n client?.notifyIntrinsicHeight?.(height);\n },\n [client]\n );\n\n // Auto-track height with ResizeObserver\n useEffect(() => {\n if (!client?.notifyIntrinsicHeight || !containerRef.current) return;\n\n const element = containerRef.current;\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const height = entry.contentRect.height;\n client.notifyIntrinsicHeight?.(height);\n }\n });\n\n observer.observe(element);\n\n // Report initial height\n client.notifyIntrinsicHeight(element.offsetHeight);\n\n return () => observer.disconnect();\n }, [client]);\n\n return { isSupported, containerRef, notify };\n}\n\n/**\n * Hook to access the current view identifier (ChatGPT only)\n *\n * Useful for multi-view widgets that need to know which view is active.\n *\n * @returns Current view identifier or undefined\n *\n * @example\n * ```tsx\n * function MultiViewWidget() {\n * const view = useView();\n *\n * switch (view) {\n * case \"settings\":\n * return <SettingsView />;\n * case \"details\":\n * return <DetailsView />;\n * default:\n * return <MainView />;\n * }\n * }\n * ```\n */\nexport function useView(): string | undefined {\n const context = useHostContext();\n return context.view;\n}\n\n// =============================================================================\n// MODAL HOOKS\n// =============================================================================\n\n/**\n * Hook for showing host-owned modal dialogs (ChatGPT only)\n *\n * Spawns native ChatGPT modals for confirmations, inputs, etc.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Modal function and state\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { showModal, isSupported, isOpen } = useModal();\n *\n * const handleDelete = async () => {\n * const result = await showModal({\n * title: \"Confirm Delete\",\n * body: \"Are you sure you want to delete this item?\",\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Delete\", variant: \"destructive\", value: \"delete\" },\n * ],\n * });\n *\n * if (result?.action === \"delete\") {\n * // Perform delete\n * }\n * };\n *\n * if (!isSupported) {\n * return <button onClick={() => window.confirm(\"Delete?\")}>Delete</button>;\n * }\n *\n * return (\n * <button onClick={handleDelete} disabled={isOpen}>\n * Delete\n * </button>\n * );\n * }\n *\n * // Input modal example\n * function RenameButton() {\n * const { showModal } = useModal();\n *\n * const handleRename = async () => {\n * const result = await showModal({\n * title: \"Rename Item\",\n * input: {\n * type: \"text\",\n * placeholder: \"Enter new name\",\n * defaultValue: \"Current Name\",\n * },\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Rename\", variant: \"primary\", value: \"rename\" },\n * ],\n * });\n *\n * if (result?.action === \"rename\" && result.inputValue) {\n * console.log(\"New name:\", result.inputValue);\n * }\n * };\n *\n * return <button onClick={handleRename}>Rename</button>;\n * }\n * ```\n */\nexport function useModal(): {\n /** Whether modal API is supported */\n isSupported: boolean;\n /** Whether a modal is currently open */\n isOpen: boolean;\n /** Show a modal dialog */\n showModal: (options: ModalOptions) => Promise<ModalResult | null>;\n} {\n const { client } = useAppsContext();\n const [isSupported, setIsSupported] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.requestModal);\n }, [client]);\n\n const showModal = useCallback(\n async (options: ModalOptions): Promise<ModalResult | null> => {\n if (!client?.requestModal) {\n return null;\n }\n\n setIsOpen(true);\n try {\n const result = await client.requestModal(options);\n return result;\n } finally {\n setIsOpen(false);\n }\n },\n [client]\n );\n\n return { isSupported, isOpen, showModal };\n}\n\n// =============================================================================\n// DEBUG LOGGING HOOKS\n// =============================================================================\n\n/**\n * Access the debug logger with automatic adapter injection\n *\n * The adapter is automatically configured when AppsProvider connects.\n * Use this hook to access the logger and optionally configure it.\n *\n * @param config - Optional configuration to apply\n * @returns The configured client debug logger\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const logger = useDebugLogger({ enabled: true, level: \"debug\" });\n *\n * const handleClick = () => {\n * logger.info(\"Button clicked\", { timestamp: Date.now() });\n * };\n *\n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n */\nexport function useDebugLogger(config?: Partial<ClientDebugConfig>): ClientDebugLogger {\n // Verify we're inside AppsProvider (adapter is set by createClient)\n useAppsContext();\n\n // Apply configuration when it changes\n useEffect(() => {\n if (config) {\n clientDebugLogger.configure(config);\n }\n }, [config]);\n\n return clientDebugLogger;\n}\n"]}
{"version":3,"sources":["../src/context.tsx","../src/hooks.ts"],"names":["AppsContext","createContext","AppsProvider","children","providedClient","_forceAdapter","fallback","ErrorFallback","client","setClient","useState","isConnecting","setIsConnecting","error","setError","useEffect","newClient","createClient","err","jsx","Fragment","useAppsContext","context","useContext","useAppsClient","useToolResult","result","setResult","newResult","useToolInput","input","setInput","newInput","useHostContext","setContext","newContext","useWidgetState","defaultValue","state","setStateInternal","setState","useCallback","newState","prev","next","useHostStyleVariables","variables","root","key","value","useDocumentTheme","lightClass","darkClass","theme","body","useDisplayMode","requestMode","mode","useSafeAreaInsets","useOnToolCancelled","handler","useOnTeardown","useOnToolInputPartial","useHostCapabilities","capabilities","setCapabilities","useHostVersion","version","setVersion","useSizeChangedNotifications","containerRef","useRef","element","observer","entries","entry","width","height","rect","useFileUpload","upload","file","useFileDownload","getDownloadUrl","fileId","useIntrinsicHeight","isSupported","setIsSupported","notify","useView","useModal","isOpen","setIsOpen","showModal","options","useDebugLogger","config","clientDebugLogger"],"mappings":"8LAyBA,IAAMA,CAAAA,CAAcC,cAAuC,IAAI,CAAA,CAuDxD,SAASC,CAAAA,CAA4C,CAC1D,QAAA,CAAAC,CAAAA,CACA,MAAA,CAAQC,CAAAA,CACR,aAAcC,CAAAA,CACd,QAAA,CAAAC,EACA,aAAA,CAAeC,CACjB,EAA4C,CAC1C,GAAM,CAACC,CAAAA,CAAQC,CAAS,CAAA,CAAIC,QAAAA,CAA+BN,CAAAA,EAAkB,IAAI,EAC3E,CAACO,CAAAA,CAAcC,CAAe,CAAA,CAAIF,SAAS,CAACN,CAAc,EAC1D,CAACS,CAAAA,CAAOC,CAAQ,CAAA,CAAIJ,QAAAA,CAAuB,IAAI,CAAA,CA0BrD,OAxBAK,SAAAA,CAAU,IAAM,CACd,GAAIX,CAAAA,CAAgB,CAClBK,CAAAA,CAAUL,CAAc,CAAA,CACxBQ,CAAAA,CAAgB,KAAK,CAAA,CACrB,MACF,EAGmB,SAAY,CAC7B,GAAI,CACF,IAAMI,CAAAA,CAAY,MAAMC,aAAgB,CACtC,YAAA,CAAcZ,CAChB,CAAC,EACDI,CAAAA,CAAUO,CAAS,CAAA,CACnBJ,CAAAA,CAAgB,EAAK,EACvB,CAAA,MAASM,EAAK,CACZJ,CAAAA,CAASI,aAAe,KAAA,CAAQA,CAAAA,CAAM,IAAI,KAAA,CAAM,OAAOA,CAAG,CAAC,CAAC,CAAA,CAC5DN,CAAAA,CAAgB,KAAK,EACvB,CACF,CAAA,IAGF,EAAG,CAACR,CAAAA,CAAgBC,CAAa,CAAC,CAAA,CAE9BQ,GAASN,CAAAA,CACJY,GAAAA,CAACZ,CAAAA,CAAA,CAAc,MAAOM,CAAAA,CAAO,KAAA,CAAO,IAAMC,CAAAA,CAAS,IAAI,CAAA,CAAG,CAAA,CAG/DH,CAAAA,EAAgBL,CAAAA,CACXa,IAAAC,QAAAA,CAAA,CAAG,SAAAd,CAAAA,CAAS,CAAA,CAInBa,IAACnB,CAAAA,CAAY,QAAA,CAAZ,CAAqB,KAAA,CAAO,CAAE,MAAA,CAAAQ,CAAAA,CAAQ,aAAAG,CAAAA,CAAc,KAAA,CAAAE,CAAM,CAAA,CAAI,QAAA,CAAAV,CAAAA,CAAS,CAE5E,CAUO,SAASkB,CAAAA,EAAqE,CACnF,IAAMC,CAAAA,CAAUC,WAAWvB,CAAW,CAAA,CACtC,GAAI,CAACsB,EACH,MAAM,IAAI,KAAA,CAAM,oDAAoD,EAEtE,OAAOA,CACT,CCpGO,SAASE,CAAAA,EAA8D,CAC5E,GAAM,CAAE,MAAA,CAAAhB,CAAAA,CAAQ,YAAA,CAAAG,EAAc,KAAA,CAAAE,CAAM,EAAIQ,CAAAA,EAAkB,CAE1D,GAAIR,CAAAA,CACF,MAAMA,CAAAA,CAGR,GAAIF,GAAgB,CAACH,CAAAA,CACnB,MAAM,IAAI,MAAM,mEAAmE,CAAA,CAGrF,OAAOA,CACT,CAoBO,SAASiB,CAAAA,EAA0E,CACxF,GAAM,CAAE,OAAAjB,CAAO,CAAA,CAAIa,CAAAA,EAAkB,CAC/B,CAACK,CAAAA,CAAQC,CAAS,EAAIjB,QAAAA,CAAoC,MAAS,EAEzE,OAAAK,SAAAA,CAAU,IACHP,CAAAA,CAEeA,EAAO,YAAA,CAAcoB,CAAAA,EAAc,CACrDD,CAAAA,CAAUC,CAA0B,EACtC,CAAC,CAAA,CAJY,MAAA,CAOZ,CAACpB,CAAM,CAAC,CAAA,CAEJkB,CACT,CAeO,SAASG,CAAAA,EAAoD,CAClE,GAAM,CAAE,OAAArB,CAAO,CAAA,CAAIa,GAAe,CAC5B,CAACS,EAAOC,CAAQ,CAAA,CAAIrB,QAAAA,CAA8CF,CAAAA,EAAQ,SAAS,CAAA,CAEzF,OAAAO,UAAU,IACHP,CAAAA,CAEeA,EAAO,WAAA,CAAawB,CAAAA,EAAa,CACnDD,CAAAA,CAASC,CAAQ,EACnB,CAAC,EAJY,MAAA,CAOZ,CAACxB,CAAM,CAAC,CAAA,CAEJsB,CACT,CAoBO,SAASG,CAAAA,EAA8B,CAC5C,GAAM,CAAE,OAAAzB,CAAO,CAAA,CAAIa,CAAAA,EAAe,CAC5B,CAACC,CAAAA,CAASY,CAAU,EAAIxB,QAAAA,CAC5BF,CAAAA,EAAQ,aAAe,CACrB,KAAA,CAAO,OAAA,CACP,WAAA,CAAa,SACb,qBAAA,CAAuB,CAAC,QAAQ,CAAA,CAChC,QAAA,CAAU,CAAE,KAAA,CAAO,CAAA,CAAG,MAAA,CAAQ,CAAE,EAChC,MAAA,CAAQ,OAAA,CACR,SAAU,KACZ,CACF,EAEA,OAAAO,SAAAA,CAAU,IACHP,CAAAA,EAEL0B,EAAW1B,CAAAA,CAAO,WAAW,CAAA,CAETA,CAAAA,CAAO,oBAAqB2B,CAAAA,EAAe,CAC7DD,CAAAA,CAAWC,CAAU,EACvB,CAAC,CAAA,EANY,OASZ,CAAC3B,CAAM,CAAC,CAAA,CAEJc,CACT,CAyBO,SAASc,EAAkBC,CAAAA,CAAgE,CAChG,GAAM,CAAE,OAAA7B,CAAO,CAAA,CAAIa,CAAAA,EAAe,CAE5B,CAACiB,CAAAA,CAAOC,CAAgB,EAAI7B,QAAAA,CAAY,IACvCF,EACUA,CAAAA,CAAO,QAAA,EAAY,EACjB6B,CAAAA,CAFGA,CAGrB,CAAA,CAEKG,CAAAA,CAAWC,WAAAA,CACdC,CAAAA,EAAmC,CAClCH,CAAAA,CAAkBI,CAAAA,EAAS,CACzB,IAAMC,EAAO,OAAOF,CAAAA,EAAa,WAAcA,CAAAA,CAA4BC,CAAI,EAAID,CAAAA,CAGnF,OAAAlC,CAAAA,EAAQ,QAAA,CAASoC,CAAI,CAAA,CAEdA,CACT,CAAC,EACH,CAAA,CACA,CAACpC,CAAM,CACT,CAAA,CAEA,OAAO,CAAC8B,CAAAA,CAAOE,CAAQ,CACzB,CAmBO,SAASK,GAA8B,CAC5C,IAAMvB,CAAAA,CAAUW,CAAAA,GAEhBlB,SAAAA,CAAU,IAAM,CACd,IAAM+B,EAAYxB,CAAAA,CAAQ,MAAA,EAAQ,SAAA,CAClC,GAAI,CAACwB,CAAAA,CAAW,OAEhB,IAAMC,CAAAA,CAAO,QAAA,CAAS,gBACtB,IAAA,GAAW,CAACC,CAAAA,CAAKC,CAAK,IAAK,MAAA,CAAO,OAAA,CAAQH,CAAS,CAAA,CACjDC,CAAAA,CAAK,MAAM,WAAA,CAAYC,CAAAA,CAAKC,CAAK,CAAA,CAInC,OAAO,IAAM,CACX,QAAWD,CAAAA,IAAO,MAAA,CAAO,KAAKF,CAAS,CAAA,CACrCC,CAAAA,CAAK,KAAA,CAAM,eAAeC,CAAG,EAEjC,CACF,CAAA,CAAG,CAAC1B,CAAAA,CAAQ,MAAA,EAAQ,SAAS,CAAC,EAChC,CAgBO,SAAS4B,EAAiBC,CAAAA,CAAa,OAAA,CAASC,EAAY,MAAA,CAAc,CAC/E,IAAM9B,CAAAA,CAAUW,GAAe,CAE/BlB,SAAAA,CAAU,IAAM,CACd,GAAM,CAAE,KAAA,CAAAsC,CAAM,CAAA,CAAI/B,CAAAA,CACZgC,EAAO,QAAA,CAAS,IAAA,CAEtB,OAAAA,CAAAA,CAAK,SAAA,CAAU,OAAOH,CAAAA,CAAYC,CAAS,CAAA,CAC3CE,CAAAA,CAAK,UAAU,GAAA,CAAID,CAAAA,GAAU,MAAA,CAASD,CAAAA,CAAYD,CAAU,CAAA,CAErD,IAAM,CACXG,CAAAA,CAAK,UAAU,MAAA,CAAOH,CAAAA,CAAYC,CAAS,EAC7C,CACF,EAAG,CAAC9B,CAAAA,CAAQ,KAAA,CAAO6B,CAAAA,CAAYC,CAAS,CAAC,EAC3C,CAsBO,SAASG,CAAAA,EAId,CACA,IAAMjC,CAAAA,CAAUW,CAAAA,EAAe,CACzB,CAAE,MAAA,CAAAzB,CAAO,EAAIa,CAAAA,EAAe,CAE5BmC,EAAcf,WAAAA,CAClB,MAAOgB,CAAAA,EAA0C,CAC/C,MAAMjD,CAAAA,EAAQ,kBAAA,CAAmBiD,CAAI,EACvC,EACA,CAACjD,CAAM,CACT,CAAA,CAEA,OAAO,CACL,IAAA,CAAMc,EAAQ,WAAA,CACd,cAAA,CAAgBA,EAAQ,qBAAA,CACxB,WAAA,CAAAkC,CACF,CACF,CAoBO,SAASE,CAAAA,EAKd,CAGA,OAFgBzB,CAAAA,GAGN,cAAA,EAAkB,CACxB,GAAA,CAAK,CAAA,CACL,MAAO,CAAA,CACP,MAAA,CAAQ,EACR,IAAA,CAAM,CACR,CAEJ,CAyBO,SAAS0B,CAAAA,CAAmBC,CAAAA,CAA0C,CAC3E,GAAM,CAAE,MAAA,CAAApD,CAAO,EAAIa,CAAAA,EAAe,CAElCN,SAAAA,CAAU,IACHP,EAEeA,CAAAA,CAAO,eAAA,CAAgBoD,CAAO,CAAA,CAFrC,MAAA,CAIZ,CAACpD,CAAAA,CAAQoD,CAAO,CAAC,EACtB,CAmBO,SAASC,CAAAA,CAAcD,EAA0C,CACtE,GAAM,CAAE,MAAA,CAAApD,CAAO,CAAA,CAAIa,CAAAA,GAEnBN,SAAAA,CAAU,IACHP,EAEeA,CAAAA,CAAO,UAAA,CAAWoD,CAAO,CAAA,CAFhC,MAAA,CAIZ,CAACpD,CAAAA,CAAQoD,CAAO,CAAC,EACtB,CAuBO,SAASE,EAAsBF,CAAAA,CAAyD,CAC7F,GAAM,CAAE,OAAApD,CAAO,CAAA,CAAIa,GAAe,CAElCN,SAAAA,CAAU,IACHP,CAAAA,CAEeA,CAAAA,CAAO,kBAAA,CAAmBoD,CAAO,EAFxC,MAAA,CAIZ,CAACpD,EAAQoD,CAAO,CAAC,EACtB,CA4BO,SAASG,CAAAA,EAAoD,CAClE,GAAM,CAAE,MAAA,CAAAvD,CAAO,CAAA,CAAIa,CAAAA,GACb,CAAC2C,CAAAA,CAAcC,CAAe,CAAA,CAAIvD,SACtCF,CAAAA,EAAQ,mBAAA,EACV,CAAA,CAEA,OAAAO,SAAAA,CAAU,IAAM,CACTP,CAAAA,EACLyD,EAAgBzD,CAAAA,CAAO,mBAAA,EAAqB,EAC9C,CAAA,CAAG,CAACA,CAAM,CAAC,CAAA,CAEJwD,CACT,CAwBO,SAASE,CAAAA,EAA0C,CACxD,GAAM,CAAE,MAAA,CAAA1D,CAAO,CAAA,CAAIa,CAAAA,GACb,CAAC8C,CAAAA,CAASC,CAAU,CAAA,CAAI1D,QAAAA,CAAkCF,GAAQ,cAAA,EAAgB,CAAA,CAExF,OAAAO,UAAU,IAAM,CACTP,CAAAA,EACL4D,CAAAA,CAAW5D,EAAO,cAAA,EAAgB,EACpC,CAAA,CAAG,CAACA,CAAM,CAAC,EAEJ2D,CACT,CA2BO,SAASE,CAAAA,EAAmE,CACjF,GAAM,CAAE,OAAA7D,CAAO,CAAA,CAAIa,GAAe,CAC5BiD,CAAAA,CAAeC,OAA2B,IAAI,CAAA,CAEpD,OAAAxD,SAAAA,CAAU,IAAM,CACd,GAAI,CAACP,CAAAA,EAAU,OAAO,eAAmB,GAAA,CAAa,OAEtD,IAAMgE,CAAAA,CAAUF,EAAa,OAAA,CAC7B,GAAI,CAACE,CAAAA,CAAS,OAEd,IAAMC,CAAAA,CAAW,IAAI,cAAA,CAAgBC,GAAY,CAC/C,IAAA,IAAWC,KAASD,CAAAA,CAAS,CAC3B,GAAM,CAAE,KAAA,CAAAE,CAAAA,CAAO,MAAA,CAAAC,CAAO,CAAA,CAAIF,CAAAA,CAAM,YAC3BnE,CAAAA,CAAO,eAAA,CAAgB,CAC1B,KAAA,CAAO,IAAA,CAAK,KAAA,CAAMoE,CAAK,EACvB,MAAA,CAAQ,IAAA,CAAK,MAAMC,CAAM,CAC3B,CAAC,EACH,CACF,CAAC,CAAA,CAEDJ,EAAS,OAAA,CAAQD,CAAO,CAAA,CAGxB,IAAMM,EAAON,CAAAA,CAAQ,qBAAA,EAAsB,CAC3C,OAAKhE,EAAO,eAAA,CAAgB,CAC1B,MAAO,IAAA,CAAK,KAAA,CAAMsE,EAAK,KAAK,CAAA,CAC5B,MAAA,CAAQ,IAAA,CAAK,MAAMA,CAAAA,CAAK,MAAM,CAChC,CAAC,CAAA,CAEM,IAAML,CAAAA,CAAS,UAAA,EACxB,CAAA,CAAG,CAACjE,CAAM,CAAC,EAEJ8D,CACT,CAkEO,SAASS,CAAAA,EAEd,CACA,GAAM,CAAE,OAAAvE,CAAO,CAAA,CAAIa,CAAAA,EAAe,CAC5B,CAACiB,CAAAA,CAAOE,CAAQ,CAAA,CAAI9B,QAAAA,CAA6B,CACrD,WAAA,CAAa,KAAA,CACb,YAAa,KAAA,CACb,KAAA,CAAO,KACP,MAAA,CAAQ,IACV,CAAC,CAAA,CAGDK,UAAU,IAAM,CACdyB,CAAAA,CAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,WAAA,CAAa,CAAC,CAACnC,CAAAA,EAAQ,UACzB,EAAE,EACJ,CAAA,CAAG,CAACA,CAAM,CAAC,CAAA,CAEX,IAAMwE,EAASvC,WAAAA,CACb,MAAOwC,CAAAA,EAAiD,CACtD,GAAI,CAACzE,CAAAA,EAAQ,UAAA,CACX,OAAAgC,EAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,KAAA,CAAO,IAAI,KAAA,CAAM,4CAA4C,CAC/D,CAAA,CAAE,EACK,IAAA,CAGTH,CAAAA,CAAUG,IAAU,CAAE,GAAGA,EAAM,WAAA,CAAa,IAAA,CAAM,KAAA,CAAO,IAAK,EAAE,CAAA,CAEhE,GAAI,CACF,IAAMjB,CAAAA,CAAS,MAAMlB,CAAAA,CAAO,UAAA,CAAWyE,CAAI,CAAA,CAC3C,OAAAzC,CAAAA,CAAUG,CAAAA,GAAU,CAClB,GAAGA,EACH,WAAA,CAAa,CAAA,CAAA,CACb,MAAA,CAAQjB,CAAAA,CAAO,MACjB,CAAA,CAAE,CAAA,CACKA,CACT,CAAA,MAASR,CAAAA,CAAK,CACZ,IAAML,CAAAA,CAAQK,CAAAA,YAAe,KAAA,CAAQA,EAAM,IAAI,KAAA,CAAM,OAAOA,CAAG,CAAC,EAChE,OAAAsB,CAAAA,CAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,WAAA,CAAa,MACb,KAAA,CAAA9B,CACF,EAAE,CAAA,CACK,IACT,CACF,CAAA,CACA,CAACL,CAAM,CACT,CAAA,CAEA,OAAO,CAAE,GAAG8B,CAAAA,CAAO,MAAA,CAAA0C,CAAO,CAC5B,CA8BO,SAASE,GAMd,CACA,GAAM,CAAE,MAAA,CAAA1E,CAAO,CAAA,CAAIa,CAAAA,GACb,CAACiB,CAAAA,CAAOE,CAAQ,CAAA,CAAI9B,QAAAA,CAKvB,CACD,WAAA,CAAa,KAAA,CACb,SAAA,CAAW,KAAA,CACX,MAAO,IAAA,CACP,WAAA,CAAa,IACf,CAAC,CAAA,CAGDK,UAAU,IAAM,CACdyB,CAAAA,CAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,WAAA,CAAa,CAAC,CAACnC,CAAAA,EAAQ,kBACzB,CAAA,CAAE,EACJ,EAAG,CAACA,CAAM,CAAC,CAAA,CAEX,IAAM2E,EAAiB1C,WAAAA,CACrB,MAAO2C,CAAAA,EAA2C,CAChD,GAAI,CAAC5E,CAAAA,EAAQ,kBAAA,CACX,OAAAgC,EAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,MAAO,IAAI,KAAA,CAAM,8CAA8C,CACjE,CAAA,CAAE,EACK,IAAA,CAGTH,CAAAA,CAAUG,CAAAA,GAAU,CAAE,GAAGA,CAAAA,CAAM,SAAA,CAAW,IAAA,CAAM,KAAA,CAAO,IAAK,CAAA,CAAE,CAAA,CAE9D,GAAI,CACF,IAAMjB,CAAAA,CAAS,MAAMlB,EAAO,kBAAA,CAAmB4E,CAAM,EACrD,OAAA5C,CAAAA,CAAUG,CAAAA,GAAU,CAClB,GAAGA,CAAAA,CACH,SAAA,CAAW,GACX,WAAA,CAAajB,CAAAA,CAAO,WACtB,CAAA,CAAE,CAAA,CACKA,CAAAA,CAAO,WAChB,OAASR,CAAAA,CAAK,CACZ,IAAML,CAAAA,CAAQK,CAAAA,YAAe,MAAQA,CAAAA,CAAM,IAAI,KAAA,CAAM,MAAA,CAAOA,CAAG,CAAC,CAAA,CAChE,OAAAsB,CAAAA,CAAUG,IAAU,CAClB,GAAGA,CAAAA,CACH,SAAA,CAAW,MACX,KAAA,CAAA9B,CACF,EAAE,CAAA,CACK,IACT,CACF,CAAA,CACA,CAACL,CAAM,CACT,EAEA,OAAO,CAAE,GAAG8B,CAAAA,CAAO,cAAA,CAAA6C,CAAe,CACpC,CAyCO,SAASE,CAAAA,EAOd,CACA,GAAM,CAAE,OAAA7E,CAAO,CAAA,CAAIa,GAAe,CAC5BiD,CAAAA,CAAeC,MAAAA,CAA2B,IAAI,EAC9C,CAACe,CAAAA,CAAaC,CAAc,CAAA,CAAI7E,SAAS,KAAK,CAAA,CAGpDK,SAAAA,CAAU,IAAM,CACdwE,CAAAA,CAAe,CAAC,CAAC/E,CAAAA,EAAQ,qBAAqB,EAChD,CAAA,CAAG,CAACA,CAAM,CAAC,EAGX,IAAMgF,CAAAA,CAAS/C,YACZoC,CAAAA,EAAmB,CAClBrE,GAAQ,qBAAA,GAAwBqE,CAAM,EACxC,CAAA,CACA,CAACrE,CAAM,CACT,EAGA,OAAAO,SAAAA,CAAU,IAAM,CACd,GAAI,CAACP,CAAAA,EAAQ,uBAAyB,CAAC8D,CAAAA,CAAa,OAAA,CAAS,OAE7D,IAAME,CAAAA,CAAUF,CAAAA,CAAa,OAAA,CACvBG,CAAAA,CAAW,IAAI,cAAA,CAAgBC,CAAAA,EAAY,CAC/C,IAAA,IAAWC,CAAAA,IAASD,EAAS,CAC3B,IAAMG,CAAAA,CAASF,CAAAA,CAAM,YAAY,MAAA,CACjCnE,CAAAA,CAAO,wBAAwBqE,CAAM,EACvC,CACF,CAAC,CAAA,CAED,OAAAJ,CAAAA,CAAS,QAAQD,CAAO,CAAA,CAGxBhE,EAAO,qBAAA,CAAsBgE,CAAAA,CAAQ,YAAY,CAAA,CAE1C,IAAMC,CAAAA,CAAS,UAAA,EACxB,CAAA,CAAG,CAACjE,CAAM,CAAC,EAEJ,CAAE,WAAA,CAAA8E,CAAAA,CAAa,YAAA,CAAAhB,EAAc,MAAA,CAAAkB,CAAO,CAC7C,CAyBO,SAASC,GAA8B,CAE5C,OADgBxD,CAAAA,EAAe,CAChB,IACjB,CAwEO,SAASyD,GAOd,CACA,GAAM,CAAE,MAAA,CAAAlF,CAAO,CAAA,CAAIa,CAAAA,GACb,CAACiE,CAAAA,CAAaC,CAAc,CAAA,CAAI7E,QAAAA,CAAS,KAAK,CAAA,CAC9C,CAACiF,CAAAA,CAAQC,CAAS,EAAIlF,QAAAA,CAAS,KAAK,CAAA,CAG1CK,SAAAA,CAAU,IAAM,CACdwE,CAAAA,CAAe,CAAC,CAAC/E,GAAQ,YAAY,EACvC,EAAG,CAACA,CAAM,CAAC,CAAA,CAEX,IAAMqF,CAAAA,CAAYpD,WAAAA,CAChB,MAAOqD,CAAAA,EAAuD,CAC5D,GAAI,CAACtF,CAAAA,EAAQ,aACX,OAAO,IAAA,CAGToF,CAAAA,CAAU,IAAI,EACd,GAAI,CAEF,OADe,MAAMpF,CAAAA,CAAO,aAAasF,CAAO,CAElD,CAAA,OAAE,CACAF,EAAU,KAAK,EACjB,CACF,CAAA,CACA,CAACpF,CAAM,CACT,CAAA,CAEA,OAAO,CAAE,WAAA,CAAA8E,CAAAA,CAAa,OAAAK,CAAAA,CAAQ,SAAA,CAAAE,CAAU,CAC1C,CA4BO,SAASE,CAAAA,CAAeC,EAAwD,CAErF,OAAA3E,GAAe,CAGfN,SAAAA,CAAU,IAAM,CACViF,CAAAA,EACFC,iBAAAA,CAAkB,SAAA,CAAUD,CAAM,EAEtC,CAAA,CAAG,CAACA,CAAM,CAAC,EAEJC,iBACT","file":"index.js","sourcesContent":["/**\n * React context and provider for @mcp-apps-kit/ui-react\n */\n\nimport React, {\n createContext,\n useContext,\n useState,\n useEffect,\n type ReactNode,\n type ComponentType,\n} from \"react\";\nimport type { AppsClient, ToolDefs } from \"@mcp-apps-kit/ui\";\nimport { createClient } from \"@mcp-apps-kit/ui\";\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\ninterface AppsContextValue<T extends ToolDefs = ToolDefs> {\n client: AppsClient<T> | null;\n isConnecting: boolean;\n error: Error | null;\n}\n\nconst AppsContext = createContext<AppsContextValue | null>(null);\n\n// =============================================================================\n// PROVIDER PROPS\n// =============================================================================\n\n/**\n * Props for AppsProvider\n */\nexport interface AppsProviderProps<T extends ToolDefs = ToolDefs> {\n /** Child components */\n children: ReactNode;\n\n /**\n * Pre-created client instance (optional)\n * If not provided, creates client automatically\n */\n client?: AppsClient<T>;\n\n /**\n * Force a specific protocol adapter\n */\n forceAdapter?: \"mcp\" | \"openai\" | \"mock\";\n\n /**\n * Fallback UI while client is connecting\n */\n fallback?: ReactNode;\n\n /**\n * Error boundary fallback\n */\n errorFallback?: ComponentType<{ error: Error; reset: () => void }>;\n}\n\n// =============================================================================\n// PROVIDER\n// =============================================================================\n\n/**\n * Context provider for mcp-apps-kit React integration\n *\n * Wraps your app and provides the client instance to all hooks.\n *\n * @example\n * ```tsx\n * function App() {\n * return (\n * <AppsProvider fallback={<Loading />}>\n * <MyWidget />\n * </AppsProvider>\n * );\n * }\n * ```\n */\nexport function AppsProvider<T extends ToolDefs = ToolDefs>({\n children,\n client: providedClient,\n forceAdapter: _forceAdapter,\n fallback,\n errorFallback: ErrorFallback,\n}: AppsProviderProps<T>): React.JSX.Element {\n const [client, setClient] = useState<AppsClient<T> | null>(providedClient ?? null);\n const [isConnecting, setIsConnecting] = useState(!providedClient);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n if (providedClient) {\n setClient(providedClient);\n setIsConnecting(false);\n return;\n }\n\n // Initialize client with auto-detection or forced adapter\n const initClient = async () => {\n try {\n const newClient = await createClient<T>({\n forceAdapter: _forceAdapter,\n });\n setClient(newClient);\n setIsConnecting(false);\n } catch (err) {\n setError(err instanceof Error ? err : new Error(String(err)));\n setIsConnecting(false);\n }\n };\n\n void initClient();\n }, [providedClient, _forceAdapter]);\n\n if (error && ErrorFallback) {\n return <ErrorFallback error={error} reset={() => setError(null)} />;\n }\n\n if (isConnecting && fallback) {\n return <>{fallback}</>;\n }\n\n return (\n <AppsContext.Provider value={{ client, isConnecting, error }}>{children}</AppsContext.Provider>\n );\n}\n\n// =============================================================================\n// INTERNAL HOOK\n// =============================================================================\n\n/**\n * Internal hook to access the context\n * @internal\n */\nexport function useAppsContext<T extends ToolDefs = ToolDefs>(): AppsContextValue<T> {\n const context = useContext(AppsContext);\n if (!context) {\n throw new Error(\"useAppsContext must be used within an AppsProvider\");\n }\n return context as AppsContextValue<T>;\n}\n","/**\n * React hooks for @mcp-apps-kit/ui-react\n */\n\nimport React, { useState, useEffect, useCallback, useRef } from \"react\";\nimport type {\n AppsClient,\n HostContext,\n ToolDefs,\n ToolResult,\n ClientDebugConfig,\n ModalOptions,\n ModalResult,\n HostCapabilities,\n HostVersion,\n} from \"@mcp-apps-kit/ui\";\nimport { clientDebugLogger, type ClientDebugLogger } from \"@mcp-apps-kit/ui\";\nimport { useAppsContext } from \"./context\";\n\n// =============================================================================\n// CORE HOOKS\n// =============================================================================\n\n/**\n * Access the typed client instance\n *\n * @returns Client instance\n * @throws Error if used outside AppsProvider\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const client = useAppsClient<typeof app.tools>();\n *\n * const handleClick = async () => {\n * await client.callTool(\"myTool\", { arg: \"value\" });\n * };\n *\n * return <button onClick={handleClick}>Call Tool</button>;\n * }\n * ```\n */\nexport function useAppsClient<T extends ToolDefs = ToolDefs>(): AppsClient<T> {\n const { client, isConnecting, error } = useAppsContext<T>();\n\n if (error) {\n throw error;\n }\n\n if (isConnecting || !client) {\n throw new Error(\"Client not ready. Make sure AppsProvider has finished connecting.\");\n }\n\n return client;\n}\n\n/**\n * Access current tool result with automatic re-renders\n *\n * @returns Current tool result or undefined\n *\n * @example\n * ```tsx\n * function ResultDisplay() {\n * const result = useToolResult<typeof app.tools>();\n *\n * if (!result?.myTool) {\n * return <div>No results yet</div>;\n * }\n *\n * return <div>{result.myTool.message}</div>;\n * }\n * ```\n */\nexport function useToolResult<T extends ToolDefs = ToolDefs>(): ToolResult<T> | undefined {\n const { client } = useAppsContext<T>();\n const [result, setResult] = useState<ToolResult<T> | undefined>(undefined);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolResult((newResult) => {\n setResult(newResult as ToolResult<T>);\n });\n\n return unsubscribe;\n }, [client]);\n\n return result;\n}\n\n/**\n * Access current tool input\n *\n * @returns Current tool input or undefined\n *\n * @example\n * ```tsx\n * function InputDisplay() {\n * const input = useToolInput();\n * return <pre>{JSON.stringify(input, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useToolInput(): Record<string, unknown> | undefined {\n const { client } = useAppsContext();\n const [input, setInput] = useState<Record<string, unknown> | undefined>(client?.toolInput);\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInput((newInput) => {\n setInput(newInput);\n });\n\n return unsubscribe;\n }, [client]);\n\n return input;\n}\n\n/**\n * Access host context with automatic re-renders on changes\n *\n * @returns Current host context\n *\n * @example\n * ```tsx\n * function ThemedComponent() {\n * const context = useHostContext();\n *\n * return (\n * <div className={context.theme}>\n * Display: {context.displayMode}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostContext(): HostContext {\n const { client } = useAppsContext();\n const [context, setContext] = useState<HostContext>(\n client?.hostContext ?? {\n theme: \"light\",\n displayMode: \"inline\",\n availableDisplayModes: [\"inline\"],\n viewport: { width: 0, height: 0 },\n locale: \"en-US\",\n platform: \"web\",\n }\n );\n\n useEffect(() => {\n if (!client) return;\n\n setContext(client.hostContext);\n\n const unsubscribe = client.onHostContextChange((newContext) => {\n setContext(newContext);\n });\n\n return unsubscribe;\n }, [client]);\n\n return context;\n}\n\n/**\n * Persisted widget state with automatic sync\n *\n * Works like useState but persists across widget reloads.\n * On ChatGPT: Session-scoped persistence\n * On MCP Apps: Silent no-op (returns default, setState is ignored)\n *\n * @param defaultValue - Initial state value\n * @returns [state, setState] tuple\n *\n * @example\n * ```tsx\n * function Counter() {\n * const [count, setCount] = useWidgetState(0);\n *\n * return (\n * <button onClick={() => setCount(c => c + 1)}>\n * Count: {count}\n * </button>\n * );\n * }\n * ```\n */\nexport function useWidgetState<S>(defaultValue: S): [S, (newState: S | ((prev: S) => S)) => void] {\n const { client } = useAppsContext();\n\n const [state, setStateInternal] = useState<S>(() => {\n if (!client) return defaultValue;\n const stored = client.getState<S>();\n return stored ?? defaultValue;\n });\n\n const setState = useCallback(\n (newState: S | ((prev: S) => S)) => {\n setStateInternal((prev) => {\n const next = typeof newState === \"function\" ? (newState as (prev: S) => S)(prev) : newState;\n\n // Persist to client (silent no-op on MCP Apps)\n client?.setState(next);\n\n return next;\n });\n },\n [client]\n );\n\n return [state, setState];\n}\n\n// =============================================================================\n// UTILITY HOOKS\n// =============================================================================\n\n/**\n * Apply host CSS variables to document root\n *\n * Call this once in your root component to apply host theming.\n *\n * @example\n * ```tsx\n * function App() {\n * useHostStyleVariables();\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useHostStyleVariables(): void {\n const context = useHostContext();\n\n useEffect(() => {\n const variables = context.styles?.variables;\n if (!variables) return;\n\n const root = document.documentElement;\n for (const [key, value] of Object.entries(variables)) {\n root.style.setProperty(key, value);\n }\n\n // Cleanup\n return () => {\n for (const key of Object.keys(variables)) {\n root.style.removeProperty(key);\n }\n };\n }, [context.styles?.variables]);\n}\n\n/**\n * Apply theme class to document body\n *\n * @param lightClass - Class name for light theme (default: \"light\")\n * @param darkClass - Class name for dark theme (default: \"dark\")\n *\n * @example\n * ```tsx\n * function App() {\n * useDocumentTheme(\"theme-light\", \"theme-dark\");\n * return <MyWidget />;\n * }\n * ```\n */\nexport function useDocumentTheme(lightClass = \"light\", darkClass = \"dark\"): void {\n const context = useHostContext();\n\n useEffect(() => {\n const { theme } = context;\n const body = document.body;\n\n body.classList.remove(lightClass, darkClass);\n body.classList.add(theme === \"dark\" ? darkClass : lightClass);\n\n return () => {\n body.classList.remove(lightClass, darkClass);\n };\n }, [context.theme, lightClass, darkClass]);\n}\n\n/**\n * Access and manage display mode\n *\n * @returns Display mode state and controls\n *\n * @example\n * ```tsx\n * function DisplayModeToggle() {\n * const { mode, availableModes, requestMode } = useDisplayMode();\n *\n * return (\n * <select value={mode} onChange={e => requestMode(e.target.value)}>\n * {availableModes.map(m => (\n * <option key={m} value={m}>{m}</option>\n * ))}\n * </select>\n * );\n * }\n * ```\n */\nexport function useDisplayMode(): {\n mode: string;\n availableModes: string[];\n requestMode: (mode: \"inline\" | \"fullscreen\" | \"pip\") => Promise<void>;\n} {\n const context = useHostContext();\n const { client } = useAppsContext();\n\n const requestMode = useCallback(\n async (mode: \"inline\" | \"fullscreen\" | \"pip\") => {\n await client?.requestDisplayMode(mode);\n },\n [client]\n );\n\n return {\n mode: context.displayMode,\n availableModes: context.availableDisplayModes,\n requestMode,\n };\n}\n\n/**\n * Access safe area insets for mobile layouts\n *\n * @returns Safe area insets or default zeros\n *\n * @example\n * ```tsx\n * function SafeContent() {\n * const insets = useSafeAreaInsets();\n *\n * return (\n * <div style={{ paddingTop: insets.top, paddingBottom: insets.bottom }}>\n * Content\n * </div>\n * );\n * }\n * ```\n */\nexport function useSafeAreaInsets(): {\n top: number;\n right: number;\n bottom: number;\n left: number;\n} {\n const context = useHostContext();\n\n return (\n context.safeAreaInsets ?? {\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n }\n );\n}\n\n// =============================================================================\n// EVENT HOOKS\n// =============================================================================\n\n/**\n * Subscribe to tool cancellation\n *\n * @param handler - Callback when tool is cancelled\n *\n * @example\n * ```tsx\n * function CancellableOperation() {\n * const [cancelled, setCancelled] = useState(false);\n *\n * useOnToolCancelled((reason) => {\n * setCancelled(true);\n * console.log(\"Cancelled:\", reason);\n * });\n *\n * return cancelled ? <div>Cancelled</div> : <div>Running...</div>;\n * }\n * ```\n */\nexport function useOnToolCancelled(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolCancelled(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to teardown events\n *\n * @param handler - Callback when widget is torn down\n *\n * @example\n * ```tsx\n * function CleanupComponent() {\n * useOnTeardown((reason) => {\n * console.log(\"Tearing down:\", reason);\n * // Cleanup resources\n * });\n *\n * return <div>Widget</div>;\n * }\n * ```\n */\nexport function useOnTeardown(handler: (reason?: string) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onTeardown(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n/**\n * Subscribe to partial/streaming tool input\n *\n * Called when the host sends partial tool arguments during streaming.\n * Useful for showing real-time input as the user types or as the model generates.\n *\n * @param handler - Callback for partial input\n *\n * @example\n * ```tsx\n * function StreamingInput() {\n * const [partialInput, setPartialInput] = useState<Record<string, unknown>>({});\n *\n * useOnToolInputPartial((input) => {\n * setPartialInput(input);\n * });\n *\n * return <pre>{JSON.stringify(partialInput, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useOnToolInputPartial(handler: (input: Record<string, unknown>) => void): void {\n const { client } = useAppsContext();\n\n useEffect(() => {\n if (!client) return;\n\n const unsubscribe = client.onToolInputPartial(handler);\n return unsubscribe;\n }, [client, handler]);\n}\n\n// =============================================================================\n// HOST INFORMATION HOOKS\n// =============================================================================\n\n/**\n * Access host capabilities\n *\n * Returns the capabilities advertised by the host during handshake.\n * Use this to check if features like logging or server tools are supported.\n *\n * @returns Host capabilities or undefined if not yet connected\n *\n * @example\n * ```tsx\n * function CapabilitiesDisplay() {\n * const capabilities = useHostCapabilities();\n *\n * return (\n * <div>\n * <p>Logging: {capabilities?.logging ? \"Supported\" : \"Not supported\"}</p>\n * <p>Open Links: {capabilities?.openLinks ? \"Supported\" : \"Not supported\"}</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostCapabilities(): HostCapabilities | undefined {\n const { client } = useAppsContext();\n const [capabilities, setCapabilities] = useState<HostCapabilities | undefined>(\n client?.getHostCapabilities()\n );\n\n useEffect(() => {\n if (!client) return;\n setCapabilities(client.getHostCapabilities());\n }, [client]);\n\n return capabilities;\n}\n\n/**\n * Access host version information\n *\n * Returns the name and version of the host application.\n *\n * @returns Host version info or undefined if not yet connected\n *\n * @example\n * ```tsx\n * function HostInfo() {\n * const hostVersion = useHostVersion();\n *\n * if (!hostVersion) return <div>Loading...</div>;\n *\n * return (\n * <div>\n * Running on {hostVersion.name} v{hostVersion.version}\n * </div>\n * );\n * }\n * ```\n */\nexport function useHostVersion(): HostVersion | undefined {\n const { client } = useAppsContext();\n const [version, setVersion] = useState<HostVersion | undefined>(client?.getHostVersion());\n\n useEffect(() => {\n if (!client) return;\n setVersion(client.getHostVersion());\n }, [client]);\n\n return version;\n}\n\n// =============================================================================\n// SIZE NOTIFICATION HOOKS\n// =============================================================================\n\n/**\n * Hook to set up automatic size change notifications\n *\n * Creates a ResizeObserver that automatically sends size changed\n * notifications to the host when the observed element resizes.\n *\n * @returns Ref to attach to the element to observe\n *\n * @example\n * ```tsx\n * function AutoSizeWidget() {\n * const containerRef = useSizeChangedNotifications();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change size...</p>\n * </div>\n * );\n * }\n * ```\n */\nexport function useSizeChangedNotifications(): React.RefObject<HTMLElement | null> {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n\n useEffect(() => {\n if (!client || typeof ResizeObserver === \"undefined\") return;\n\n const element = containerRef.current;\n if (!element) return;\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const { width, height } = entry.contentRect;\n void client.sendSizeChanged({\n width: Math.round(width),\n height: Math.round(height),\n });\n }\n });\n\n observer.observe(element);\n\n // Report initial size\n const rect = element.getBoundingClientRect();\n void client.sendSizeChanged({\n width: Math.round(rect.width),\n height: Math.round(rect.height),\n });\n\n return () => observer.disconnect();\n }, [client]);\n\n return containerRef;\n}\n\n// =============================================================================\n// FILE OPERATION HOOKS\n// =============================================================================\n\n/**\n * File upload result\n */\nexport interface FileUploadResult {\n /** Uploaded file ID */\n fileId: string;\n}\n\n/**\n * File upload state\n */\nexport interface UseFileUploadState {\n /** Whether file upload is supported on this platform */\n isSupported: boolean;\n /** Whether upload is in progress */\n isUploading: boolean;\n /** Upload error if any */\n error: Error | null;\n /** Last uploaded file ID */\n fileId: string | null;\n}\n\n/**\n * Hook for uploading files (ChatGPT only)\n *\n * Provides a function to upload files and tracks upload state.\n * On unsupported platforms, isSupported will be false.\n *\n * Supported file types: PNG, JPEG, WebP images\n *\n * @returns Upload function and state\n *\n * @example\n * ```tsx\n * function ImageUploader() {\n * const { upload, isSupported, isUploading, error, fileId } = useFileUpload();\n *\n * if (!isSupported) {\n * return <div>File upload not supported on this platform</div>;\n * }\n *\n * const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {\n * const file = e.target.files?.[0];\n * if (file) {\n * const result = await upload(file);\n * console.log(\"Uploaded:\", result?.fileId);\n * }\n * };\n *\n * return (\n * <div>\n * <input type=\"file\" accept=\"image/*\" onChange={handleFileChange} />\n * {isUploading && <span>Uploading...</span>}\n * {error && <span>Error: {error.message}</span>}\n * {fileId && <span>Uploaded: {fileId}</span>}\n * </div>\n * );\n * }\n * ```\n */\nexport function useFileUpload(): UseFileUploadState & {\n upload: (file: File) => Promise<FileUploadResult | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<UseFileUploadState>({\n isSupported: false,\n isUploading: false,\n error: null,\n fileId: null,\n });\n\n // Check if upload is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.uploadFile,\n }));\n }, [client]);\n\n const upload = useCallback(\n async (file: File): Promise<FileUploadResult | null> => {\n if (!client?.uploadFile) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File upload not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isUploading: true, error: null }));\n\n try {\n const result = await client.uploadFile(file);\n setState((prev) => ({\n ...prev,\n isUploading: false,\n fileId: result.fileId,\n }));\n return result;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isUploading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, upload };\n}\n\n/**\n * Hook for getting file download URLs (ChatGPT only)\n *\n * Provides a function to get temporary download URLs for uploaded files.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Download URL function and state\n *\n * @example\n * ```tsx\n * function FileDownloader({ fileId }: { fileId: string }) {\n * const { getDownloadUrl, isSupported, isLoading, error, downloadUrl } = useFileDownload();\n *\n * useEffect(() => {\n * if (fileId && isSupported) {\n * getDownloadUrl(fileId);\n * }\n * }, [fileId, isSupported, getDownloadUrl]);\n *\n * if (!isSupported) return <div>Not supported</div>;\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (downloadUrl) return <img src={downloadUrl} alt=\"Uploaded file\" />;\n *\n * return null;\n * }\n * ```\n */\nexport function useFileDownload(): {\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n getDownloadUrl: (fileId: string) => Promise<string | null>;\n} {\n const { client } = useAppsContext();\n const [state, setState] = useState<{\n isSupported: boolean;\n isLoading: boolean;\n error: Error | null;\n downloadUrl: string | null;\n }>({\n isSupported: false,\n isLoading: false,\n error: null,\n downloadUrl: null,\n });\n\n // Check if download URL is supported\n useEffect(() => {\n setState((prev) => ({\n ...prev,\n isSupported: !!client?.getFileDownloadUrl,\n }));\n }, [client]);\n\n const getDownloadUrl = useCallback(\n async (fileId: string): Promise<string | null> => {\n if (!client?.getFileDownloadUrl) {\n setState((prev) => ({\n ...prev,\n error: new Error(\"File download not supported on this platform\"),\n }));\n return null;\n }\n\n setState((prev) => ({ ...prev, isLoading: true, error: null }));\n\n try {\n const result = await client.getFileDownloadUrl(fileId);\n setState((prev) => ({\n ...prev,\n isLoading: false,\n downloadUrl: result.downloadUrl,\n }));\n return result.downloadUrl;\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n setState((prev) => ({\n ...prev,\n isLoading: false,\n error,\n }));\n return null;\n }\n },\n [client]\n );\n\n return { ...state, getDownloadUrl };\n}\n\n// =============================================================================\n// LAYOUT HOOKS\n// =============================================================================\n\n/**\n * Hook to notify host of widget's intrinsic height (ChatGPT only)\n *\n * Automatically reports height changes to prevent scroll clipping.\n * Returns a ref to attach to your root element for automatic height tracking,\n * or a manual notify function for custom height reporting.\n *\n * @returns Ref for auto-tracking and manual notify function\n *\n * @example\n * ```tsx\n * // Automatic height tracking\n * function AutoHeightWidget() {\n * const { containerRef, isSupported } = useIntrinsicHeight();\n *\n * return (\n * <div ref={containerRef}>\n * <p>Content that may change height...</p>\n * </div>\n * );\n * }\n *\n * // Manual height notification\n * function ManualHeightWidget() {\n * const { notify, isSupported } = useIntrinsicHeight();\n *\n * useEffect(() => {\n * // Notify after content loads\n * notify(500);\n * }, [notify]);\n *\n * return <div style={{ height: 500 }}>Fixed height content</div>;\n * }\n * ```\n */\nexport function useIntrinsicHeight(): {\n /** Whether intrinsic height notification is supported */\n isSupported: boolean;\n /** Ref to attach to container for automatic height tracking */\n containerRef: React.RefObject<HTMLElement | null>;\n /** Manually notify host of height */\n notify: (height: number) => void;\n} {\n const { client } = useAppsContext();\n const containerRef = useRef<HTMLElement | null>(null);\n const [isSupported, setIsSupported] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.notifyIntrinsicHeight);\n }, [client]);\n\n // Manual notify function\n const notify = useCallback(\n (height: number) => {\n client?.notifyIntrinsicHeight?.(height);\n },\n [client]\n );\n\n // Auto-track height with ResizeObserver\n useEffect(() => {\n if (!client?.notifyIntrinsicHeight || !containerRef.current) return;\n\n const element = containerRef.current;\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n const height = entry.contentRect.height;\n client.notifyIntrinsicHeight?.(height);\n }\n });\n\n observer.observe(element);\n\n // Report initial height\n client.notifyIntrinsicHeight(element.offsetHeight);\n\n return () => observer.disconnect();\n }, [client]);\n\n return { isSupported, containerRef, notify };\n}\n\n/**\n * Hook to access the current view identifier (ChatGPT only)\n *\n * Useful for multi-view widgets that need to know which view is active.\n *\n * @returns Current view identifier or undefined\n *\n * @example\n * ```tsx\n * function MultiViewWidget() {\n * const view = useView();\n *\n * switch (view) {\n * case \"settings\":\n * return <SettingsView />;\n * case \"details\":\n * return <DetailsView />;\n * default:\n * return <MainView />;\n * }\n * }\n * ```\n */\nexport function useView(): string | undefined {\n const context = useHostContext();\n return context.view;\n}\n\n// =============================================================================\n// MODAL HOOKS\n// =============================================================================\n\n/**\n * Hook for showing host-owned modal dialogs (ChatGPT only)\n *\n * Spawns native ChatGPT modals for confirmations, inputs, etc.\n * On unsupported platforms, isSupported will be false.\n *\n * @returns Modal function and state\n *\n * @example\n * ```tsx\n * function DeleteButton() {\n * const { showModal, isSupported, isOpen } = useModal();\n *\n * const handleDelete = async () => {\n * const result = await showModal({\n * title: \"Confirm Delete\",\n * body: \"Are you sure you want to delete this item?\",\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Delete\", variant: \"destructive\", value: \"delete\" },\n * ],\n * });\n *\n * if (result?.action === \"delete\") {\n * // Perform delete\n * }\n * };\n *\n * if (!isSupported) {\n * return <button onClick={() => window.confirm(\"Delete?\")}>Delete</button>;\n * }\n *\n * return (\n * <button onClick={handleDelete} disabled={isOpen}>\n * Delete\n * </button>\n * );\n * }\n *\n * // Input modal example\n * function RenameButton() {\n * const { showModal } = useModal();\n *\n * const handleRename = async () => {\n * const result = await showModal({\n * title: \"Rename Item\",\n * input: {\n * type: \"text\",\n * placeholder: \"Enter new name\",\n * defaultValue: \"Current Name\",\n * },\n * buttons: [\n * { label: \"Cancel\", variant: \"secondary\", value: \"cancel\" },\n * { label: \"Rename\", variant: \"primary\", value: \"rename\" },\n * ],\n * });\n *\n * if (result?.action === \"rename\" && result.inputValue) {\n * console.log(\"New name:\", result.inputValue);\n * }\n * };\n *\n * return <button onClick={handleRename}>Rename</button>;\n * }\n * ```\n */\nexport function useModal(): {\n /** Whether modal API is supported */\n isSupported: boolean;\n /** Whether a modal is currently open */\n isOpen: boolean;\n /** Show a modal dialog */\n showModal: (options: ModalOptions) => Promise<ModalResult | null>;\n} {\n const { client } = useAppsContext();\n const [isSupported, setIsSupported] = useState(false);\n const [isOpen, setIsOpen] = useState(false);\n\n // Check if supported\n useEffect(() => {\n setIsSupported(!!client?.requestModal);\n }, [client]);\n\n const showModal = useCallback(\n async (options: ModalOptions): Promise<ModalResult | null> => {\n if (!client?.requestModal) {\n return null;\n }\n\n setIsOpen(true);\n try {\n const result = await client.requestModal(options);\n return result;\n } finally {\n setIsOpen(false);\n }\n },\n [client]\n );\n\n return { isSupported, isOpen, showModal };\n}\n\n// =============================================================================\n// DEBUG LOGGING HOOKS\n// =============================================================================\n\n/**\n * Access the debug logger with automatic adapter injection\n *\n * The adapter is automatically configured when AppsProvider connects.\n * Use this hook to access the logger and optionally configure it.\n *\n * @param config - Optional configuration to apply\n * @returns The configured client debug logger\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const logger = useDebugLogger({ enabled: true, level: \"debug\" });\n *\n * const handleClick = () => {\n * logger.info(\"Button clicked\", { timestamp: Date.now() });\n * };\n *\n * return <button onClick={handleClick}>Click me</button>;\n * }\n * ```\n */\nexport function useDebugLogger(config?: Partial<ClientDebugConfig>): ClientDebugLogger {\n // Verify we're inside AppsProvider (adapter is set by createClient)\n useAppsContext();\n\n // Apply configuration when it changes\n useEffect(() => {\n if (config) {\n clientDebugLogger.configure(config);\n }\n }, [config]);\n\n return clientDebugLogger;\n}\n"]}
{
"name": "@mcp-apps-kit/ui-react",
"version": "0.2.3",
"version": "0.2.4",
"description": "React bindings for MCP applications",

@@ -5,0 +5,0 @@ "type": "module",

+148
-18

@@ -5,24 +5,48 @@ # @mcp-apps-kit/ui-react

React bindings for MCP app UIs/widgets.
React bindings for MCP applications.
This package builds on `@mcp-apps-kit/ui` and provides:
`@mcp-apps-kit/ui-react` builds on `@mcp-apps-kit/ui` to provide React context and hooks for tool calls, tool results, and host context.
- an `AppsProvider`
- React hooks for host context + tool interactions
## Table of Contents
Use it inside your widget UI when you want React-first ergonomics.
- [Background](#background)
- [Features](#features)
- [Compatibility](#compatibility)
- [Install](#install)
- [Usage](#usage)
- [Examples](#examples)
- [API](#api)
- [Contributing](#contributing)
- [License](#license)
## Background
React widgets often need host-aware APIs for tool calls and UI state. This package provides a React-first wrapper around the vanilla UI SDK so you can use hooks instead of manual subscriptions.
## Features
- `AppsProvider` context wrapper
- Hooks for tools, host context, and widget state
- Typed tool calls with generics
- Optional debug logger hook
- **Host capabilities** - Query what the host supports (theming, display modes, file upload, etc.)
- **Size notifications** - Automatic resize observer integration
- **Partial tool input** - React to streaming tool inputs
## Compatibility
- Hosts: MCP Apps and ChatGPT (OpenAI Apps SDK)
- Node.js: `>= 18` for tooling/builds (browser runtime)
- Peer dependencies: `react` and `react-dom` `^18 || ^19`
## Install
```bash
npm install @mcp-apps-kit/ui-react @mcp-apps-kit/ui
npm install @mcp-apps-kit/ui-react
```
Peer dependencies:
## Usage
- `react` `^18 || ^19`
- `react-dom` `^18 || ^19`
### Quick start
## Quick start
```tsx

@@ -53,6 +77,4 @@ import { AppsProvider, useAppsClient, useToolResult, useHostContext } from "@mcp-apps-kit/ui-react";

## Typed tools
### Typed tools
For best TypeScript inference, parameterize the provider/client with your server-exported tool types.
```tsx

@@ -64,3 +86,2 @@ import { AppsProvider, useAppsClient } from "@mcp-apps-kit/ui-react";

const client = useAppsClient<AppClientTools>();
// client.callTool(...) is now typed
return null;

@@ -78,9 +99,118 @@ }

## Documentation & examples
## Examples
- Project overview: ../../README.md
- Example React widget: [kanban-mcp-example](https://github.com/AndurilCode/kanban-mcp-example)
- [kanban-mcp-example](https://github.com/AndurilCode/kanban-mcp-example)
## API
### Provider
- `AppsProvider` - Context wrapper for all hooks
### Core Hooks
| Hook | Description |
| ---------------- | ----------------------------------------- |
| `useAppsClient` | Client instance for tool calls |
| `useToolResult` | Current tool result data |
| `useToolInput` | Tool input parameters |
| `useHostContext` | Host info (theme, viewport, locale, etc.) |
| `useWidgetState` | Persisted state across reloads |
| `useDisplayMode` | Fullscreen/panel mode control |
| `useDebugLogger` | Debug logging configuration |
### Host Capabilities & Version
```tsx
import { useHostCapabilities, useHostVersion } from "@mcp-apps-kit/ui-react";
function Widget() {
const capabilities = useHostCapabilities();
const version = useHostVersion();
// Common capabilities (both platforms)
const themes = capabilities?.theming?.themes; // ["light", "dark"]
const modes = capabilities?.displayModes?.modes; // ["inline", "fullscreen", "pip"]
// MCP Apps specific
const hasPartialInput = !!capabilities?.partialToolInput;
// ChatGPT specific
const hasFileUpload = !!capabilities?.fileUpload;
// Host version (MCP Apps only)
// { name: "Claude Desktop", version: "1.0.0" }
return <div>Host: {version?.name}</div>;
}
```
### Size Notifications (MCP Apps)
```tsx
import { useSizeChangedNotifications } from "@mcp-apps-kit/ui-react";
function Widget() {
// Attach to container to auto-report size changes
const containerRef = useSizeChangedNotifications();
return <div ref={containerRef}>Content that may resize</div>;
}
```
### Partial Tool Input (MCP Apps)
```tsx
import { useOnToolInputPartial } from "@mcp-apps-kit/ui-react";
function Widget() {
useOnToolInputPartial((input) => {
// React to streaming partial input from the model
console.log("Partial input:", input);
});
return <div>Streaming input widget</div>;
}
```
### Theme & Style Hooks
| Hook | Description |
| ----------------------- | --------------------------------- |
| `useHostStyleVariables` | Apply host-provided CSS variables |
| `useDocumentTheme` | Sync document theme with host |
| `useSafeAreaInsets` | Safe area insets (ChatGPT) |
### Lifecycle Hooks
| Hook | Description |
| -------------------- | -------------------------------------- |
| `useOnToolCancelled` | Callback when tool is cancelled |
| `useOnTeardown` | Cleanup callback before widget removal |
### File Operations (ChatGPT)
| Hook | Description |
| ----------------- | ---------------------- |
| `useFileUpload` | Upload files to host |
| `useFileDownload` | Get file download URLs |
### Layout (ChatGPT)
| Hook | Description |
| -------------------- | --------------------------- |
| `useIntrinsicHeight` | Set widget intrinsic height |
| `useView` | View management |
### Modals (ChatGPT)
| Hook | Description |
| ---------- | ----------------------- |
| `useModal` | Modal dialog management |
## Contributing
See `../../CONTRIBUTING.md` for development setup and guidelines. Issues and pull requests are welcome.
## License
MIT